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

enum LinkState {
    Front,
    Back,
}

/// An iterator links two [`MarkdownExt`].
///
/// This `struct` is created by [`link`] method on [`MarkdownExt`].
///
/// [`link`]: trait.MarkdownExt.html#method.link
/// [`MarkdownExt`]: trait.MarkdownExt.html
#[derive(new)]
pub struct Link<A, B> {
    frontmatter: Share<RefCell<Option<FrontMatter>>>,
    a: A,
    b: B,

    #[new(value = "LinkState::Front")]
    state: LinkState,
}

impl<A, B, T> MarkdownExt<T> for Link<A, B>
where
    A: Iterator<Item = T>,
    B: Iterator<Item = T>,
{
    fn frontmatter(&mut self) -> &mut Share<RefCell<Option<FrontMatter>>> {
        &mut self.frontmatter
    }
}

impl<A, B, T> Iterator for Link<A, B>
where
    A: Iterator<Item = T>,
    B: Iterator<Item = T>,
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        match self.state {
            LinkState::Front => match self.a.next() {
                node @ Some(_) => node,
                None => {
                    self.state = LinkState::Back;
                    self.b.next()
                }
            },
            LinkState::Back => self.b.next(),
        }
    }
}

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

    struct TestIter {
        frontmatter: Share<RefCell<Option<FrontMatter>>>,
        range: std::ops::Range<u8>,
    }

    impl From<std::ops::Range<u8>> for TestIter {
        fn from(r: std::ops::Range<u8>) -> Self {
            Self {
                frontmatter: Rc::new(RefCell::new(None)).into(),
                range: r,
            }
        }
    }

    impl Iterator for TestIter {
        type Item = u8;

        fn next(&mut self) -> Option<Self::Item> {
            self.range.next()
        }
    }

    impl MarkdownExt<u8> for TestIter {
        fn frontmatter(&mut self) -> &mut Share<RefCell<Option<FrontMatter>>> {
            &mut self.frontmatter
        }
    }

    #[test]
    fn iter() {
        assert_eq!(
            Link::new(
                Rc::new(RefCell::new(None)).into(),
                TestIter::from(1..4),
                TestIter::from(4..7),
            )
            .collect::<Vec<_>>(),
            (1..7).collect::<Vec<_>>()
        );
    }
}
