use crate::MarkdownExt;
use equt_md_frontmatter::FrontMatter;
use std::iter::Peekable;

/// An iterator places a pseudo events iterator before the first occurence of certain event.
/// This `struct` is created by [`before`] method on [`MarkdownExt`].
///
/// [`before`]: trait.MarkdownExt.html#method.before
/// [`MarkdownExt`]: trait.MarkdownExt.html
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Before<I, S, P>
where
    I: Iterator,
    S: Iterator,
{
    frontmatter: Option<FrontMatter>,
    iter: Peekable<I>,
    pseudo: Peekable<S>,
    before: P,
}

impl<I, S, P, T> Before<I, S, P>
where
    I: Iterator<Item = T>,
    S: Iterator<Item = T>,
    P: Fn(&T) -> bool,
{
    pub(crate) fn new(
        iter: I,
        frontmatter: Option<FrontMatter>,
        before: P,
        pseudo: impl IntoIterator<Item = T, IntoIter = S>,
    ) -> Self {
        Self {
            frontmatter,
            iter: iter.peekable(),
            pseudo: pseudo.into_iter().peekable(),
            before,
        }
    }
}

impl<I, S, P, T> MarkdownExt<T> for Before<I, S, P>
where
    I: Iterator<Item = T>,
    S: Iterator<Item = T>,
    P: Fn(&T) -> bool,
{
    fn frontmatter(&mut self) -> Option<FrontMatter> {
        self.frontmatter.take()
    }
}

impl<I, S, P, T> Iterator for Before<I, S, P>
where
    P: Fn(&T) -> bool,
    I: Iterator<Item = T>,
    S: Iterator<Item = T>,
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        let peek = self.iter.peek()?;
        if (self.before)(&peek) {
            if self.pseudo.peek().is_some() {
                return self.pseudo.next();
            }
        }
        self.iter.next()
    }
}

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

    #[test]
    fn before_iter() {
        let result: Vec<u8> = Before::new((1..4).chain(7..10), None, |&n| n > 4, 4..7).collect();
        assert_eq!(result, (1..10).collect::<Vec<_>>());
    }
}
