use num::Zero;
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 std::iter::Sum;
use std::ops::Add;
use std::ops::Mul;

use crate::filtering::fir::Fir;

#[derive(BlockIO)]
pub struct FirFilter<F, T, O>
where
    T: Clone + Zero + 'static,
    F: Mul<T, Output = O> + Clone + 'static,
    O: Add<O, Output = O> + Sum + Clone + Zero + 'static,
{
    #[input]
    input: In<T>,

    #[output]
    output: Out<O>,

    filter: crate::filtering::fir::FirFilter<F, T, O>,
}

impl<F, T, O> FirFilter<F, T, O>
where
    T: Clone + Zero + 'static,
    F: Mul<T, Output = O> + Clone + 'static,
    O: Add<O, Output = O> + Sum + Clone + Zero,
{
    pub fn new(input: In<T>, impulse_response: Fir<F>) -> (Self, In<O>)
    {
        let (output, filtered) = oxydsp_flowgraph::io::stream();
        (
            Self {
                input,
                output,
                filter: crate::filtering::fir::FirFilter::new(impulse_response),
            },
            filtered,
        )
    }
}

impl<F, T, O> Block for FirFilter<F, T, O>
where
    T: Clone + Zero,
    F: Mul<T, Output = O> + Clone + 'static,
    O: Add<O, Output = O> + Sum + Clone + Zero,
{
    fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult 
    {
        self.output.push_iter(self.input.iter().map(|x| (self.filter.next(x.0), x.1).into()));
        BlockResult::Ok
    }
}
