use std::iter::Sum;
use std::ops::Add;

use num::Zero;
use oxydsp_flowgraph::BlockIO;
use oxydsp_flowgraph::block::Block;
use oxydsp_flowgraph::io::In;
use oxydsp_flowgraph::io::Out;

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

#[derive(BlockIO)]
pub struct PulseShaper<T: 'static + std::ops::Mul<Output = T> + std::iter::Sum + Add<T, Output = T> + Sum + Clone + Zero>
{
    #[input]
    input: In<T>,

    #[output]
    output: Out<T>,

    symbol_length: usize,
    remaining: usize,
    pulse_shaper: FirFilter<T, T, T>,
}

impl<T: 'static + std::ops::Mul<Output = T> + std::iter::Sum + std::clone::Clone + Zero> PulseShaper<T>
{
    pub fn new(input: In<T>, pulse_shape: Fir<T>, symbol_length: usize) -> (Self, In<T>)
    {
        let (output, pulse_shaped) = oxydsp_flowgraph::io::stream();
        (
            Self {
                input,
                output,
                symbol_length,
                remaining: 0,
                pulse_shaper: FirFilter::new(pulse_shape),
            },
            pulse_shaped,
        )
    }
}

impl<T: 'static + std::ops::Mul<Output = T> + std::iter::Sum + std::clone::Clone + Zero> Block
    for PulseShaper<T>
{
    fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult
    {
        let mut reader = self.input.iter();
        let mut writer = self.output.write_push();

        for _ in 0..writer.len()
        {
            if self.remaining == 0
            {
                if let Some(input) = reader.next()
                {
                    let (data, tag) = input.into();
                    let _ = writer.push((self.pulse_shaper.next(data), tag).into());
                    self.remaining = self.symbol_length - 1;
                }
                else
                {
                    return oxydsp_flowgraph::block::BlockResult::Ok;
                }
            }
            else
            {
                let _ = writer.push(self.pulse_shaper.next(T::zero()).into());
                self.remaining -= 1;
            }
        }

        oxydsp_flowgraph::block::BlockResult::Ok
    }
}
