use crate::Float;
use crate::core::geometry::{Vector3f, VectorLike};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct GPUFloatMixTexture {
    pub tex1: Ptr<GPUFloatTexture>,
    pub tex2: Ptr<GPUFloatTexture>,
    pub amount: Ptr<GPUFloatTexture>,
}

impl GPUFloatMixTexture {
    pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
        let amt = self.amount.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0);
        let t1 = if amt != 1.0 {
            self.tex1.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0)
        } else {
            0.0
        };

        let t2 = if amt != 0.0 {
            self.tex2.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0)
        } else {
            0.0
        };

        (1.0 - amt) * t1 + amt * t2
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct GPUFloatDirectionMixTexture {
    pub tex1: Ptr<GPUFloatTexture>,
    pub tex2: Ptr<GPUFloatTexture>,
    pub dir: Vector3f,
}

impl GPUFloatDirectionMixTexture {
    pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
        let amt = self.dir.abs_dot(ctx.n.into());
        let t1 = if amt != 1.0 {
            self.tex1.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0)
        } else {
            0.0
        };

        let t2 = if amt != 0.0 {
            self.tex2.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0)
        } else {
            0.0
        };

        (1.0 - amt) * t1 + amt * t2
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct GPUSpectrumMixTexture {
    pub tex1: Ptr<GPUSpectrumTexture>,
    pub tex2: Ptr<GPUSpectrumTexture>,
    pub amount: Ptr<GPUFloatTexture>,
}

impl GPUSpectrumMixTexture {
    pub fn evaluate(
        &self,
        ctx: &TextureEvalContext,
        lambda: &SampledWavelengths,
    ) -> SampledSpectrum {
        let amt = self.amount.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0);
        let t1 = if amt != 1.0 {
            self.tex1
                .get()
                .map(|t| t.evaluate(ctx, lambda))
                .unwrap_or(SampledSpectrum::new(0.))
        } else {
            SampledSpectrum::new(0.)
        };

        let t2 = if amt != 0.0 {
            self.tex2
                .get()
                .map(|t| t.evaluate(ctx, lambda))
                .unwrap_or(SampledSpectrum::new(0.))
        } else {
            SampledSpectrum::new(0.)
        };

        (1.0 - amt) * t1 + amt * t2
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct GPUSpectrumDirectionMixTexture {
    pub tex1: Ptr<GPUSpectrumTexture>,
    pub tex2: Ptr<GPUSpectrumTexture>,
    pub dir: Vector3f,
}

impl GPUSpectrumDirectionMixTexture {
    pub fn evaluate(
        &self,
        ctx: &TextureEvalContext,
        lambda: &SampledWavelengths,
    ) -> SampledSpectrum {
        let amt = self.dir.abs_dot(ctx.n.into());
        let t1 = if amt != 1.0 {
            self.tex1
                .get()
                .map(|t| t.evaluate(ctx, lambda))
                .unwrap_or(SampledSpectrum::new(0.))
        } else {
            SampledSpectrum::new(0.)
        };

        let t2 = if amt != 0.0 {
            self.tex2
                .get()
                .map(|t| t.evaluate(ctx, lambda))
                .unwrap_or(SampledSpectrum::new(0.))
        } else {
            SampledSpectrum::new(0.)
        };

        (1.0 - amt) * t1 + amt * t2
    }
}
