use std::fmt;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
#[repr(u8)]
#[non_exhaustive]
pub enum Register {
    // general purpose
    Rg0 = 0,
    Rg1 = 1,
    Rg2 = 2,
    Rg3 = 3,
    Rg4 = 4,
    Rg5 = 5,
    Rg6 = 6,
    Rg7 = 7,
    Rg8 = 8,
    Rg9 = 9,
    Rga = 10,
    Rgb = 11,
    Rgc = 12,
    Rgd = 13,
    Rge = 14,
    Rgf = 15,

    // special purpose
    Acc = 16,
    Spr = 17,
    Bpr = 18,
    Ret = 19,
    Idr = 20,
    Mmr = 21,

    // system - read only
    Zero = 22,
    Sts = 23,
    Cir = 24,
    Pcx = 25,

    #[default]
    Null = 26,
}

impl Register {
    /// Total number of registers, not counting Null as it's not a real register.
    /// reading or writing to Null is undefined behaviour.
    pub const COUNT: u8 = Self::Null as u8;

    /// # Safety
    /// the caller must ensure that the index is always within the range 0..COUNT, failing to do so will result in undefined behaviour.
    #[must_use]
    #[inline]
    pub unsafe fn from_u8_unchecked(idx: u8) -> Self {
        debug_assert!(
            idx < Self::COUNT,
            "Tried to access a null register using the unchecked method. undefined behaviour alert!"
        );
        unsafe { std::mem::transmute(idx) }
    }

    #[inline]
    pub fn from_u8(idx: u8) -> Result<Self, ()> {
        if idx >= Self::COUNT {
            return Err(());
        }

        Ok(unsafe { Self::from_u8_unchecked(idx) })
    }
}

impl fmt::Display for Register {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                // general purpose
                Self::Rg0 => "Rg0",
                Self::Rg1 => "Rg1",
                Self::Rg2 => "Rg2",
                Self::Rg3 => "Rg3",
                Self::Rg4 => "Rg4",
                Self::Rg5 => "Rg5",
                Self::Rg6 => "Rg6",
                Self::Rg7 => "Rg7",
                Self::Rg8 => "Rg8",
                Self::Rg9 => "Rg9",
                Self::Rga => "Rga",
                Self::Rgb => "Rgb",
                Self::Rgc => "Rgc",
                Self::Rgd => "Rgd",
                Self::Rge => "Rge",
                Self::Rgf => "Rgf",

                // special purpose
                Self::Acc => "Acc",
                Self::Spr => "Spr",
                Self::Bpr => "Bpr",
                Self::Ret => "Ret",
                Self::Idr => "Idr",
                Self::Mmr => "Mmr",

                // system - read only
                Self::Zero => "Zero",
                Self::Sts => "Sts",
                Self::Cir => "Cir",
                Self::Pcx => "Pcx",

                Self::Null => "Null",
            }
        )
    }
}

impl std::str::FromStr for Register {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_ascii_lowercase().as_str() {
            // general purpose
            "rg0" => Ok(Self::Rg0),
            "rg1" => Ok(Self::Rg1),
            "rg2" => Ok(Self::Rg2),
            "rg3" => Ok(Self::Rg3),
            "rg4" => Ok(Self::Rg4),
            "rg5" => Ok(Self::Rg5),
            "rg6" => Ok(Self::Rg6),
            "rg7" => Ok(Self::Rg7),
            "rg8" => Ok(Self::Rg8),
            "rg9" => Ok(Self::Rg9),
            "rga" => Ok(Self::Rga),
            "rgb" => Ok(Self::Rgb),
            "rgc" => Ok(Self::Rgc),
            "rgd" => Ok(Self::Rgd),
            "rge" => Ok(Self::Rge),
            "rgf" => Ok(Self::Rgf),

            // special purpose
            "acc" => Ok(Self::Acc),
            "spr" => Ok(Self::Spr),
            "bpr" => Ok(Self::Bpr),
            // "ret" => Ok(Self::Ret),
            "idr" => Ok(Self::Idr),
            "mmr" => Ok(Self::Mmr),

            // system - read only
            "zero" => Ok(Self::Zero),
            "sts" => Ok(Self::Sts),
            "cir" => Ok(Self::Cir),
            "pcx" => Ok(Self::Pcx),

            "null" => Ok(Self::Null),
            _ => Err(()),
        }
    }
}
