// ----- 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

// ----- Strucutre -----
#[derive(Clone, PartialEq, Eq)]
pub struct Version {
    data: Vec<u8>,
}

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

use std::convert::TryFrom;

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

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        let mut data = Vec::new();
        let major;
        let minor;

        let mut split = value.split('.');
        if let Some(major_str) = split.next() {
            if let Ok(major_val) = major_str.parse::<u8>() {
                major = major_val;
            } else {
                return Err("Invalid major version");
            }
        } else {
            return Err("Major version not found");
        }

        if let Some(minor_str) = split.next() {
            if let Ok(minor_val) = minor_str.parse::<u8>() {
                minor = minor_val;
            } else {
                return Err("Invalid minor version");
            }
        } else {
            return Err("Minor version not found");
        }

        data.push(major);
        data.push(minor);

        Ok(Self { data })
    }
}

// From Vec<u8>
impl TryFrom<Vec<u8>> for Version {
    type Error = &'static str;

    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
        if value.len() != 2 {
            return Err("Invalid version data");
        }

        Ok(Self { data: value })
    }
}

// From f32
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;

        let mut data = Vec::new();
        data.push(major);
        data.push(minor);

        Ok(Self { data })
    }
}

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

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

    pub fn to_vec(&self) -> Vec<u8> {
        self.data.clone()
    }

    pub fn to_float(&self) -> f32 {
        self.data[0] as f32 + self.data[1] as f32 / 10.0
    }
}

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

impl Version {
    pub fn get_major(&self) -> u8 {
        self.data[0]
    }

    pub fn get_minor(&self) -> u8 {
        self.data[1]
    }

    pub fn get_bytecount(&self) -> usize {
        2
    }
}

// ----- Byte Conversion -----

impl Version {
    pub fn to_bytes(&self) -> [u8; 2] {
        self.data.as_slice().try_into().unwrap()
    }

    pub fn from_bytes(bytes: [u8; 2]) -> Self {
        Self { data: bytes.to_vec() }
    }
}

