use crate::core::texture::{CreateSpectrumTexture, FloatTexture, SpectrumTexture};
use crate::core::texture::{FloatTextureTrait, SpectrumTextureTrait};
use crate::utils::{FileLoc, TextureParameterDictionary};
use crate::Arena;
use anyhow::Result;
use shared::core::texture::{SpectrumType, TextureEvalContext};
use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::textures::{GPUFloatScaledTexture, GPUSpectrumScaledTexture};
use shared::utils::Transform;
use shared::Float;
use std::sync::Arc;

#[derive(Clone, Debug)]
pub struct FloatScaledTexture {
    pub tex: Arc<FloatTexture>,
    pub scale: Arc<FloatTexture>,
}

impl FloatScaledTexture {
    pub fn new(tex: Arc<FloatTexture>, scale: Arc<FloatTexture>) -> Self {
        Self { tex, scale }
    }

    pub fn create(
        _render_from_texture: &Transform,
        params: &TextureParameterDictionary,
        _loc: &FileLoc,
        _arena: &Arena,
    ) -> Result<FloatTexture> {
        let mut tex = params.get_float_texture("tex", 1.)?;
        let mut scale = params.get_float_texture("scale", 1.)?;

        for _ in 0..2 {
            if let FloatTexture::Constant(c_tex) = &*scale {
                let cs = c_tex.value;
                if cs == 1.0 {
                    return Ok((*tex).clone());
                } else if let FloatTexture::Image(img_tex) = &*tex {
                    let mut image_copy = img_tex.clone();
                    image_copy.base.multiply_scale(cs);
                    return Ok(FloatTexture::Image(image_copy));
                }
            }

            std::mem::swap(&mut tex, &mut scale);
        }
        std::mem::swap(&mut tex, &mut scale);
        Ok(FloatTexture::Scaled(FloatScaledTexture::new(
            tex.clone(),
            scale.clone(),
        )))
    }
}

impl FloatTextureTrait for FloatScaledTexture {
    fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
        let sc = self.scale.evaluate(ctx);
        if sc == 0. {
            return 0.;
        }
        self.tex.evaluate(ctx)
    }
}

#[derive(Clone, Debug)]
pub struct SpectrumScaledTexture {
    pub tex: Arc<SpectrumTexture>,
    pub scale: Arc<FloatTexture>,
}

impl CreateSpectrumTexture for SpectrumScaledTexture {
    fn create(
        _render_from_texture: Transform,
        _parameters: TextureParameterDictionary,
        _spectrum_type: SpectrumType,
        _loc: FileLoc,
    ) -> Result<SpectrumTexture> {
        todo!()
    }
}

impl SpectrumTextureTrait for SpectrumScaledTexture {
    fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
        let sc = self.scale.evaluate(ctx);
        if sc == 0. {
            return SampledSpectrum::new(0.);
        }
        self.tex.evaluate(ctx, lambda) * sc
    }
}
