// ----- Type Explanation -----
//
// GlobalID
// - GlobalID is an ID type that uniquely identifies entities with an associated ArchiveID.
// - Structure:
//   - ArchiveID [1 byte]
//   - FormID [2 bytes]
//
// - Stores an ID composed of an ArchiveID (0-255) and a FormID (0-65535).
//
// - Supports conversion from and to base 10 and hexadecimal string representations.
// - Includes a `get_formid` function to retrieve the FormID from the GlobalID.

// ----- Structure -----
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct GlobalID {
    data: Vec<u8>, // 3 bytes: [ArchiveID, FormID_low, FormID_high]
}

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

use std::convert::TryFrom;

use super::smn_id_form::FormID;

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

    fn try_from(value: (u8, u16)) -> Result<Self, Self::Error> {
        let (archive_id, form_id) = value;
        let mut data = Vec::with_capacity(3);
        data.push(archive_id);
        data.extend(&form_id.to_le_bytes());
        Ok(Self { data })
    }
}


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

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

        Ok(Self { data: value })
    }
}

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

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        if value.starts_with("0x") || value.starts_with("0X") {
            // Hexadecimal format: "0xAABBBB"
            let hex_str = &value[2..];
            if hex_str.len() != 6 {
                return Err("Invalid hexadecimal GlobalID format. Expected 6 hexadecimal digits after '0x'.");
            }

            let archive_id = u8::from_str_radix(&hex_str[0..2], 16).map_err(|_| "Invalid hexadecimal ArchiveID value")?;
            let form_id = u16::from_str_radix(&hex_str[2..6], 16).map_err(|_| "Invalid hexadecimal FormID value")?;

            let data = vec![archive_id, (form_id & 0xFF) as u8, (form_id >> 8) as u8];
            Ok(Self { data })
        } else {
            if value.len() != 8 {
                return Err("Invalid GlobalID format. Expected 8 characters.");
            }

            let archive_id_str = &value[0..3];
            let form_id_str = &value[3..8];

            let archive_id = archive_id_str.parse::<u8>().map_err(|_| "Invalid base 10 ArchiveID value")?;
            let form_id = form_id_str.parse::<u16>().map_err(|_| "Invalid base 10 FormID value")?;

            let data = vec![archive_id, (form_id & 0xFF) as u8, (form_id >> 8) as u8];
            Ok(Self { data })
        }
    }
}

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

impl GlobalID {
    /// Converts the internal data to a `u32`.
    pub fn to_u32(&self) -> u32 {
        (self.get_archive_id() as u32) * 100_000 + self.get_formid().to_u16() as u32
    }

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

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

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

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

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

    /// Retrieves the `FormID` from the `GlobalID`.
    pub fn get_formid(&self) -> FormID {
        let form_id_bytes = vec![self.data[1], self.data[2]];
        FormID::try_from(form_id_bytes).unwrap()
    }

    /// Converts the internal data to an `ArchiveID` as `u8`.
    pub fn get_archive_id(&self) -> u8 {
        self.data[0]
    }

}

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

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

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