// ----- Type Explanation -----
// FormID
// - FormID is an ID type that is used to identify something in an efficient way.
// - Form ID [2 bytes],
//
// - Stores an id value with a maximum value of 65535.
//
// - Supports conversion from and to base 10 and hexadecimal string representations.

// ----- Structure -----

#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FormID {
    data: Vec<u8>,
}

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

use std::convert::TryFrom;
use std::convert::TryInto;

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

    fn try_from(value: u16) -> Result<Self, Self::Error> {
        let data = value.to_le_bytes().to_vec();
        Ok(Self { data })
    }
}

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

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

        Ok(Self { data: value })
    }
}

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

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        let id: u16 = if value.starts_with("0x") || value.starts_with("0X") {
            // Attempt to parse as hexadecimal, stripping the "0x" or "0X" prefix
            u16::from_str_radix(&value[2..], 16).map_err(|_| "Invalid hexadecimal FormID value")?
        } else {
            // Default to base 10
            value.parse::<u16>().map_err(|_| "Invalid base 10 FormID value")?
        };

        let data = id.to_le_bytes().to_vec();
        Ok(Self { data })
    }
}

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

impl FormID {
    /// Converts the internal data to a `u16`.
    pub fn to_u16(&self) -> u16 {
        u16::from_le_bytes(self.data.as_slice().try_into().unwrap())
    }

    /// Returns a clone of the internal byte vector.
    pub fn to_vec(&self) -> Vec<u8> {
        self.data.clone()
    }

    /// Converts the `FormID` to a base 10 `String`, padded with leading zeroes to ensure 5 characters.
    pub fn to_string(&self) -> String {
        format!("{:0>5}", self.to_u16())
    }

    /// Converts the `FormID` to a hexadecimal `String`, prefixed with `0x`.
    pub fn to_hex(&self) -> String {
        format!("0x{:04X}", self.to_u16())
    }
}

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

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

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

impl FormID {
    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() }
    }
}

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

use std::fmt;

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

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