use std::fmt::Debug;

use femtovg::Color;
use num_traits::NumOps;
pub trait ColorMapper<T: NumOps + PartialOrd> : Debug + Send + Sync {
    fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color>;
}

#[derive(Debug)]
pub struct BoundaryNorm<T: NumOps + PartialOrd> {
    boundaries: Vec<T>,
    extrand: bool,
    colors: Vec<femtovg::Color>,
}

impl Default for BoundaryNorm<i8> {
    fn default() -> Self {
        Self {
            boundaries: vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65],
            extrand: true,
            colors: vec![
                Color::rgb(0, 172, 164),
                Color::rgb(192, 192, 254),
                Color::rgb(122, 114, 238),
                Color::rgb(30, 38, 208),
                Color::rgb(166, 252, 168),
                Color::rgb(0, 234, 0),
                Color::rgb(16, 146, 26),
                Color::rgb(252, 244, 100),
                Color::rgb(200, 200, 2),
                Color::rgb(140, 140, 0),
                Color::rgb(254, 172, 172),
                Color::rgb(254, 100, 92),
                Color::rgb(238, 2, 48),
                Color::rgb(212, 142, 254),
                Color::rgb(170, 36, 250),
            ],
        }
    }
}

impl<T: NumOps + PartialOrd> BoundaryNorm<T> {
    pub fn new(boundaries: Vec<T>, colors: Vec<femtovg::Color>, extrand: bool) -> Self {
        // assert_eq!(boundaries.len(), colors.len() + 1);
        BoundaryNorm {
            boundaries,
            extrand,
            colors,
        }
    }

    pub fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
        let mut index = 0;
        if value == invalid_value {
            return None;
        }
        for (i, boundary) in self.boundaries.iter().enumerate() {
            if value < *boundary {
                break;
            }
            index = i;
        }
        index = index.saturating_sub(1).min(self.colors.len() - 1);
        Some(self.colors[index])
    }
}

impl<T: NumOps + PartialOrd + Debug + Send + Sync> ColorMapper<T> for BoundaryNorm<T> {
    fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
        self.map_value_to_color(value, invalid_value)
    }
}
