// ----- 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, Copy)]
pub struct GlobalID {
    data: [u8; 3], // 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 form_id_bytes = form_id.to_le_bytes();
        Ok(Self {
            data: [archive_id, form_id_bytes[0], form_id_bytes[1]],
        })
    }
}

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

    fn try_from(value: [u8; 3]) -> Result<Self, Self::Error> {
        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")?;

            Ok(Self {
                data: [archive_id, form_id.to_le_bytes()[0], form_id.to_le_bytes()[1]],
            })
        } else {
            // Base 10 format: "AAAFFFFF"
            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")?;

            Ok(Self {
                data: [archive_id, form_id.to_le_bytes()[0], form_id.to_le_bytes()[1]],
            })
        }
    }
}

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

impl GlobalID {
    /// Converts the internal data to a `u32`.
    pub fn to_u32(&self) -> u32 {
        let archive_id = self.data[0] as u32;
        let form_id = u16::from_le_bytes([self.data[1], self.data[2]]) as u32;
        archive_id * 100_000 + form_id
    }

    /// 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 = u16::from_le_bytes([self.data[1], self.data[2]]);
        FormID::try_from(form_id).unwrap()
    }

    /// Retrieves the `ArchiveID` from the `GlobalID`.
    pub fn get_archive_id(&self) -> u8 {
        self.data[0]
    }

}

// ----- Byte Conversion -----
impl GlobalID {
    

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

    /// Creates a `GlobalID` from a byte array.
    pub fn from_bytes(bytes: [u8; 3]) -> Self {
        Self { data: bytes }
    }
}


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

use std::fmt;

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

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