/// Filtro biquad IIR de 2do orden, forma directa I.
/// Coeficientes normalizados (a0 = 1).
/// y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2]
#[derive(Clone, Debug)]
pub struct Biquad {
    pub b0: f64, pub b1: f64, pub b2: f64,
    pub a1: f64, pub a2: f64,
    x1: f64, x2: f64,
    y1: f64, y2: f64,
}

impl Biquad {
    pub fn identity() -> Self {
        Self { b0: 1.0, b1: 0.0, b2: 0.0, a1: 0.0, a2: 0.0,
               x1: 0.0, x2: 0.0, y1: 0.0, y2: 0.0 }
    }

    /// Filtro pasa-bajos Butterworth 2do orden
    /// Q = 1/sqrt(2) ≈ 0.7071 para respuesta Butterworth
    pub fn lpf(fc: f32, sample_rate: f32) -> Self {
        butterworth(fc, sample_rate, FilterType::Lpf)
    }

    /// Filtro pasa-altos Butterworth 2do orden
    pub fn hpf(fc: f32, sample_rate: f32) -> Self {
        butterworth(fc, sample_rate, FilterType::Hpf)
    }

    #[inline]
    pub fn process(&mut self, x: f64) -> f64 {
        let y = self.b0 * x + self.b1 * self.x1 + self.b2 * self.x2
                            - self.a1 * self.y1 - self.a2 * self.y2;
        self.x2 = self.x1; self.x1 = x;
        self.y2 = self.y1; self.y1 = y;
        y
    }

    pub fn reset(&mut self) {
        self.x1 = 0.0; self.x2 = 0.0;
        self.y1 = 0.0; self.y2 = 0.0;
    }
}

#[derive(Clone, Copy)]
enum FilterType { Lpf, Hpf }

fn butterworth(fc: f32, sample_rate: f32, t: FilterType) -> Biquad {
    use std::f64::consts::PI;
    let q = std::f64::consts::FRAC_1_SQRT_2; // 1/√2 = 0.7071
    let fc = fc as f64;
    let fs = sample_rate as f64;

    let fc_clamped = fc.clamp(1.0, fs * 0.499);
    let w0 = 2.0 * PI * fc_clamped / fs;
    let cos_w0 = w0.cos();
    let sin_w0 = w0.sin();
    let alpha = sin_w0 / (2.0 * q);

    let (b0, b1, b2) = match t {
        FilterType::Lpf => {
            let b0 = (1.0 - cos_w0) / 2.0;
            (b0, 1.0 - cos_w0, b0)
        }
        FilterType::Hpf => {
            let b0 = (1.0 + cos_w0) / 2.0;
            (b0, -(1.0 + cos_w0), b0)
        }
    };

    let a0 =   1.0 + alpha;
    let a1 =  -2.0 * cos_w0;
    let a2 =   1.0 - alpha;

    Biquad {
        b0: b0 / a0, b1: b1 / a0, b2: b2 / a0,
        a1: a1 / a0, a2: a2 / a0,
        x1: 0.0, x2: 0.0, y1: 0.0, y2: 0.0,
    }
}
