use serde::{Deserialize, Serialize};
use std::fmt;
use std::marker::PhantomData;

/// Big-endian value wrapper matching `xe::be<T>` from the C++ codebase.
/// Stores the value in big-endian byte order and transparently converts
/// on access. Used for guest memory structures that are natively big-endian.
#[derive(Clone, Copy, Serialize, Deserialize)]
#[repr(transparent)]
pub struct Be<T: BeSwap>(T::Bytes, PhantomData<T>);

impl<T: BeSwap> Be<T> {
    pub fn new(val: T) -> Self {
        Self(val.to_be_bytes(), PhantomData)
    }

    pub fn get(self) -> T {
        T::from_be_bytes(self.0)
    }

    pub fn set(&mut self, val: T) {
        self.0 = val.to_be_bytes();
    }

    pub fn raw_bytes(&self) -> &T::Bytes {
        &self.0
    }
}

impl<T: BeSwap + Default> Default for Be<T> {
    fn default() -> Self {
        Self::new(T::default())
    }
}

impl<T: BeSwap + fmt::Debug> fmt::Debug for Be<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get().fmt(f)
    }
}

impl<T: BeSwap + fmt::Display> fmt::Display for Be<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get().fmt(f)
    }
}

impl<T: BeSwap + PartialEq> PartialEq for Be<T> {
    fn eq(&self, other: &Self) -> bool {
        // Compare raw bytes for efficiency (same byte order)
        self.0.as_ref() == other.0.as_ref()
    }
}

impl<T: BeSwap + Eq> Eq for Be<T> {}

/// Trait for types that can be converted to/from big-endian byte representations.
pub trait BeSwap: Copy {
    type Bytes: Copy + AsRef<[u8]> + serde::Serialize + for<'de> serde::Deserialize<'de>;
    fn to_be_bytes(self) -> Self::Bytes;
    fn from_be_bytes(bytes: Self::Bytes) -> Self;
}

macro_rules! impl_be_swap {
    ($t:ty) => {
        impl BeSwap for $t {
            type Bytes = [u8; std::mem::size_of::<$t>()];
            fn to_be_bytes(self) -> Self::Bytes {
                <$t>::to_be_bytes(self)
            }
            fn from_be_bytes(bytes: Self::Bytes) -> Self {
                <$t>::from_be_bytes(bytes)
            }
        }
    };
}

impl_be_swap!(u8);
impl_be_swap!(u16);
impl_be_swap!(u32);
impl_be_swap!(u64);
impl_be_swap!(i8);
impl_be_swap!(i16);
impl_be_swap!(i32);
impl_be_swap!(i64);
impl_be_swap!(f32);
impl_be_swap!(f64);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_be_u32() {
        let v = Be::<u32>::new(0x12345678);
        assert_eq!(v.get(), 0x12345678);
        assert_eq!(v.raw_bytes(), &[0x12, 0x34, 0x56, 0x78]);
    }

    #[test]
    fn test_be_u16() {
        let v = Be::<u16>::new(0xABCD);
        assert_eq!(v.get(), 0xABCD);
        assert_eq!(v.raw_bytes(), &[0xAB, 0xCD]);
    }

    #[test]
    fn test_be_mutate() {
        let mut v = Be::<u32>::new(1);
        assert_eq!(v.get(), 1);
        v.set(42);
        assert_eq!(v.get(), 42);
    }

    #[test]
    fn test_be_f32() {
        let v = Be::<f32>::new(1.0);
        assert_eq!(v.get(), 1.0);
        // IEEE 754: 1.0f = 0x3F800000 => bytes [0x3F, 0x80, 0x00, 0x00]
        assert_eq!(v.raw_bytes(), &[0x3F, 0x80, 0x00, 0x00]);
    }
}
