use super::biquad::Biquad;

/// Filtro Linkwitz-Riley de 4° orden (LR4) para una frecuencia de corte.
/// Cascada de dos Butterworth de 2° orden idénticos.
/// La suma LP + HP es plana en magnitud y fase.
#[derive(Clone, Debug)]
pub struct Lr4Stage {
    lp1_l: Biquad, lp2_l: Biquad,
    hp1_l: Biquad, hp2_l: Biquad,
    lp1_r: Biquad, lp2_r: Biquad,
    hp1_r: Biquad, hp2_r: Biquad,
}

impl Lr4Stage {
    pub fn new(fc: f32, sample_rate: f32) -> Self {
        Self {
            lp1_l: Biquad::lpf(fc, sample_rate),
            lp2_l: Biquad::lpf(fc, sample_rate),
            hp1_l: Biquad::hpf(fc, sample_rate),
            hp2_l: Biquad::hpf(fc, sample_rate),
            lp1_r: Biquad::lpf(fc, sample_rate),
            lp2_r: Biquad::lpf(fc, sample_rate),
            hp1_r: Biquad::hpf(fc, sample_rate),
            hp2_r: Biquad::hpf(fc, sample_rate),
        }
    }

    /// Divide una muestra en baja (lo) y alta (hi) por canal.
    #[inline]
    pub fn split_l(&mut self, x: f32) -> (f32, f32) {
        let lo = self.lp2_l.process(self.lp1_l.process(x as f64)) as f32;
        let hi = self.hp2_l.process(self.hp1_l.process(x as f64)) as f32;
        (lo, hi)
    }

    #[inline]
    pub fn split_r(&mut self, x: f32) -> (f32, f32) {
        let lo = self.lp2_r.process(self.lp1_r.process(x as f64)) as f32;
        let hi = self.hp2_r.process(self.hp1_r.process(x as f64)) as f32;
        (lo, hi)
    }

    pub fn recalculate(&mut self, fc: f32, sample_rate: f32) {
        *self = Self::new(fc, sample_rate);
    }
}

/// Red de crossover multiband con hasta 6 bandas (5 frecuencias de corte).
/// La cadena encadena N-1 etapas LR4:
///
///  input → [Stage 0, f0] → lo → banda[0]
///                        → hi → [Stage 1, f1] → lo → banda[1]
///                                              → hi → ... → banda[N-1]
pub struct Crossover {
    stages:      Vec<Lr4Stage>,
    pub num_bands:   usize,
    sample_rate: f32,
}

impl Crossover {
    pub fn new(freqs: &[f32; 5], num_bands: usize, sample_rate: f32) -> Self {
        let n = num_bands.clamp(2, 6);
        let stages = (0..n-1)
            .map(|i| Lr4Stage::new(freqs[i], sample_rate))
            .collect();
        Self { stages, num_bands: n, sample_rate }
    }

    pub fn recalculate(&mut self, freqs: &[f32; 5], num_bands: usize) {
        let n = num_bands.clamp(2, 6);
        self.num_bands = n;
        while self.stages.len() < n - 1 {
            self.stages.push(Lr4Stage::new(freqs[self.stages.len()], self.sample_rate));
        }
        self.stages.truncate(n - 1);
        for (i, stage) in self.stages.iter_mut().enumerate() {
            stage.recalculate(freqs[i], self.sample_rate);
        }
    }

    /// Separa un par de muestras estéreo en `num_bands` pares de bandas.
    /// `bands_l` y `bands_r` deben tener al menos `num_bands` elementos.
    #[inline]
    pub fn process_sample(
        &mut self,
        l: f32, r: f32,
        bands_l: &mut [f32],
        bands_r: &mut [f32],
    ) {
        let n = self.num_bands;
        let mut rem_l = l;
        let mut rem_r = r;

        for (i, stage) in self.stages.iter_mut().enumerate() {
            let (lo_l, hi_l) = stage.split_l(rem_l);
            let (lo_r, hi_r) = stage.split_r(rem_r);
            bands_l[i] = lo_l;
            bands_r[i] = lo_r;
            rem_l = hi_l;
            rem_r = hi_r;
        }
        bands_l[n - 1] = rem_l;
        bands_r[n - 1] = rem_r;
    }
}
