use crate::utils::sampling::PiecewiseConstant2D;
use crate::utils::{FileLoc, ParameterDictionary};
use crate::Arena;
use anyhow::{anyhow, Result};
use shared::core::filter::Filter;
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
use shared::filters::*;
use shared::{Array2D, Float};

pub trait FilterFactory {
    fn create(
        name: &str,
        params: &ParameterDictionary,
        loc: &FileLoc,
        arena: &Arena,
    ) -> Result<Filter>;
}

impl FilterFactory for Filter {
    fn create(
        name: &str,
        params: &ParameterDictionary,
        loc: &FileLoc,
        arena: &Arena,
    ) -> Result<Self> {
        match name {
            "box" => {
                let xw = params.get_one_float("xradius", 0.5)?;
                let yw = params.get_one_float("yradius", 0.5)?;
                let filter = BoxFilter::new(Vector2f::new(xw, yw));
                Ok(Filter::Box(filter))
            }
            "gaussian" => {
                let xw = params.get_one_float("xradius", 1.5)?;
                let yw = params.get_one_float("yradius", 1.5)?;
                let sigma = params.get_one_float("sigma", 0.5)?;
                let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
            }
            "mitchell" => {
                let xw = params.get_one_float("xradius", 2.)?;
                let yw = params.get_one_float("yradius", 2.)?;
                let b = params.get_one_float("B", 1. / 3.)?;
                let c = params.get_one_float("C", 1. / 3.)?;
                let filter = MitchellFilter::new(Vector2f::new(xw, yw), b, c);
                Ok(Filter::Mitchell(filter))
            }
            "sinc" => {
                let xw = params.get_one_float("xradius", 4.)?;
                let yw = params.get_one_float("yradius", 4.)?;
                let tau = params.get_one_float("tau", 3.)?;
                let filter = LanczosSincFilter::new(Vector2f::new(xw, yw), tau);
                Ok(Filter::LanczosSinc(filter))
            }
            "triangle" => {
                let xw = params.get_one_float("xradius", 2.)?;
                let yw = params.get_one_float("yradius", 2.)?;
                let filter = TriangleFilter::new(Vector2f::new(xw, yw));
                Ok(Filter::Triangle(filter))
            }
            _ => Err(anyhow!("Film type '{}' unknown at {}", name, loc)),
        }
    }
}
