use crate::Float;
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
use crate::core::geometry::{Point2f, Vector2f};
use crate::utils::math::{lerp, windowed_sinc};
use rand::Rng;

#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct LanczosSincFilter {
    pub radius: Vector2f,
    pub tau: Float,
    pub sampler: FilterSampler,
}

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

    fn evaluate(&self, p: Point2f) -> Float {
        windowed_sinc(p.x(), self.radius.x(), self.tau)
            * windowed_sinc(p.y(), self.radius.y(), self.tau)
    }

    fn integral(&self) -> Float {
        let sqrt_samples = 64;
        let n_samples = sqrt_samples * sqrt_samples;
        let area = (2.0 * self.radius.x()) * (2.0 * self.radius.y());
        let mut sum = 0.0;
        let mut rng = rand::rng();

        for y in 0..sqrt_samples {
            for x in 0..sqrt_samples {
                let u = Point2f::new(
                    (x as Float + rng.random::<Float>()) / sqrt_samples as Float,
                    (y as Float + rng.random::<Float>()) / sqrt_samples as Float,
                );
                let p = Point2f::new(
                    lerp(u.x(), -self.radius.x(), self.radius.x()),
                    lerp(u.y(), -self.radius.y(), self.radius.y()),
                );
                sum += self.evaluate(p);
            }
        }
        sum / n_samples as Float * area
    }

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