use std::collections::VecDeque;
use std::iter::Sum;

use num::Float;
use num::FromPrimitive;
use num::One;
use num::Zero;
use num::complex::ComplexFloat;
use oxydsp_flowgraph::BlockIO;
use oxydsp_flowgraph::block::Block;
use oxydsp_flowgraph::block::BlockResult;
use oxydsp_flowgraph::io::In;
use oxydsp_flowgraph::io::Out;

#[derive(BlockIO)]
pub struct Squelch<T>
where
    T: ComplexFloat + 'static,
    T::Real: Float + One + Zero + FromPrimitive + Sum + Clone,
{
    #[input]
    input: In<T>,

    #[output]
    output: Out<T>,

    trigger_level: T::Real,

    history: VecDeque<T::Real>,
    sum: T::Real,
    divider: T::Real,
}

impl<T> Squelch<T>
where
    T: ComplexFloat + 'static,
    T::Real: Float + Sum + Clone + FromPrimitive,
{
    pub fn new(input: In<T>, trigger_level: T::Real, mean_length: usize) -> (Self, In<T>)
    {
        let (output, port) = oxydsp_flowgraph::io::stream();
        (
            Self {
                input,
                output,
                trigger_level,
                history: VecDeque::from(vec![T::Real::zero(); mean_length]),
                sum: T::Real::zero(),
                divider: T::Real::from_usize(mean_length).unwrap(),
            },
            port,
        )
    }
}

impl<T> Block for Squelch<T>
where
    T: ComplexFloat + 'static,
    T::Real: Float + Sum + Clone + FromPrimitive,
{
    fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult
    {
        let mut writer = self.output.write_push();
        for x in self.input.iter().take(writer.len())
        {
            let (element, tag) = x.into();

            let oldest = self.history.pop_front().unwrap();
            let newest = element.abs();
            self.history.push_back(newest);

            self.sum = self.sum - oldest;
            self.sum = self.sum + newest;

            if (self.sum / self.divider) > self.trigger_level
            {
                let _ = writer.push((element, tag).into());
            }
        }
        BlockResult::Ok
    }
}
