use crate::core::geometry::{
    Bounds3f, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector3f, VectorLike,
};
use crate::core::interaction::{Interaction, InteractionBase, SimpleInteraction};
use crate::core::light::{
    Light, LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
};
use crate::core::spectrum::SpectrumTrait;
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
use crate::{Float, PI, Ptr, Transform};
use num_traits::Float as NumFloat;

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct PointLight {
    pub base: LightBase,
    pub scale: Float,
    pub i: Ptr<DenselySampledSpectrum>,
}

impl LightTrait for PointLight {
    fn base(&self) -> &LightBase {
        &self.base
    }

    fn sample_li(
        &self,
        ctx: &LightSampleContext,
        _u: Point2f,
        lambda: &SampledWavelengths,
        _allow_incomplete_pdf: bool,
    ) -> Option<LightLiSample> {
        let pi = self
            .base
            .render_from_light
            .apply_to_interval(&Point3fi::default());
        let p: Point3f = pi.into();
        let wi = (p - ctx.p()).normalize();
        let li = self.scale * self.i.sample(lambda) / p.distance_squared(ctx.p());
        let base = InteractionBase::new_boundary(p, 0., self.base.medium_interface);
        let intr = SimpleInteraction::new(base);
        Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr)))
    }

    fn pdf_li(
        &self,
        _ctx: &LightSampleContext,
        _wi: Vector3f,
        _allow_incomplete_pdf: bool,
    ) -> Float {
        0.
    }

    fn l(
        &self,
        _p: Point3f,
        _n: Normal3f,
        _uv: Point2f,
        _w: Vector3f,
        _lambda: &SampledWavelengths,
    ) -> SampledSpectrum {
        todo!()
    }

    fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
        todo!()
    }

    #[cfg(not(target_os = "cuda"))]
    fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
        4. * PI * self.scale * self.i.sample(&lambda)
    }

    #[cfg(not(target_os = "cuda"))]
    fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
        todo!()
    }

    #[cfg(not(target_os = "cuda"))]
    fn bounds(&self) -> Option<LightBounds> {
        let p = self
            .base
            .render_from_light
            .apply_to_point(Point3f::new(0., 0., 0.));
        let phi = 4. * PI * self.scale * self.i.max_value();
        Some(LightBounds::new(
            &Bounds3f::from_points(p, p),
            Vector3f::new(0., 0., 1.),
            phi,
            PI.cos(),
            (PI / 2.).cos(),
            false,
        ))
    }
}
