use crate::core::color::{RGB, RGBSigmoidPolynomial, RGBToSpectrumTable, XYZ};
use crate::core::geometry::Point2f;
use crate::core::pbrt::Float;
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
use crate::utils::math::SquareMatrix3f;
use crate::utils::ptr::Ptr;
use core::cmp::{Eq, PartialEq};

#[repr(C)]
#[derive(Copy, Debug, Clone)]
pub struct DeviceStandardColorSpaces {
    pub srgb: Ptr<RGBColorSpace>,
    pub dci_p3: Ptr<RGBColorSpace>,
    pub rec2020: Ptr<RGBColorSpace>,
    pub aces2065_1: Ptr<RGBColorSpace>,
}

impl DeviceStandardColorSpaces {
    #[cfg(not(target_arch = "nvptx64"))]
    pub fn get_named(&self, name: &str) -> Option<Ptr<RGBColorSpace>> {
        let lower = name.as_bytes();
        match lower {
            b if b.eq_ignore_ascii_case(b"srgb") => Some(self.srgb),
            b if b.eq_ignore_ascii_case(b"dci-p3") => Some(self.dci_p3),
            b if b.eq_ignore_ascii_case(b"rec2020") => Some(self.rec2020),
            b if b.eq_ignore_ascii_case(b"aces2065-1") => Some(self.aces2065_1),
            _ => None,
        }
    }

    pub fn get_by_id(&self, id: ColorSpaceId) -> Ptr<RGBColorSpace> {
        match id {
            ColorSpaceId::SRGB => self.srgb,
            ColorSpaceId::DciP3 => self.dci_p3,
            ColorSpaceId::Rec2020 => self.rec2020,
            ColorSpaceId::Aces2065_1 => self.aces2065_1,
        }
    }
}

#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ColorSpaceId {
    SRGB = 0,
    DciP3 = 1,
    Rec2020 = 2,
    Aces2065_1 = 3,
}

impl ColorSpaceId {
    #[cfg(not(target_arch = "nvptx64"))]
    pub fn from_name(name: &str) -> Option<Self> {
        let lower = name.as_bytes();
        match lower {
            b if b.eq_ignore_ascii_case(b"srgb") => Some(Self::SRGB),
            b if b.eq_ignore_ascii_case(b"dci-p3") => Some(Self::DciP3),
            b if b.eq_ignore_ascii_case(b"rec2020") => Some(Self::Rec2020),
            b if b.eq_ignore_ascii_case(b"aces2065-1") => Some(Self::Aces2065_1),
            _ => None,
        }
    }
}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RGBColorSpace {
    pub r: Point2f,
    pub g: Point2f,
    pub b: Point2f,
    pub w: Point2f,
    pub xyz_from_rgb: SquareMatrix3f,
    pub rgb_from_xyz: SquareMatrix3f,
    pub illuminant: Ptr<DenselySampledSpectrum>,
    pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
}

unsafe impl Send for RGBColorSpace {}
unsafe impl Sync for RGBColorSpace {}

impl RGBColorSpace {
    pub fn to_xyz(&self, rgb: RGB) -> XYZ {
        self.xyz_from_rgb * rgb
    }

    pub fn to_rgb(&self, xyz: XYZ) -> RGB {
        self.rgb_from_xyz * xyz
    }

    pub fn to_rgb_coeffs(&self, rgb: RGB) -> RGBSigmoidPolynomial {
        self.rgb_to_spectrum_table.evaluate(rgb)
    }

    pub fn convert_colorspace(&self, other: &RGBColorSpace) -> SquareMatrix3f {
        if self == other {
            return SquareMatrix3f::default();
        }

        self.rgb_from_xyz * other.xyz_from_rgb
    }

    pub fn luminance_vector(&self) -> RGB {
        RGB::new(
            self.xyz_from_rgb[1][0],
            self.xyz_from_rgb[1][1],
            self.xyz_from_rgb[1][2],
        )
    }
}

impl PartialEq for RGBColorSpace {
    fn eq(&self, other: &Self) -> bool {
        self.r == other.r
            && self.g == other.g
            && self.b == other.b
            && self.w == other.w
            && self.rgb_to_spectrum_table == other.rgb_to_spectrum_table
    }
}
