// Finite impulse response filters

use std::{collections::VecDeque, f32::consts::PI};
use crate::complex::Complex32;

pub struct FIRFilter {
    size: usize,
    normalization: f32,
    impulse_response: Box<[Complex32]>,
    taps: VecDeque<Complex32>,
}

impl FIRFilter {
    pub fn new(impulse_response: &[Complex32]) -> Self {
        FIRFilter {
            size: impulse_response.len(),
            impulse_response: impulse_response.iter().copied().collect(),
            /*
            normalization: impulse_response
                .iter()
                .map(|x| x.mag())
                .reduce(|acc, e| acc.max(e))
                .unwrap(),
            */
            normalization: 1., // DC normalization
            // TODO: Maybe we'd want other types of normalization (per frequency)
            taps: VecDeque::from(vec![Complex32::zero(); impulse_response.len()]),
        }
    }

    // Normalizes FIR Filter such that DC is at unity gain
    pub fn normalize_dc(&mut self)
    {
        self.normalization = self.impulse_response.iter().copied().sum::<Complex32>().mag()
    }

    // Normalizes FIR Filter such that frequency is at unity gain
    pub fn normalize_freq(&mut self, frequency: f32)
    {
        // Perform DTFT of specified frequency
        let dft: Complex32 = self.impulse_response
            .iter()
            .enumerate()
            .map(|(i, x)| *x * Complex32::cexp(- 2. * PI * i as f32 * frequency / self.impulse_response.len() as f32))
            .sum();
        self.normalization = dft.mag();
    }

    pub fn next(&mut self, next: Complex32) -> Complex32 {
        let _ = self.taps.pop_front();

        self.taps.push_back(next);
        self.taps
            .iter()
            .zip(self.impulse_response.iter())
            .map(|(tap, coef)| *tap * *coef)
            .sum::<Complex32>()
            / self.normalization
    }

    pub fn next_real(&mut self, next: f32) -> f32 {
        self.next(Complex32::new(next, 0.)).re
    }
}
