use crate::Float;
use crate::core::color::{ColorEncoding, RGB};
use crate::core::spectrum::{SpectrumTrait, StandardSpectra};
use crate::core::texture::{SpectrumType, TextureEvalContext};
use crate::spectra::{
    DeviceStandardColorSpaces, RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum,
    RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths,
};
use crate::utils::Ptr;

#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GPUFloatPtexTexture {
    pub face_values: *const Float,
}

impl GPUFloatPtexTexture {
    pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
        unsafe { *self.face_values.add(ctx.face_index as usize) }
    }
}

#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct GPUSpectrumPtexTexture {
    pub face_values: Ptr<RGB>,
    pub n_faces: u32,
    pub spectrum_type: SpectrumType,
    pub colorspaces: DeviceStandardColorSpaces,
}

impl GPUSpectrumPtexTexture {
    pub fn evaluate(
        &self,
        ctx: &TextureEvalContext,
        lambda: &SampledWavelengths,
    ) -> SampledSpectrum {
        let index = (ctx.face_index as u32).clamp(0, self.n_faces.saturating_sub(1));
        let rgb = unsafe { &*self.face_values.add(index as usize) };
        let s_rgb = self.colorspaces.srgb;

        match self.spectrum_type {
            SpectrumType::Unbounded => RGBUnboundedSpectrum::new(&s_rgb, *rgb).sample(lambda),
            SpectrumType::Albedo => {
                let clamped_rgb = rgb.clamp(0.0, 1.0);
                RGBAlbedoSpectrum::new(&s_rgb, clamped_rgb).sample(lambda)
            }
            SpectrumType::Illuminant => RGBIlluminantSpectrum::new(&s_rgb, *rgb).sample(lambda),
        }
    }
}
