use crate::core::light::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::{Float, PI, Ptr, Transform};

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

impl CreatePointLight for PointLight {
    fn new(
        render_from_light: Transform,
        medium_interface: MediumInterface,
        le: Spectrum,
        scale: Float,
        arena: &Arena,
    ) -> Self {
        let base = LightBase::new(
            LightType::DeltaPosition,
            render_from_light,
            medium_interface,
        );
        let iemit = lookup_spectrum(&le);
        let i = arena.alloc((*iemit).clone());
        Self { base, scale, i }
    }
}


pub fn create(
    render_from_light: Transform,
    medium: Option<Medium>,
    parameters: &ParameterDictionary,
    _loc: &FileLoc,
    _shape: &Shape,
    _alpha: &FloatTexture,
    colorspace: Option<&RGBColorSpace>,
    arena: &Arena,
) -> Result<Light> {
    let default_cs = crate::spectra::default_colorspace();
    let cs = colorspace.unwrap_or(&default_cs);

    let l = parameters
        .get_one_spectrum(
            "L",
            Some(Spectrum::Dense(cs.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 mi = match medium {
        Some(m) => {
            let ptr = arena.alloc(m);
            MediumInterface {
                inside: ptr,
                outside: ptr,
            }
        }
        None => MediumInterface::default(),
    };

    let specific = PointLight::new(final_render, mi, l, scale, arena);
    Ok(Light::Point(specific))
}
