use num_traits::{Float, FromPrimitive, One, Zero};

use crate::filters::{base::{Factory, FilterApply}, fir::{FIRFilterDesign, Gauss1dFilter}, iir::{BesselFilter, ButterWorthFilter, ChebyShevType1Filter, ChebyShevType2Filter, IIRFilterDesign}, impls::IIRFilter, FilterError, FilterType};


struct FilterFactory;

impl<T> Factory<T> for FilterFactory  where
    T: Float + FromPrimitive + Clone + Zero + One + 'static,
{
    fn create(
        &self,
        ftype: FilterType,
    ) -> Result<Box<dyn FilterApply<T>>, FilterError> {
        match ftype {
            FilterType::Bessel { order, cutoff, band, analog, criterion, fs } => {
                let designer = BesselFilter::new(order, cutoff, band, analog, criterion, fs);
                let sos: IIRFilter = designer.design();
                Ok(Box::new(sos))
            }
            FilterType::Butterworth { order, cutoff, band, analog, fs } => {
                let designer = ButterWorthFilter::new(order, cutoff, band, analog, fs);
                let sos: IIRFilter = designer.design();
                Ok(Box::new(sos))
            }
            FilterType::ChebyshevType1 { order, cutoff, band, analog, fs, rp } => {
                let designer = ChebyShevType1Filter::new(order, cutoff, band, analog, fs, rp);
                let sos: IIRFilter = designer.design();
                Ok(Box::new(sos))
            }
            FilterType::ChebyshevType2 { order, cutoff, band, analog, fs, rs } => {
                let designer = ChebyShevType2Filter::new(order, cutoff, band, analog, fs, rs);
                let sos: IIRFilter = designer.design();
                Ok(Box::new(sos))
            }
            FilterType::Gauss1d { fs, cutoff, truncate, radius } => {
                let designer = Gauss1dFilter::new(fs, cutoff, truncate, radius);
                let sos = designer.design();
                Ok(Box::new(sos))
            }
        }
    }
}

pub fn filter_factory<T>() -> Box<dyn Factory<T>>
where
    T: Float + FromPrimitive + Clone + Zero + One + 'static,
{
    Box::new(FilterFactory)
}
