use crate::utils::read_float_file;
use shared::Float;
use shared::spectra::PiecewiseLinearSpectrum;
use std::ops::Deref;
use std::sync::atomic::Ordering;

pub struct PiecewiseLinearSpectrumBuffer {
    pub view: PiecewiseLinearSpectrum,
    _lambdas: Vec<Float>,
    _values: Vec<Float>,
}

impl Deref for PiecewiseLinearSpectrumBuffer {
    type Target = PiecewiseLinearSpectrum;
    fn deref(&self) -> &Self::Target {
        &self.view
    }
}

impl PiecewiseLinearSpectrumBuffer {
    pub fn new(lambdas: Vec<Float>, values: Vec<Float>) -> Self {
        assert_eq!(lambdas.len(), values.len());
        let view = PiecewiseLinearSpectrum {
            lambdas: lambdas.as_ptr(),
            values: values.as_ptr(),
            count: lambdas.len() as u32,
        };
        Self {
            view,
            _lambdas: lambdas,
            _values: values,
        }
    }

    pub fn from_interleaved(data: &[Float], _normalize: bool) -> Self {
        if data.len() % 2 != 0 {
            panic!("Interleaved data must have an even number of elements");
        }

        let mut temp: Vec<(Float, Float)> =
            data.chunks(2).map(|chunk| (chunk[0], chunk[1])).collect();

        temp.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));

        let (lambdas, values): (Vec<Float>, Vec<Float>) = temp.into_iter().unzip();

        // (Normalization logic usually goes here)

        Self::new(lambdas, values)
    }

    pub fn read(filepath: &str) -> Option<Self> {
        let vals = read_float_file(filepath).ok()?;

        if vals.is_empty() || vals.len() % 2 == 0 {
            return None;
        }

        let count = vals.len() / 2;
        let mut lambdas = Vec::with_capacity(count);
        let mut values = Vec::with_capacity(count);

        for (_, pair) in vals.chunks(2).enumerate() {
            let curr_lambda = pair[0];
            let curr_val = pair[1];

            if let Some(&prev_lambda) = lambdas.last() {
                if curr_lambda <= prev_lambda {
                    return None;
                }
            }

            lambdas.push(curr_lambda);
            values.push(curr_val);
        }

        Some(Self::new(lambdas, values))
    }
}
