use crate::{MarkdownExt, Share, FrontMatter};
use std::cell::{Ref, RefCell};

enum HeadState {
    Head,
    Origin,
}

/// An iterator places events at the start of the original one.
///
/// This `struct` is created by [`head`] method on [`MarkdownExt`].
///
/// [`head`]: trait.MarkdownExt.html#method.head
/// [`MarkdownExt`]: trait.MarkdownExt.html
#[derive(new)]
pub struct Head<I, H, F, G, T>
where
    G: IntoIterator<Item = T, IntoIter = H>,
    H: Iterator<Item = T>,
    F: Fn(Ref<Option<FrontMatter>>) -> G,
{
    frontmatter: Share<RefCell<Option<FrontMatter>>>,
    origin: I,
    f: F,

    #[new(default)]
    head: Option<H>,

    #[new(value = "HeadState::Head")]
    state: HeadState,
}

impl<I, H, F, G, T> Iterator for Head<I, H, F, G, T>
where
    I: Iterator<Item = T>,
    H: Iterator<Item = T>,
    G: IntoIterator<Item = T, IntoIter = H>,
    F: Fn(Ref<Option<FrontMatter>>) -> G,
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        if self.head.is_none() {
            self.head =
                Some((self.f)(self.frontmatter.upgrade().unwrap().borrow()).into_iter());
        }
        match self.state {
            HeadState::Head => match self.head.as_mut().unwrap().next() {
                node @ Some(_) => node,
                None => {
                    self.state = HeadState::Origin;
                    self.next()
                }
            },
            HeadState::Origin => self.origin.next(),
        }
    }
}

impl<I, H, F, G, T> MarkdownExt<T> for Head<I, H, F, G, T>
where
    I: Iterator<Item = T>,
    H: Iterator<Item = T>,
    G: IntoIterator<Item = T, IntoIter = H>,
    F: Fn(Ref<Option<FrontMatter>>) -> G,
{
    fn frontmatter(&mut self) -> &mut Share<RefCell<Option<FrontMatter>>> {
        &mut self.frontmatter
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::rc::Rc;

    #[test]
    fn normal() {
        assert_eq!(
            Head::new(Rc::new(RefCell::new(None)).into(), 3..6, |_| 1..3).collect::<Vec<_>>(),
            vec![1, 2, 3, 4, 5]
        );
    }
}
