use crate::Float;
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
use crate::core::geometry::{Point2f, Vector2f};
use crate::utils::math::{gaussian, gaussian_integral};

#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct GaussianFilter {
    pub radius: Vector2f,
    pub sigma: Float,
    pub exp_x: Float,
    pub exp_y: Float,
    pub sampler: FilterSampler,
}

impl FilterTrait for GaussianFilter {
    fn radius(&self) -> Vector2f {
        self.radius
    }

    fn evaluate(&self, p: Point2f) -> Float {
        (gaussian(p.x(), 0.0, self.sigma) - self.exp_x).max(0.0)
            * (gaussian(p.y(), 0.0, self.sigma) - self.exp_y).max(0.0)
    }

    fn integral(&self) -> Float {
        (gaussian_integral(-self.radius.x(), self.radius.x(), 0.0, self.sigma)
            - 2.0 * self.radius.x() * self.exp_x)
            * (gaussian_integral(-self.radius.y(), self.radius.y(), 0.0, self.sigma)
                - 2.0 * self.radius.y() * self.exp_y)
    }

    fn sample(&self, u: Point2f) -> FilterSample {
        self.sampler.sample(u)
    }
}
