use crate::defined::{float, PI};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct EmitIntensity {
    value: u8,
}

impl EmitIntensity {
    pub const MAX: EmitIntensity = EmitIntensity { value: 255 };
    pub const MIN: EmitIntensity = EmitIntensity { value: 0 };
    pub const DEFAULT_CORRECTED_ALPHA: float = 0.803;

    pub const fn new(value: u8) -> Self {
        Self { value }
    }

    pub fn with_correction(value: u8) -> Self {
        Self::with_correction_alpha(value, Self::DEFAULT_CORRECTED_ALPHA)
    }

    pub fn with_correction_alpha(value: u8, alpha: float) -> Self {
        Self {
            value: ((value as float / 255.).powf(1. / alpha).asin() / PI * 510.0).round() as u8,
        }
    }

    pub const fn value(&self) -> u8 {
        self.value
    }
}

impl From<u8> for EmitIntensity {
    fn from(v: u8) -> Self {
        Self::new(v)
    }
}

impl std::ops::Div<u8> for EmitIntensity {
    type Output = Self;

    fn div(self, rhs: u8) -> Self::Output {
        Self::new(self.value / rhs)
    }
}

impl std::ops::Div<u8> for &EmitIntensity {
    type Output = EmitIntensity;

    fn div(self, rhs: u8) -> Self::Output {
        Self::Output::new(self.value / rhs)
    }
}

impl std::ops::Add<EmitIntensity> for EmitIntensity {
    type Output = Self;

    fn add(self, rhs: EmitIntensity) -> Self::Output {
        Self::new(self.value + rhs.value)
    }
}

impl std::ops::Sub<EmitIntensity> for EmitIntensity {
    type Output = Self;

    fn sub(self, rhs: EmitIntensity) -> Self::Output {
        Self::new(self.value - rhs.value)
    }
}

#[cfg(test)]
impl EmitIntensity {
    pub fn random() -> Self {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        Self::new(rng.gen())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    

    #[test]
    fn test_new() {
        (0x00..=0xFF).for_each(|i| {
            assert_eq!(EmitIntensity::new(i).value(), i);
        });
    }

    #[test]
    fn test_with_correction() {
        (0x00..=0xFF).for_each(|i| {
            assert_eq!(
                EmitIntensity::with_correction(i).value(),
                ((i as float / 255.)
                    .powf(1. / EmitIntensity::DEFAULT_CORRECTED_ALPHA)
                    .asin()
                    / PI
                    * 510.0)
                    .round() as u8
            );
        });
    }

    #[test]
    fn test_div() {
        let intensity = EmitIntensity::new(0xFF);
        assert_eq!(intensity / 2, EmitIntensity::new(0x7F));
    }

    #[test]
    fn test_add() {
        let intensity1 = EmitIntensity::new(0x01);
        let intensity2 = EmitIntensity::new(0x02);
        assert_eq!(intensity1 + intensity2, EmitIntensity::new(0x03));
    }

    #[test]
    fn test_sub() {
        let intensity1 = EmitIntensity::new(0x03);
        let intensity2 = EmitIntensity::new(0x01);
        assert_eq!(intensity1 - intensity2, EmitIntensity::new(0x02));
    }

    #[test]
    fn test_clone() {
        let intensity = EmitIntensity::new(0);
        assert_eq!(intensity.value(), intensity.clone().value());
    }

    #[test]
    fn test_debug() {
        let intensity = EmitIntensity::new(0);
        assert_eq!(format!("{:?}", intensity), "EmitIntensity { value: 0 }");
    }

    #[test]
    fn test_ord() {
        let intensity1 = EmitIntensity::new(0);
        let intensity2 = EmitIntensity::new(1);
        assert!(intensity1 < intensity2);
        assert_eq!(intensity1.min(intensity2), intensity1);
    }
}
