use std::iter::Peekable;

use oxydsp_flowgraph::BlockIO;
use oxydsp_flowgraph::block::Block;
use oxydsp_flowgraph::block::BlockResult;
use oxydsp_flowgraph::io::In;
use oxydsp_flowgraph::io::Out;
use oxydsp_flowgraph::io::stream;
use oxydsp_flowgraph::tag::Tag;
use oxydsp_flowgraph::tag::TagKey;

#[derive(BlockIO)]
pub struct IterSource<I: Iterator>
where
    I::Item: 'static,
{
    iter: Peekable<I>,
    finished_tag: Option<TagKey<()>>,

    #[output]
    output: Out<I::Item>,
}

impl<I> IterSource<I>
where
    I: Iterator,
    I::Item: 'static,
{
    pub fn new(iter: I) -> (Self, In<I::Item>)
    {
        let (output, items) = stream();
        (
            Self {
                iter: iter.peekable(),
                finished_tag: None,
                output,
            },
            items,
        )
    }

    pub fn tag_last_with(&mut self, finished_tag: TagKey<()>)
    {
        self.finished_tag = Some(finished_tag);
    }
}

impl<I> Block for IterSource<I>
where
    I: Iterator,
    I::Item: 'static,
{
    fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult
    {
        let mut writer = self.output.write_push();

        for _ in 0..writer.len()
        {
            if let Some(element) = self.iter.next()
            {
                let mut tag = None;
                if let Some(tag_key) = &self.finished_tag
                    && self.iter.peek().is_none()
                {
                    tag = Some(Tag::with_entry(tag_key.clone(), ()));
                }
                let _ = writer.push((element, tag).into());
            }
        }
        BlockResult::Ok
    }
}
