use crate::spectra::get_spectra_context;
use shared::core::color::{RGBToSpectrumTable, RGB, XYZ};
use shared::core::geometry::Point2f;
use shared::core::spectrum::Spectrum;
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
use shared::utils::math::SquareMatrix;
use shared::Ptr;
use std::sync::Arc;

pub trait CreateRGBColorSpace {
    fn new(
        r: Point2f,
        g: Point2f,
        b: Point2f,
        illuminant: &DenselySampledSpectrum,
        rgb_to_spectrum_table: &RGBToSpectrumTable,
    ) -> Self;
}

impl CreateRGBColorSpace for RGBColorSpace {
    fn new(
        r: Point2f,
        g: Point2f,
        b: Point2f,
        illuminant: &DenselySampledSpectrum,
        rgb_to_spectrum_table: &RGBToSpectrumTable,
    ) -> Self {
        let stdspec = get_spectra_context();
        let illum_ptr = Ptr::from(illuminant);
        let illum_spectrum = Spectrum::Dense(illum_ptr);
        let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
        let w = w_xyz.xy();

        let r_xyz = XYZ::from_xyy(r, Some(1.0));
        let g_xyz = XYZ::from_xyy(g, Some(1.0));
        let b_xyz = XYZ::from_xyy(b, Some(1.0));

        let rgb_values = [
            [r_xyz.x(), g_xyz.x(), b_xyz.x()],
            [r_xyz.y(), g_xyz.y(), b_xyz.y()],
            [r_xyz.z(), g_xyz.z(), b_xyz.z()],
        ];
        let rgb = SquareMatrix::new(rgb_values);
        let c: RGB = rgb.inverse().unwrap() * w_xyz;
        let xyz_from_rgb = rgb * SquareMatrix::diag(&[c.r, c.g, c.b]);
        let rgb_from_xyz = xyz_from_rgb.inverse().expect("singular");

        RGBColorSpace {
            r,
            g,
            b,
            w,
            illuminant: illum_ptr,
            rgb_to_spectrum_table: Ptr::from(rgb_to_spectrum_table),
            xyz_from_rgb,
            rgb_from_xyz,
        }
    }
}
