use crate::core::spectrum::SPECTRUM_CACHE;
use crate::core::texture::FloatTexture;
use crate::spectra::DenselySampledSpectrumBuffer;
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;
use std::sync::Arc;

pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrumBuffer> {
    let cache = &SPECTRUM_CACHE;
    let dense_spectrum = DenselySampledSpectrumBuffer::from_spectrum(s);
    cache.lookup(dense_spectrum).into()
}

pub trait CreateLight {
    fn create(
        render_from_light: Transform,
        medium: Medium,
        parameters: &ParameterDictionary,
        loc: &FileLoc,
        shape: &Shape,
        alpha_text: &FloatTexture,
        colorspace: Option<&RGBColorSpace>,
        arena: &Arena,
    ) -> 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>
    where
        Self: Sized;
}

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>
    where
        Self: Sized,
    {
        match name {
            "diffuse" => DiffuseAreaLight::create(
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
                arena,
            ),
            "point" => PointLight::create(
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
                arena,
            ),
            "spot" => SpotLight::create(
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
                arena,
            ),
            "goniometric" => GoniometricLight::create(
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
                arena,
            ),
            "projection" => ProjectionLight::create(
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
                arena,
            ),
            "distant" => DistantLight::create(
                render_from_light,
                medium,
                parameters,
                loc,
                shape,
                alpha_tex,
                colorspace,
                arena,
            ),
            "infinite" => crate::lights::infinite::create(
                render_from_light,
                medium.into(),
                camera_transform,
                parameters,
                colorspace,
                loc,
                arena,
            ),
            _ => Err(anyhow!("{}: unknown light type: \"{}\"", loc, name)),
        }
    }
}
