use crate::core::texture::{
    CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait,
};
use crate::utils::{FileLoc, TextureParameterDictionary};
use crate::Arena;
use anyhow::Result;
use shared::core::geometry::{Vector3f, VectorLike};
use shared::core::texture::{SpectrumType, TextureEvalContext};
use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::textures::{
    GPUFloatDirectionMixTexture, GPUFloatMixTexture, GPUSpectrumDirectionMixTexture,
    GPUSpectrumMixTexture,
};
use shared::utils::Transform;
use shared::Float;
use std::sync::Arc;

#[derive(Clone, Debug)]
pub struct FloatMixTexture {
    pub tex1: Arc<FloatTexture>,
    pub tex2: Arc<FloatTexture>,
    pub amount: Arc<FloatTexture>,
}

impl FloatMixTexture {
    pub fn new(
        tex1: Arc<FloatTexture>,
        tex2: Arc<FloatTexture>,
        amount: Arc<FloatTexture>,
    ) -> Self {
        Self { tex1, tex2, amount }
    }

    pub fn create(
        _render_from_texture: &Transform,
        params: &TextureParameterDictionary,
        _loc: &FileLoc,
        _arena: &Arena,
    ) -> Result<FloatTexture> {
        let tex1 = params.get_float_texture("tex1", 0.)?;
        let tex2 = params.get_float_texture("tex2", 1.)?;
        let amount = params.get_float_texture("amount", 0.5)?;
        // arena.alloc(Self::new(tex1, tex2, amount));
        Ok(FloatTexture::Mix(Self::new(tex1, tex2, amount)))
    }
}

impl FloatTextureTrait for FloatMixTexture {
    fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
        let amt = self.amount.evaluate(ctx);
        let mut t1 = 0.;
        let mut t2 = 0.;
        if amt != 1. {
            t1 = self.tex1.evaluate(ctx);
        }
        if amt != 0. {
            t2 = self.tex2.evaluate(ctx);
        }
        (1. - amt) * t1 + amt * t2
    }
}

#[derive(Clone, Debug)]
pub struct FloatDirectionMixTexture {
    pub tex1: Arc<FloatTexture>,
    pub tex2: Arc<FloatTexture>,
    pub dir: Vector3f,
}

impl FloatDirectionMixTexture {
    pub fn new(tex1: Arc<FloatTexture>, tex2: Arc<FloatTexture>, dir: Vector3f) -> Self {
        Self { tex1, tex2, dir }
    }

    pub fn create(
        render_from_texture: &Transform,
        params: &TextureParameterDictionary,
        _loc: &FileLoc,
        _arena: &Arena,
    ) -> Result<FloatTexture> {
        let dir_raw = params.get_one_vector3f("dir", Vector3f::new(0., 1., 0.))?;
        let dir = render_from_texture.apply_to_vector(dir_raw).normalize();
        let tex1 = params.get_float_texture("tex1", 0.)?;
        let tex2 = params.get_float_texture("tex2", 1.)?;
        // arena.alloc(Self::new(tex1, tex2, dir))
        Ok(FloatTexture::DirectionMix(Self::new(tex1, tex2, dir)))
    }
}

impl FloatTextureTrait for FloatDirectionMixTexture {
    fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
        todo!()
    }
}

#[derive(Clone, Debug)]
pub struct SpectrumMixTexture {
    pub tex1: Arc<SpectrumTexture>,
    pub tex2: Arc<SpectrumTexture>,
    pub amount: Arc<FloatTexture>,
}

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

impl SpectrumTextureTrait for SpectrumMixTexture {
    fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
        todo!()
    }
}

#[derive(Clone, Debug)]
pub struct SpectrumDirectionMixTexture {
    pub tex1: Arc<SpectrumTexture>,
    pub tex2: Arc<SpectrumTexture>,
    pub dir: Vector3f,
}

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

impl SpectrumTextureTrait for SpectrumDirectionMixTexture {
    fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
        todo!()
    }
}
