use aksr::Builder;
use std::convert::TryFrom;

#[derive(Debug, Builder, PartialEq, Eq, Copy, Clone, Hash, Default, PartialOrd, Ord)]
pub struct Version(pub u8, pub u8, pub Option<u8>);

impl Version {
    pub fn new(major: u8, minor: u8) -> Self {
        Self(major, minor, None)
    }
}

impl std::fmt::Display for Version {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.2 {
            None => {
                if self.1 == 0 {
                    write!(f, "v{}", self.0)
                } else {
                    write!(f, "v{}.{}", self.0, self.1)
                }
            }
            Some(patch) => write!(f, "v{}.{}.{}", self.0, self.1, patch),
        }
    }
}

impl From<u8> for Version {
    fn from(major: u8) -> Self {
        Self(major, 0, None)
    }
}

impl From<(u8, u8)> for Version {
    fn from((major, minor): (u8, u8)) -> Self {
        Self(major, minor, None)
    }
}

impl From<(u8, u8, u8)> for Version {
    fn from((major, minor, patch): (u8, u8, u8)) -> Self {
        Self(major, minor, Some(patch))
    }
}

impl TryFrom<f32> for Version {
    type Error = anyhow::Error;

    fn try_from(value: f32) -> Result<Self, Self::Error> {
        if !(0.0..=255.0).contains(&value) {
            return Err(anyhow::anyhow!("Float value out of range for Version"));
        }

        let major = value.trunc() as u8;
        let minor_float = (value - major as f32) * 10.0;
        let minor = minor_float.trunc() as u8;

        Ok(Self(major, minor, None))
    }
}
