use crate::Float;
use crate::core::spectrum::Spectrum;
use crate::core::spectrum::SpectrumTrait;
use crate::core::texture::{TextureEvalContext, TextureMapping2D};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::{Ptr, Transform};

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct FloatBilerpTexture {
    pub mapping: TextureMapping2D,
    pub v00: Float,
    pub v01: Float,
    pub v10: Float,
    pub v11: Float,
}

#[inline(always)]
pub fn bilerp(st_frac: [Float; 2], v: [SampledSpectrum; 4]) -> SampledSpectrum {
    let sx = st_frac[0];
    let sy = st_frac[1];
    let one_minus_sx = 1.0 - sx;
    let one_minus_sy = 1.0 - sy;

    one_minus_sx * one_minus_sy * v[0]
        + sx * one_minus_sy * v[1]
        + one_minus_sx * sy * v[2]
        + sx * sy * v[3]
}

impl FloatBilerpTexture {
    pub fn new(mapping: TextureMapping2D, v00: Float, v01: Float, v10: Float, v11: Float) -> Self {
        Self {
            mapping,
            v00,
            v01,
            v10,
            v11,
        }
    }

    pub fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
        todo!()
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct SpectrumBilerpTexture {
    pub mapping: TextureMapping2D,
    pub v00: Ptr<Spectrum>,
    pub v01: Ptr<Spectrum>,
    pub v10: Ptr<Spectrum>,
    pub v11: Ptr<Spectrum>,
}

impl SpectrumBilerpTexture {
    pub fn new(
        mapping: TextureMapping2D,
        v00: Ptr<Spectrum>,
        v01: Ptr<Spectrum>,
        v10: Ptr<Spectrum>,
        v11: Ptr<Spectrum>,
    ) -> Self {
        Self {
            mapping,
            v00,
            v01,
            v10,
            v11,
        }
    }

    pub fn evaluate(
        &self,
        ctx: &TextureEvalContext,
        lambda: &SampledWavelengths,
    ) -> SampledSpectrum {
        let c = self.mapping.map(ctx);
        bilerp(
            [c.st[0], c.st[1]],
            [
                self.v00.sample(lambda),
                self.v01.sample(lambda),
                self.v10.sample(lambda),
                self.v11.sample(lambda),
            ],
        )
    }
}
