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

/// An iterator link another iterator, lazily.
///
/// This `struct` is created by [`iter_with`] method on [`MarkdownExt`].
///
/// [`iter_with`]: trait.MarkdownExt.html#method.iter_with
/// [`MarkdownExt`]: trait.MarkdownExt.html
#[derive(Debug)]
pub struct IterWith<I, F, G> {
    frontmatter: Share<RefCell<Option<FrontMatter>>>,
    iter: I,
    f: Option<F>,

    gen: Option<G>,
}

impl<I, F, G> IterWith<I, F, G> {
    pub fn new(frontmatter: Share<RefCell<Option<FrontMatter>>>, iter: I, f: F) -> Self {
        Self {
            frontmatter,
            iter,
            f: Some(f),
            gen: None,
        }
    }
}

impl<I, T, F, G> Iterator for IterWith<I, F, G>
where
    I: Iterator<Item = T>,
    G: Iterator<Item = T>,
    F: FnOnce() -> G,
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        match self.iter.next() {
            node @ Some(_) => node,
            None => {
                if self.gen.is_none() {
                    self.gen = Some((self.f.take().unwrap())());
                }
                self.gen.as_mut().unwrap().next()
            }
        }
    }
}

impl<I, T, F, G> MarkdownExt<T> for IterWith<I, F, G>
where
    I: Iterator<Item = T>,
    G: Iterator<Item = T>,
    F: FnOnce() -> 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!(
            IterWith::new(Rc::new(RefCell::new(None)).into(), 1..3, || 3..6).collect::<Vec<_>>(),
            vec![1, 2, 3, 4, 5]
        );
    }
}
