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

/// 128-bit vector register type matching the Xbox 360's VMX128 registers.
/// Stored in big-endian byte order (matching guest memory layout).
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(C, align(16))]
pub struct Vec128 {
    pub bytes: [u8; 16],
}

impl Vec128 {
    pub const ZERO: Self = Self { bytes: [0; 16] };

    pub fn from_u32x4(a: u32, b: u32, c: u32, d: u32) -> Self {
        let mut bytes = [0u8; 16];
        bytes[0..4].copy_from_slice(&a.to_be_bytes());
        bytes[4..8].copy_from_slice(&b.to_be_bytes());
        bytes[8..12].copy_from_slice(&c.to_be_bytes());
        bytes[12..16].copy_from_slice(&d.to_be_bytes());
        Self { bytes }
    }

    pub fn from_f32x4(a: f32, b: f32, c: f32, d: f32) -> Self {
        Self::from_u32x4(a.to_bits(), b.to_bits(), c.to_bits(), d.to_bits())
    }

    /// Read the i-th u32 element (big-endian, 0-indexed).
    pub fn u32x4(&self, i: usize) -> u32 {
        let off = i * 4;
        u32::from_be_bytes([
            self.bytes[off],
            self.bytes[off + 1],
            self.bytes[off + 2],
            self.bytes[off + 3],
        ])
    }

    /// Write the i-th u32 element (big-endian, 0-indexed).
    pub fn set_u32x4(&mut self, i: usize, val: u32) {
        let off = i * 4;
        self.bytes[off..off + 4].copy_from_slice(&val.to_be_bytes());
    }

    /// Read the i-th f32 element (big-endian, 0-indexed).
    pub fn f32x4(&self, i: usize) -> f32 {
        f32::from_bits(self.u32x4(i))
    }

    /// Write the i-th f32 element (big-endian, 0-indexed).
    pub fn set_f32x4(&mut self, i: usize, val: f32) {
        self.set_u32x4(i, val.to_bits());
    }

    /// Read the i-th u16 element (big-endian, 0-indexed).
    pub fn u16x8(&self, i: usize) -> u16 {
        let off = i * 2;
        u16::from_be_bytes([self.bytes[off], self.bytes[off + 1]])
    }

    /// Write the i-th u16 element (big-endian, 0-indexed).
    pub fn set_u16x8(&mut self, i: usize, val: u16) {
        let off = i * 2;
        self.bytes[off..off + 2].copy_from_slice(&val.to_be_bytes());
    }

    /// Read the i-th u8 element (0-indexed).
    pub fn u8x16(&self, i: usize) -> u8 {
        self.bytes[i]
    }

    /// Write the i-th u8 element (0-indexed).
    pub fn set_u8x16(&mut self, i: usize, val: u8) {
        self.bytes[i] = val;
    }

    /// Read as two u64 values (big-endian).
    pub fn u64x2(&self, i: usize) -> u64 {
        let off = i * 8;
        u64::from_be_bytes([
            self.bytes[off],
            self.bytes[off + 1],
            self.bytes[off + 2],
            self.bytes[off + 3],
            self.bytes[off + 4],
            self.bytes[off + 5],
            self.bytes[off + 6],
            self.bytes[off + 7],
        ])
    }

    pub fn set_u64x2(&mut self, i: usize, val: u64) {
        let off = i * 8;
        self.bytes[off..off + 8].copy_from_slice(&val.to_be_bytes());
    }

    /// Get all 4 u32 elements as an array.
    pub fn as_u32x4(&self) -> [u32; 4] {
        [self.u32x4(0), self.u32x4(1), self.u32x4(2), self.u32x4(3)]
    }

    /// Get all 4 f32 elements as an array.
    pub fn as_f32x4(&self) -> [f32; 4] {
        [self.f32x4(0), self.f32x4(1), self.f32x4(2), self.f32x4(3)]
    }

    /// Get all 8 u16 elements as an array.
    pub fn as_u16x8(&self) -> [u16; 8] {
        [
            self.u16x8(0), self.u16x8(1), self.u16x8(2), self.u16x8(3),
            self.u16x8(4), self.u16x8(5), self.u16x8(6), self.u16x8(7),
        ]
    }

    /// Get all 16 bytes as an array.
    pub fn as_bytes(&self) -> [u8; 16] {
        self.bytes
    }

    /// Create from a byte array.
    pub fn from_bytes(bytes: [u8; 16]) -> Self {
        Self { bytes }
    }

    /// Create from a u32 array (big-endian elements).
    pub fn from_u32x4_array(arr: [u32; 4]) -> Self {
        Self::from_u32x4(arr[0], arr[1], arr[2], arr[3])
    }

    /// Create from an f32 array (big-endian elements).
    pub fn from_f32x4_array(arr: [f32; 4]) -> Self {
        Self::from_f32x4(arr[0], arr[1], arr[2], arr[3])
    }

    /// Create from a u16 array (big-endian elements).
    pub fn from_u16x8_array(arr: [u16; 8]) -> Self {
        let mut v = Self::ZERO;
        for i in 0..8 { v.set_u16x8(i, arr[i]); }
        v
    }
}

impl Default for Vec128 {
    fn default() -> Self {
        Self::ZERO
    }
}

impl fmt::Debug for Vec128 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Vec128({:08X}_{:08X}_{:08X}_{:08X})",
            self.u32x4(0),
            self.u32x4(1),
            self.u32x4(2),
            self.u32x4(3),
        )
    }
}

impl fmt::Display for Vec128 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}

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

    #[test]
    fn test_u32x4_roundtrip() {
        let v = Vec128::from_u32x4(0xDEADBEEF, 0xCAFEBABE, 0x12345678, 0x9ABCDEF0);
        assert_eq!(v.u32x4(0), 0xDEADBEEF);
        assert_eq!(v.u32x4(1), 0xCAFEBABE);
        assert_eq!(v.u32x4(2), 0x12345678);
        assert_eq!(v.u32x4(3), 0x9ABCDEF0);
    }

    #[test]
    fn test_f32x4_roundtrip() {
        let v = Vec128::from_f32x4(1.0, -2.5, 3.14, 0.0);
        assert_eq!(v.f32x4(0), 1.0);
        assert_eq!(v.f32x4(1), -2.5);
        assert!((v.f32x4(2) - 3.14).abs() < f32::EPSILON);
        assert_eq!(v.f32x4(3), 0.0);
    }

    #[test]
    fn test_u16x8() {
        let v = Vec128::from_u32x4(0x00010002, 0x00030004, 0x00050006, 0x00070008);
        assert_eq!(v.u16x8(0), 0x0001);
        assert_eq!(v.u16x8(1), 0x0002);
        assert_eq!(v.u16x8(6), 0x0007);
        assert_eq!(v.u16x8(7), 0x0008);
    }

    #[test]
    fn test_zero() {
        let v = Vec128::ZERO;
        for i in 0..4 {
            assert_eq!(v.u32x4(i), 0);
        }
    }
}
