use crate::core::color::CreateRGBToSpectrumTable;
use once_cell::sync::Lazy;
use shared::core::color::{RGBToSpectrumTable, RES};
use shared::Float;
use shared::PBRTOptions;
use std::sync::OnceLock;

static OPTIONS: OnceLock<PBRTOptions> = OnceLock::new();

pub fn init_pbrt(options: PBRTOptions) {
    OPTIONS
        .set(options)
        .expect("PBRT has already been initialized!");
}

pub fn get_options() -> &'static PBRTOptions {
    OPTIONS.get().unwrap_or_else(|| {
        static DEFAULT: OnceLock<PBRTOptions> = OnceLock::new();
        DEFAULT.get_or_init(PBRTOptions::default)
    })
}

fn aligned_cast(bytes: &[u8]) -> &[Float] {
    match bytemuck::try_cast_slice(bytes) {
        Ok(s) => s,
        Err(_) => {
            let v: Vec<Float> = bytemuck::pod_collect_to_vec(bytes);
            Box::leak(v.into_boxed_slice())
        }
    }
}

static SRGB_SCALE_BYTES: &[u8] = include_bytes!("../data/srgb_scale.dat");
static SRGB_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat");
static DCI_P3_SCALE_BYTES: &[u8] = include_bytes!("../data/dcip3_scale.dat");
static DCI_P3_COEFFS_BYTES: &[u8] = include_bytes!("../data/dcip3_coeffs.dat");
static ACES_SCALE_BYTES: &[u8] = include_bytes!("../data/aces_scale.dat");
static ACES_COEFFS_BYTES: &[u8] = include_bytes!("../data/aces_coeffs.dat");
static REC2020_SCALE_BYTES: &[u8] = include_bytes!("../data/rec2020_scale.dat");
static REC2020_COEFFS_BYTES: &[u8] = include_bytes!("../data/rec2020_coeffs.dat");

fn strip_to_len(bytes: &[u8], expected_len: usize) -> &'static [Float] {
    let all: Vec<Float> = bytemuck::pod_collect_to_vec(bytes);
    let skip = all.len() - expected_len;
    let data = all[skip..].to_vec();
    Box::leak(data.into_boxed_slice())
}

const COEFFS_LEN: usize = (RES * RES * RES) as usize * 3 * 3;

pub static SRGB_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_SCALE_BYTES, RES as usize));
pub static SRGB_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_COEFFS_BYTES, COEFFS_LEN));

pub static DCI_P3_SCALE: Lazy<&[Float]> =
    Lazy::new(|| strip_to_len(DCI_P3_SCALE_BYTES, RES as usize));
pub static DCI_P3_COEFFS: Lazy<&[Float]> =
    Lazy::new(|| strip_to_len(DCI_P3_COEFFS_BYTES, COEFFS_LEN));

pub static ACES_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_SCALE_BYTES, RES as usize));
pub static ACES_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_COEFFS_BYTES, COEFFS_LEN));

pub static REC2020_SCALE: Lazy<&[Float]> =
    Lazy::new(|| strip_to_len(REC2020_SCALE_BYTES, RES as usize));
pub static REC2020_COEFFS: Lazy<&[Float]> =
    Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));

pub static SRGB_TABLE: Lazy<RGBToSpectrumTable> =
    Lazy::new(|| RGBToSpectrumTable::new(&SRGB_SCALE, &SRGB_COEFFS));
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTable> =
    Lazy::new(|| RGBToSpectrumTable::new(&DCI_P3_SCALE, &DCI_P3_COEFFS));
pub static REC2020_TABLE: Lazy<RGBToSpectrumTable> =
    Lazy::new(|| RGBToSpectrumTable::new(&REC2020_SCALE, &REC2020_COEFFS));
pub static ACES_TABLE: Lazy<RGBToSpectrumTable> =
    Lazy::new(|| RGBToSpectrumTable::new(&ACES_SCALE, &ACES_COEFFS));
