use crate::core::spectrum::SPECTRUM_CACHE;
use crate::core::texture::FloatTexture;
use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{anyhow, Result};
use shared::core::camera::CameraTransform;
use shared::core::light::Light;
use shared::core::medium::Medium;
use shared::core::shape::Shape;
use shared::spectra::DenselySampledSpectrum;
use shared::core::spectrum::Spectrum;
use shared::spectra::RGBColorSpace;
use shared::Transform;
use std::sync::Arc;

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

// Placeholders for non-area lights that never inspect these arguments.
// TODO: refactor each light to only take what it actually needs,
// then delete this bullshit
fn dummy_shape() -> Shape {
    Shape::default()
}

fn dummy_alpha() -> FloatTexture {
    FloatTexture::default()
}

pub fn create_light(
    name: &str,
    render_from_light: Transform,
    medium: Option<Medium>,
    parameters: &ParameterDictionary,
    loc: &FileLoc,
    camera_transform: CameraTransform,
    arena: &mut Arena,
) -> Result<Light> {
    let shape = dummy_shape();
    let alpha = dummy_alpha();

    match name {
        "point" => crate::lights::point::create(
            render_from_light,
            medium,
            parameters,
            loc,
            &shape,
            &alpha,
            None,
            arena,
        ),
        "spot" => crate::lights::spot::create(
            render_from_light,
            medium,
            parameters,
            loc,
            &shape,
            &alpha,
            None,
            arena,
        ),
        "distant" => crate::lights::distant::create(
            render_from_light,
            medium,
            parameters,
            loc,
            &shape,
            &alpha,
            None,
            arena,
        ),
        "goniometric" => crate::lights::goniometric::create(
            render_from_light,
            medium,
            parameters,
            loc,
            &shape,
            &alpha,
            None,
            arena,
        ),
        "projection" => crate::lights::projection::create(
            render_from_light,
            medium,
            parameters,
            loc,
            &shape,
            &alpha,
            None,
            arena,
        ),
        "infinite" => crate::lights::infinite::create(
            render_from_light,
            medium.into(),
            camera_transform,
            parameters,
            None,
            loc,
            arena,
        ),
        "diffuse" => Err(anyhow!(
            "{}: \"diffuse\" is an area light. Use create_area_light with a shape",
            loc
        )),
        _ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)),
    }
}

pub fn create_area_light(
    render_from_light: Transform,
    medium: Option<Medium>,
    parameters: &ParameterDictionary,
    loc: &FileLoc,
    shape: &Shape,
    alpha_tex: &FloatTexture,
    colorspace: Option<&RGBColorSpace>,
    arena: &mut Arena,
) -> Result<Ptr<Light>> {
    let light = crate::lights::diffuse::create(
        render_from_light, medium, parameters, loc,
        shape, alpha_tex, colorspace, arena,
    )?;
    Ok(arena.alloc(light))
}
