use crate::core::spectrum::SPECTRUM_CACHE;
use crate::core::texture::FloatTexture;
use crate::spectra::DenselySampledSpectrumBuffer;
use crate::utils::containers::InternCache;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use anyhow::{Result, anyhow};
use shared::core::camera::CameraTransform;
use shared::core::light::Light;
use shared::core::medium::Medium;
use shared::core::shape::Shape;
use shared::core::spectrum::Spectrum;
use shared::lights::*;
use shared::spectra::RGBColorSpace;
use shared::utils::Transform;

pub fn lookup_spectrum(s: &Spectrum) -> DenselySampledSpectrumBuffer {
    let cache = SPECTRUM_CACHE.get_or_init(InternCache::new);
    let dense_spectrum = DenselySampledSpectrumBuffer::from_spectrum(s);
    cache.lookup(dense_spectrum).as_ref()
}

pub trait CreateLight {
    fn create(
        arena: &mut Arena,
        render_from_light: Transform,
        medium: Medium,
        parameters: &ParameterDictionary,
        loc: &FileLoc,
        shape: &Shape,
        alpha_text: &FloatTexture,
        colorspace: Option<&RGBColorSpace>,
    ) -> Result<Light>;
}

pub trait LightFactory {
    fn create(
        name: &str,
        arena: &mut Arena,
        render_from_light: Transform,
        medium: Medium,
        parameters: &ParameterDictionary,
        loc: &FileLoc,
        shape: &Shape,
        alpha_tex: &FloatTexture,
        colorspace: Option<&RGBColorSpace>,
        camera_transform: CameraTransform,
    ) -> Result<Self>;
}

impl LightFactory for Light {
    fn create(
        name: &str,
        arena: &mut Arena,
        render_from_light: Transform,
        medium: Medium,
        parameters: &ParameterDictionary,
        loc: &FileLoc,
        shape: &Shape,
        alpha_tex: &FloatTexture,
        colorspace: Option<&RGBColorSpace>,
        camera_transform: CameraTransform,
    ) -> Result<Self> {
        match name {
            "diffuse" => DiffuseAreaLight::create(
                name,
                arena,
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
            )?,
            "point" => PointLight::create(
                arena,
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
            )?,
            "spot" => SpotLight::create(
                arena,
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
            )?,
            "goniometric" => GoniometricLight::create(
                arena,
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
            )?,
            "projection" => ProjectionLight::create(
                arena,
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
            )?,
            "distant" => DistantLight::create(
                arena,
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
            )?,
            "infinite" => crate::lights::infinite::create(
                arena,
                render_from_light,
                medium,
                camera_transform,
                parameters,
                colorspace,
                loc,
            )?,
            _ => Err(anyhow!(loc, "unknown light type: \"{}\"", name)),
        }
    }
}
