use crate::globals::{ACES_TABLE, DCI_P3_TABLE, REC2020_TABLE, SRGB_TABLE};
use crate::spectra::colorspace::CreateRGBColorSpace;
use anyhow::{anyhow, Result};
use shared::core::geometry::Point2f;
use shared::core::spectrum::{Spectrum, StandardSpectra};
use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z};
use shared::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
use shared::Ptr;
use std::sync::Arc;
use std::sync::LazyLock;

pub mod colorspace;
pub mod data;
pub mod piecewise;

pub static CIE_X_DATA: LazyLock<DenselySampledSpectrum> =
    LazyLock::new(|| data::create_cie(&CIE_X));
pub static CIE_Y_DATA: LazyLock<DenselySampledSpectrum> =
    LazyLock::new(|| data::create_cie(&CIE_Y));
pub static CIE_Z_DATA: LazyLock<DenselySampledSpectrum> =
    LazyLock::new(|| data::create_cie(&CIE_Z));
pub static CIE_D65_DATA: LazyLock<DenselySampledSpectrum> =
    LazyLock::new(|| data::create_cie(&CIE_D65));

fn get_d65_illuminant_buffer() -> Arc<DenselySampledSpectrum> {
    Arc::new(CIE_D65_DATA.clone())
}

pub fn cie_x() -> Spectrum {
    Spectrum::Dense(Ptr::from(&*CIE_X_DATA))
}
pub fn cie_y() -> Spectrum {
    Spectrum::Dense(Ptr::from(&*CIE_Y_DATA))
}
pub fn cie_z() -> Spectrum {
    Spectrum::Dense(Ptr::from(&*CIE_Z_DATA))
}
pub fn cie_d65() -> Spectrum {
    Spectrum::Dense(Ptr::from(&*CIE_D65_DATA))
}

pub fn get_spectra_context() -> StandardSpectra {
    StandardSpectra {
        x: Ptr::from(&*CIE_X_DATA),
        y: Ptr::from(&*CIE_Y_DATA),
        z: Ptr::from(&*CIE_Z_DATA),
        d65: Ptr::from(&*CIE_D65_DATA),
    }
}


pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
    let illum = get_d65_illuminant_buffer();
    let r = Point2f::new(0.64, 0.33);
    let g = Point2f::new(0.3, 0.6);
    let b = Point2f::new(0.15, 0.06);
    let table_ptr = Ptr::from(&*SRGB_TABLE);

    Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
});

pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
    let illum = get_d65_illuminant_buffer();
    let r = Point2f::new(0.680, 0.320);
    let g = Point2f::new(0.265, 0.690);
    let b = Point2f::new(0.150, 0.060);
    let table_ptr = Ptr::from(&*DCI_P3_TABLE);
    Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
});

pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
    let illum = get_d65_illuminant_buffer();
    let r = Point2f::new(0.708, 0.292);
    let g = Point2f::new(0.170, 0.797);
    let b = Point2f::new(0.131, 0.046);
    let table_ptr = Ptr::from(&*REC2020_TABLE);
    Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
});

pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
    let illum = get_d65_illuminant_buffer();
    let r = Point2f::new(0.7347, 0.2653);
    let g = Point2f::new(0.0000, 1.0000);
    let b = Point2f::new(0.0001, -0.0770);
    let table_ptr = Ptr::from(&ACES_TABLE);
    Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
});

#[derive(Debug, Clone)]
pub struct StandardColorSpaces {
    pub srgb: Arc<RGBColorSpaceData>,
    pub dci_p3: Arc<RGBColorSpaceData>,
    pub rec2020: Arc<RGBColorSpaceData>,
    pub aces2065_1: Arc<RGBColorSpaceData>,
}

impl StandardColorSpaces {
    pub fn get_named(&self, name: &str) -> Result<Arc<RGBColorSpace>> {
        match name.to_lowercase().as_str() {
            "srgb" => Ok(self.srgb.clone()),
            "dci-p3" => Ok(self.dci_p3.clone()),
            "rec2020" => Ok(self.rec2020.clone()),
            "aces2065-1" => Ok(self.aces2065_1.clone()),
            _ => Err(anyhow!("No such spectrum")),
        }
    }
}

pub fn get_colorspace_context() -> StandardColorSpaces {
    StandardColorSpaces {
        srgb: SRGB.clone().into(),
        dci_p3: DCI_P3.clone().into(),
        rec2020: REC2020.clone().into(),
        aces2065_1: ACES.clone().into(),
    }
}

pub fn get_colorspace_device() -> DeviceStandardColorSpaces {
    DeviceStandardColorSpaces {
        srgb: Ptr::from(&**SRGB),
        dci_p3: Ptr::from(&**DCI_P3),
        rec2020: Ptr::from(&**REC2020),
        aces2065_1: Ptr::from(&**ACES),
    }
}

pub fn default_colorspace() -> RGBColorSpace {
    let stdcs = get_colorspace_device();
    *stdcs.srgb
}

pub fn default_colorspace_arc() -> Arc<RGBColorSpace> {
    Arc::new(default_colorspace())
}

pub fn default_colorspace_ref() -> &'static RGBColorSpace {
    static CS: OnceLock<RGBColorSpace> = OnceLock::new();
    CS.get_or_init(|| stdcs.srgb)
}

pub fn default_illuminant() -> Spectrum {
    Spectrum::Dense(default_colorspace().illuminant)
}
