// ----- Type Explanation -----
// Version
// - Version is a versioning type that is used to identify a version.
// - Major value [1 byte], Minor value [1 byte],
// 
// - Stores a version with a major and minor value.
// "1.4" -> Major: 1, Minor: 4

// ----- Structure -----
#[derive(Clone, PartialEq, Eq, Copy)]
pub struct Version {
    data: [u8; 2],
}

// ----- Conversion: From -----

use std::convert::TryFrom;

/// Implements conversions from various types to `Version`.
impl TryFrom<&str> for Version {
    type Error = &'static str;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        let mut split = value.split('.');
        let major = split
            .next()
            .ok_or("Major version not found")?
            .parse::<u8>()
            .map_err(|_| "Invalid major version")?;
        let minor = split
            .next()
            .ok_or("Minor version not found")?
            .parse::<u8>()
            .map_err(|_| "Invalid minor version")?;

        Ok(Self {
            data: [major, minor],
        })
    }
}

impl TryFrom<[u8; 2]> for Version {
    type Error = &'static str;

    fn try_from(value: [u8; 2]) -> Result<Self, Self::Error> {
        Ok(Self { data: value })
    }
}

impl TryFrom<f32> for Version {
    type Error = &'static str;

    fn try_from(value: f32) -> Result<Self, Self::Error> {
        let major = value as u8;
        let minor = ((value - major as f32) * 10.0) as u8;

        Ok(Self {
            data: [major, minor],
        })
    }
}

// ----- Conversion: To -----

impl Version {
    /// Converts the `Version` to a `String`.
    pub fn to_string(&self) -> String {
        format!("{}.{}", self.data[0], self.data[1])
    }

    /// Converts the `Version` to a `float`.
    pub fn to_float(&self) -> f32 {
        self.data[0] as f32 + self.data[1] as f32 / 10.0
    }


    // ----- Getters -----

    
    /// Returns the major version number.
    pub fn get_major(&self) -> u8 {
        self.data[0]
    }

    /// Returns the minor version number.
    pub fn get_minor(&self) -> u8 {
        self.data[1]
    }

    /// Returns the number of bytes in the `Version`.
    pub fn get_bytecount(&self) -> usize {
        self.data.len()
    }

}

// ----- Byte Conversion -----
impl Version {
    /// Creates a `Version` from a byte array.
    pub fn from_bytes(bytes: [u8; 2]) -> Self {
        Self { data: bytes }
    }

    /// Returns the internal byte array.
    pub fn to_bytes(&self) -> [u8; 2] {
        self.data
    }
}

// ----- Display / Debug -----

use std::fmt;

impl fmt::Display for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl fmt::Debug for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Version({})", self.to_string())
    }
}
