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

use crate::smooth::{base::SmoothApply, lib::{ema, median_filter, rma, sma}, SmoothError};

pub struct SMAImpl;

impl<T> SmoothApply<T> for SMAImpl
where
    T: Float + FromPrimitive + Zero + Clone + One + 'static,
{
    fn apply(&self, data: &Vec<T>, window_size: usize, _precision: Option<f64>) -> Result<Vec<T>, SmoothError> {
        let mut input: Vec<T> = data.clone();
        let _ = sma(&mut input, window_size);
        Ok(input)
    }
    fn apply_inplase(&self, data: &mut Vec<T>, window_size: usize, _precision: Option<f64>) -> Result<(), SmoothError> {
        sma(data, window_size)
    }
}

pub struct RMAImpl;

impl<T> SmoothApply<T> for RMAImpl
where
    T: Float + FromPrimitive + Zero + Clone + One + 'static,
{
    fn apply(&self, data: &Vec<T>, window_size: usize, _precision: Option<f64>) -> Result<Vec<T>, SmoothError> {
        rma(data, window_size)
    }
    fn apply_inplase(&self, _data: &mut Vec<T>, _window_size: usize, _precision: Option<f64>) -> Result<(), SmoothError> {
        todo!()
    }
}

pub struct EMAImpl;

impl<T> SmoothApply<T> for EMAImpl
where
    T: Float + FromPrimitive + Zero + Clone + One + 'static,
{
    fn apply(&self, data: &Vec<T>, window_size: usize, _precision: Option<f64>) -> Result<Vec<T>, SmoothError> {
        ema(data, window_size)
    }
    fn apply_inplase(&self, _data: &mut Vec<T>, _window_size: usize, _precision: Option<f64>) -> Result<(), SmoothError> {
        todo!()
    }
}

pub struct MedianImpl;

impl<T> SmoothApply<T> for MedianImpl
where
    T: Float + FromPrimitive + Zero + Clone + One + 'static,
{
    fn apply(&self, data: &Vec<T>, window_size: usize, precision: Option<f64>) -> Result<Vec<T>, SmoothError> {
        let prec = if let Some(p) = precision {
            p
        }else{
            return Err(SmoothError::MedianApplyError);
        };
        median_filter(data, window_size, prec)
    }
    fn apply_inplase(&self, _data: &mut Vec<T>, _window_size: usize, _precision: Option<f64>) -> Result<(), SmoothError> {
        todo!()
    }
}
