use super::cie::*;
use super::sampled::{LAMBDA_MAX, LAMBDA_MIN};
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::spectra::{SampledSpectrum, SampledWavelengths, N_SPECTRUM_SAMPLES};
use crate::utils::find_interval;
use crate::{gvec, gvec_with_capacity, Float, GVec, Ptr};
use core::hash::{Hash, Hasher};
use num_traits::Float as NumFloat;

#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ConstantSpectrum {
    pub c: Float,
}

impl ConstantSpectrum {
    pub fn new(c: Float) -> Self {
        Self { c }
    }
}

impl SpectrumTrait for ConstantSpectrum {
    fn evaluate(&self, _lambda: Float) -> Float {
        self.c
    }

    fn max_value(&self) -> Float {
        self.c
    }
}

#[repr(C)]
#[derive(Debug, Clone)]
pub struct DenselySampledSpectrum {
    pub lambda_min: i32,
    pub lambda_max: i32,
    pub values: GVec<Float>,
}

unsafe impl Send for DenselySampledSpectrum {}
unsafe impl Sync for DenselySampledSpectrum {}

impl DenselySampledSpectrum {
    pub fn new(lambda_min: i32, lambda_max: i32, values: GVec<Float>) -> Self {
        let func_integral = 0.0;
        Self {
            lambda_min,
            lambda_max,
            values,
        }
    }

    pub fn new_zero(lambda_min: i32, lambda_max: i32) -> Self {
        let n = (lambda_max - lambda_min + 1).max(0) as usize;
        let mut values = gvec_with_capacity(n);
        values.resize(n, 0.0);
        Self {
            lambda_min,
            lambda_max,
            values,
        }
    }

    pub fn from_spectrum(spec: &Spectrum) -> Self {
        let mut values = gvec_with_capacity((LAMBDA_MAX - LAMBDA_MIN + 1) as usize);
        for lambda in LAMBDA_MIN..=LAMBDA_MAX {
            values.push(spec.evaluate(lambda as Float));
        }
        Self {
            lambda_min: LAMBDA_MIN,
            lambda_max: LAMBDA_MAX,
            values,
        }
    }

    pub fn from_function<F>(f: F, lambda_min: i32, lambda_max: i32) -> Self
    where
        F: Fn(Float) -> Float,
    {
        let mut values = gvec_with_capacity((lambda_max - lambda_min + 1) as usize);
        for lambda in lambda_min..=lambda_max {
            values.push(f(lambda as Float));
        }
        Self {
            lambda_min,
            lambda_max,
            values,
        }
    }

    pub fn scale(&mut self, s: Float) {
        for v in &mut self.values {
            *v *= s;
        }
    }

    #[inline(always)]
    pub fn count(&self) -> usize {
        if self.values.is_empty() {
            0
        } else {
            (self.lambda_max - self.lambda_min + 1) as usize
        }
    }

    #[inline(always)]
    pub fn value(&self, idx: u32) -> Float {
        unsafe { *self.values.as_ptr().add(idx as usize) }
    }
}

impl PartialEq for DenselySampledSpectrum {
    fn eq(&self, other: &Self) -> bool {
        self.lambda_min == other.lambda_min
            && self.lambda_max == other.lambda_max
            && self.values == other.values
    }
}

impl Eq for DenselySampledSpectrum {}

impl Hash for DenselySampledSpectrum {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.lambda_min.hash(state);
        self.lambda_max.hash(state);
        for &val in self.values.iter() {
            val.to_bits().hash(state);
        }
    }
}

impl SpectrumTrait for DenselySampledSpectrum {
    fn max_value(&self) -> Float {
        if self.values.is_empty() {
            return 0.0;
        }
        let mut max_val = Float::NEG_INFINITY;
        for i in 0..self.count() {
            let val = self.value(i as u32);
            if val > max_val {
                max_val = val;
            }
        }
        max_val
    }

    fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
        let mut s = SampledSpectrum::default();
        let n = self.count() as i32;
        for i in 0..N_SPECTRUM_SAMPLES {
            let offset = lambda[i].round() as i32 - self.lambda_min;
            s[i] = if offset < 0 || offset >= n {
                0.0
            } else {
                self.value(offset as u32)
            };
        }
        s
    }

    fn evaluate(&self, lambda: Float) -> Float {
        let offset = (lambda.round() as i32) - self.lambda_min;
        let n = self.count() as i32;
        if offset < 0 || offset >= n {
            0.0
        } else {
            self.value(offset as u32)
        }
    }
}

#[repr(C)]
#[derive(Debug, Clone)]
pub struct PiecewiseLinearSpectrum {
    pub lambdas: GVec<Float>,
    pub values: GVec<Float>,
    pub count: u32,
}

impl PiecewiseLinearSpectrum {
    #[inline(always)]
    pub fn count(&self) -> usize {
        self.count.try_into().unwrap()
    }


    #[inline(always)]
    pub fn lambda(&self, idx: u32) -> Float {
        unsafe { *self.lambdas.as_ptr().add(idx as usize) }
    }

    #[inline(always)]
    pub fn value(&self, idx: u32) -> Float {
        unsafe { *self.values.as_ptr().add(idx as usize) }
    }

    pub fn new(lambdas: GVec<Float>, values: GVec<Float>) -> Self {
        assert_eq!(lambdas.len(), values.len());
        let count = lambdas.len() as u32;
        Self {
            lambdas,
            values,
            count,
        }
    }

    pub fn from_interleaved(data: &[Float], _normalize: bool) -> Self {
        assert!(
            data.len() % 2 == 0,
            "Interleaved data must have even length"
        );

        let n = data.len() / 2;
        let mut pairs: GVec<(Float, Float)> = gvec_with_capacity(n);
        for chunk in data.chunks(2) {
            pairs.push((chunk[0], chunk[1]));
        }
        pairs.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(core::cmp::Ordering::Equal));

        let mut lambdas = gvec_with_capacity(n);
        let mut values = gvec_with_capacity(n);
        for (l, v) in pairs.iter() {
            lambdas.push(*l);
            values.push(*v);
        }
        Self::new(lambdas, values)
    }
}

unsafe impl Send for PiecewiseLinearSpectrum {}
unsafe impl Sync for PiecewiseLinearSpectrum {}

impl SpectrumTrait for PiecewiseLinearSpectrum {
    fn evaluate(&self, lambda: Float) -> Float {
        if self.lambdas.is_empty() {
            return 0.0;
        }

        if lambda <= self.lambda(0) {
            return self.value(0);
        }

        if lambda >= self.lambda(self.count - 1) {
            return self.value(self.count - 1);
        }

        let i = find_interval(self.count, |idx| self.lambda(idx) <= lambda);

        let l0 = self.lambda(i);
        let l1 = self.lambda(i + 1);
        let v0 = self.value(i);
        let v1 = self.value(i + 1);

        let t = (lambda - l0) / (l1 - l0);
        v0 + t * (v1 - v0)
    }

    fn max_value(&self) -> Float {
        if self.values.is_empty() {
            return 0.;
        }

        let n = self.count;
        let mut max_val = Float::NEG_INFINITY;

        for i in 0..n {
            unsafe {
                let val = *self.values.as_ptr().add(i as usize);
                if val > max_val {
                    max_val = val;
                }
            }
        }
        max_val
    }
}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct BlackbodySpectrum {
    pub temperature: Float,
    pub normalization_factor: Float,
}

// Planck's Law
impl BlackbodySpectrum {
    const C: Float = 299792458.0;
    const H: Float = 6.62606957e-34;
    const KB: Float = 1.3806488e-23;

    pub fn new(temperature: Float) -> Self {
        // Physical constants
        let lambda_max = 2.8977721e-3 / temperature * 1e9;
        let max_val = Self::planck_law(lambda_max, temperature);
        Self {
            temperature,
            normalization_factor: if max_val > 0.0 { 1.0 / max_val } else { 0.0 },
        }
    }

    fn planck_law(lambda_nm: Float, temp: Float) -> Float {
        if temp <= 0.0 {
            return 0.0;
        }
        let lambda_m = lambda_nm * 1e-9;
        let c1 = 2.0 * Self::H * Self::C * Self::C;
        let c2 = (Self::H * Self::C) / Self::KB;

        let numerator = c1 / lambda_m.powi(5);
        let denominator = (c2 / (lambda_m * temp)).exp() - 1.0;

        if denominator.is_infinite() {
            0.0
        } else {
            numerator / denominator
        }
    }

    pub fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
        SampledSpectrum::from_fn(|i| {
            Self::planck_law(lambda[i], self.temperature) * self.normalization_factor
        })
    }
}

impl SpectrumTrait for BlackbodySpectrum {
    fn evaluate(&self, lambda: Float) -> Float {
        Self::planck_law(lambda, self.temperature) * self.normalization_factor
    }

    fn max_value(&self) -> Float {
        let lambda_max = 2.8977721e-3 / self.temperature * 1e9;
        Self::planck_law(lambda_max, self.temperature)
    }
}
