use num_complex::Complex;
use std::error::Error;
use std::fmt::{Display, Formatter};

mod butter;
mod cheby1;
mod cheby2;
mod elliptic;
mod filter_zpk;
mod zpk;

pub use butter::ButterworthFilter;
pub use cheby1::Chebyshev1Filter;
pub use cheby2::Chebyshev2Filter;
pub use elliptic::EllipticFilter;

#[derive(Debug, Copy, Clone)]
pub struct SosFilter {
    pub b: [f64; 3],
    pub a: [f64; 3],
}

impl SosFilter {
    pub fn new(b: [f64; 3], a: [f64; 3]) -> Self {
        Self { b, a }
    }
}

impl From<[f64; 6]> for SosFilter {
    fn from(row: [f64; 6]) -> Self {
        Self {
            b: [row[0], row[1], row[2]],
            a: [row[3], row[4], row[5]],
        }
    }
}

/// Transfer function coefficients `(b, a)`.
///
/// For high-order filters prefer [`Vec<SosFilter>`] (via [`.sos()`][ButterworthFilter::sos])
/// which is numerically more stable.
#[derive(Debug, Clone, PartialEq)]
pub struct BaFilter {
    /// Numerator polynomial coefficients, highest power first.
    pub b: Vec<f64>,
    /// Denominator polynomial coefficients, highest power first. `a[0]` is always 1.
    pub a: Vec<f64>,
}

/// Zeros, poles, and gain.
#[derive(Debug, Clone)]
pub struct ZpkFilter {
    /// Zeros of the transfer function.
    pub z: Vec<Complex<f64>>,
    /// Poles of the transfer function.
    pub p: Vec<Complex<f64>>,
    /// System gain.
    pub k: f64,
}

#[derive(Debug, PartialEq)]
pub enum ImpliirError {
    /// Filter order must be ≥ 1.
    InvalidOrder,
    /// Cutoff frequency must be strictly inside (0, Nyquist).
    InvalidFrequency,
    /// Bandpass requires two frequencies with 0 < low < high < Nyquist.
    InvalidBandFrequencies,
    InvalidRipple,
    InvalidAttenuation,
    InvalidRpRsCombination,
}

impl Error for ImpliirError {}

impl Display for ImpliirError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            ImpliirError::InvalidOrder => f.write_str("Invalid order"),
            ImpliirError::InvalidFrequency => f.write_str("Invalid frequency"),
            ImpliirError::InvalidBandFrequencies => f.write_str("Invalid band frequencies"),
            ImpliirError::InvalidRipple => f.write_str("Invalid ripple"),
            ImpliirError::InvalidAttenuation => f.write_str("Invalid attenuation"),
            ImpliirError::InvalidRpRsCombination => f.write_str("Invalid rp combination"),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum FilterType {
    Lowpass,
    Highpass,
    Bandpass,
}
