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

/* GPU heavy code, dont know if this will ever work the way Im doing things.
* Leaving it here isolated, for careful handling */
#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct GPUSpectrumImageTexture {
    pub mapping: TextureMapping2D,
    pub tex_obj: u64,
    pub scale: Float,
    pub invert: bool,
    pub is_single_channel: bool,
    pub color_space: Ptr<RGBColorSpace>,
    pub spectrum_type: SpectrumType,
}

impl GPUSpectrumImageTexture {
    #[allow(unused)]
    pub fn evaluate(
        &self,
        ctx: &TextureEvalContext,
        lambda: &SampledWavelengths,
    ) -> SampledSpectrum {
        #[cfg(not(feature = "cuda"))]
        {
            return SampledSpectrum::zero();
        }

        #[cfg(feature = "cuda")]
        {
            use cuda_std::intrinsics;
            let c = self.mapping.map(ctx);
            let u = c.st.x();
            let v = 1.0 - c.st.y();

            let d_p_dx = [c.dsdx, c.dtdx];
            let d_p_dy = [c.dsdy, c.dtdy];

            let tex_color = if self.is_single_channel {
                let val = 0.;
                // let val: Float =
                //     unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
                RGB::new(val, val, val)
            } else {
                let val = [0., 0., 0., 0.];
                // let val: [Float; 4] =
                //     unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
                RGB::new(val[0], val[1], val[2])
            };

            let mut rgb = tex_color * self.scale;
            if self.invert {
                rgb = (RGB::new(1.0, 1.0, 1.0) - rgb).clamp_zero();
            }

            match self.spectrum_type {
                SpectrumType::Unbounded => {
                    RGBUnboundedSpectrum::new(&self.color_space, rgb).sample(lambda)
                }
                SpectrumType::Albedo => {
                    RGBAlbedoSpectrum::new(&self.color_space, rgb.clamp(0.0, 1.0)).sample(lambda)
                }
                _ => RGBIlluminantSpectrum::new(&self.color_space, rgb).sample(lambda),
            }
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct GPUFloatImageTexture {
    pub mapping: TextureMapping2D,
    pub tex_obj: u64,
    pub scale: Float,
    pub invert: bool,
}

impl GPUFloatImageTexture {
    #[allow(unused_variables)]
    pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
        #[cfg(not(feature = "cuda"))]
        {
            return 0.;
        }
        #[cfg(feature = "cuda")]
        #[allow(unused)]
        {
            use cuda_std::intrinsics;
            let c = self.mapping.map(ctx);
            let u = c.st.x();
            let v = 1.0 - c.st.y();
            let d_p_dx = [c.dsdx, c.dtdx];
            let d_p_dy = [c.dsdy, c.dtdy];
            // let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
            let val: Float = 0.;

            let result = if self.invert {
                // Invert the pixel intensity
                (1.0 - val).max(0.0)
            } else {
                val
            };

            return result * self.scale;
        }
    }
}
