use super::cie::*;
use super::sampled::{LAMBDA_MAX, LAMBDA_MIN};
use crate::Float;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths};
use crate::utils::find_interval;
use crate::utils::ptr::Ptr;
use core::slice;
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, Copy, Clone)]
pub struct DenselySampledSpectrum {
    pub lambda_min: i32,
    pub lambda_max: i32,
    pub values: Ptr<Float>,
}

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

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

    #[inline(always)]
    fn get(&self, idx: u32) -> Float {
        unsafe { *self.values.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 SpectrumTrait for DenselySampledSpectrum {
    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;

            if offset < 0 || offset >= n {
                s[i] = 0.0;
            } else {
                unsafe {
                    s[i] = *self.values.add(offset as usize);
                }
            }
        }
        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 {
            unsafe { *self.values.add(offset as usize) }
        }
    }

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

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

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

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

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

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

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

impl SpectrumTrait for PiecewiseLinearSpectrum {
    fn evaluate(&self, lambda: Float) -> Float {
        if self.lambdas.is_null() {
            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_null() {
            return 0.;
        }

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

        for i in 0..n {
            unsafe {
                let val = *self.values.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)
    }
}
