use crate::core::light::{CreateLight, lookup_spectrum};
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use anyhow::Result;
use shared::core::geometry::Point3f;
use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface};
use shared::core::shape::Shape;
use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType;
use shared::lights::PointLight;
use shared::spectra::RGBColorSpace;
use shared::utils::{Ptr, Transform};
use shared::{Float, PI};

pub trait CreatePointLight {
    fn new(
        render_from_light: Transform,
        medium_interface: MediumInterface,
        le: Spectrum,
        scale: Float,
    ) -> Self;
}

impl CreatePointLight for PointLight {
    fn new(
        render_from_light: Transform,
        medium_interface: MediumInterface,
        le: Spectrum,
        scale: Float,
    ) -> Self {
        let base = LightBase::new(
            LightType::DeltaPosition,
            render_from_light,
            medium_interface,
        );
        let iemit = lookup_spectrum(&le);
        let i = Ptr::from(iemit.device());

        Self { base, scale, i }
    }
}

impl CreateLight for PointLight {
    fn create(
        _arena: &mut Arena,
        render_from_light: Transform,
        medium: Medium,
        parameters: &ParameterDictionary,
        _loc: &FileLoc,
        _shape: &Shape,
        _alpha: &FloatTexture,
        colorspace: Option<&RGBColorSpace>,
    ) -> Result<Light> {
        let l = parameters
            .get_one_spectrum(
                "L",
                Some(Spectrum::Dense(colorspace.unwrap().illuminant)),
                SpectrumType::Illuminant,
            )
            .unwrap();
        let mut scale = parameters.get_one_float("scale", 1.);
        scale /= spectrum_to_photometric(l);
        let phi_v = parameters.get_one_float("power", 1.);
        if phi_v > 0. {
            let k_e = 4. * PI;
            scale *= phi_v / k_e;
        }

        let from = parameters.get_one_point3f("from", Point3f::zero());
        let tf = Transform::translate(from.into());
        let final_render = render_from_light * tf;
        let specific = PointLight::new(final_render, medium.into(), l, scale);
        Ok(Light::Point(specific))
    }
}
