use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f};
use crate::core::pbrt::Float;
use crate::filters::*;
use crate::utils::containers::DeviceArray2D;
use crate::utils::math::{gaussian, gaussian_integral, lerp, sample_tent, windowed_sinc};
use crate::utils::sampling::DevicePiecewiseConstant2D;
use enum_dispatch::enum_dispatch;

pub struct FilterSample {
    pub p: Point2f,
    pub weight: Float,
}

#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct FilterSampler {
    pub domain: Bounds2f,
    pub distrib: DevicePiecewiseConstant2D,
    pub f: DeviceArray2D<Float>,
}

impl FilterSampler {
    pub fn sample(&self, u: Point2f) -> FilterSample {
        let (p, pdf, pi) = self.distrib.sample(u);

        if pdf == 0.0 {
            return FilterSample { p, weight: 0.0 };
        }

        let idx = pi.x() as u32 + self.f.x_size();
        let weight = *self.f.get_linear(idx as usize) / pdf;
        FilterSample { p, weight }
    }
}

#[enum_dispatch]
pub trait FilterTrait {
    fn radius(&self) -> Vector2f;
    fn evaluate(&self, p: Point2f) -> Float;
    fn integral(&self) -> Float;
    fn sample(&self, u: Point2f) -> FilterSample;
}

#[repr(C)]
#[enum_dispatch(FilterTrait)]
#[derive(Clone, Copy, Debug)]
pub enum Filter {
    Box(BoxFilter),
    Gaussian(GaussianFilter),
    Mitchell(MitchellFilter),
    LanczosSinc(LanczosSincFilter),
    Triangle(TriangleFilter),
}
