use std::fmt;

use crate::*;

// Re-export this common types.
pub use num::rational::Ratio;
pub use num::traits::ToBytes;

/// A four-character code used to identify atoms.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
// TODO serialize as a string
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FourCC([u8; 4]);

impl FourCC {
    // Helper function to create a FourCC from a string literal
    // ex. FourCC::new(b"abcd")
    pub const fn new(value: &[u8; 4]) -> Self {
        FourCC(*value)
    }
}

impl From<u32> for FourCC {
    fn from(value: u32) -> Self {
        FourCC(value.to_be_bytes())
    }
}

impl From<FourCC> for u32 {
    fn from(cc: FourCC) -> Self {
        u32::from_be_bytes(cc.0)
    }
}

impl From<[u8; 4]> for FourCC {
    fn from(value: [u8; 4]) -> Self {
        FourCC(value)
    }
}

impl From<FourCC> for [u8; 4] {
    fn from(cc: FourCC) -> Self {
        cc.0
    }
}

impl From<&[u8; 4]> for FourCC {
    fn from(value: &[u8; 4]) -> Self {
        FourCC(*value)
    }
}

impl fmt::Display for FourCC {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = String::from_utf8_lossy(&self.0);
        write!(f, "{s}")
    }
}

impl fmt::Debug for FourCC {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = String::from_utf8_lossy(&self.0);
        write!(f, "{s}")
    }
}

impl Encode for FourCC {
    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
        self.0.encode(buf)
    }
}

impl Decode for FourCC {
    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
        Ok(FourCC(<[u8; 4]>::decode(buf)?))
    }
}

impl AsRef<[u8; 4]> for FourCC {
    fn as_ref(&self) -> &[u8; 4] {
        &self.0
    }
}

#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct u24([u8; 3]);

impl u24 {
    pub const MAX: u32 = 0x00FF_FFFF;
}

impl From<[u8; 3]> for u24 {
    fn from(value: [u8; 3]) -> Self {
        Self(value)
    }
}

impl Decode for u24 {
    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
        Ok(Self(<[u8; 3]>::decode(buf)?))
    }
}

impl Encode for u24 {
    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
        self.0.encode(buf)
    }
}

impl From<u24> for u32 {
    fn from(value: u24) -> Self {
        u32::from_be_bytes([0, value.0[0], value.0[1], value.0[2]])
    }
}

impl TryFrom<u32> for u24 {
    type Error = std::array::TryFromSliceError;

    fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
        Ok(Self(value.to_be_bytes()[1..].try_into()?))
    }
}

impl ToBytes for u24 {
    type Bytes = [u8; 3];

    fn to_be_bytes(&self) -> Self::Bytes {
        self.0
    }

    fn to_le_bytes(&self) -> Self::Bytes {
        [self.0[2], self.0[1], self.0[0]]
    }
}

#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct u48([u8; 6]);

impl Decode for u48 {
    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
        Ok(Self(<[u8; 6]>::decode(buf)?))
    }
}

impl Encode for u48 {
    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
        self.0.encode(buf)
    }
}

impl TryFrom<u64> for u48 {
    type Error = std::array::TryFromSliceError;

    fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
        Ok(Self(value.to_be_bytes()[2..].try_into()?))
    }
}

impl From<u48> for u64 {
    fn from(value: u48) -> Self {
        u64::from_be_bytes([
            0, 0, value.0[0], value.0[1], value.0[2], value.0[3], value.0[4], value.0[5],
        ])
    }
}

impl From<[u8; 6]> for u48 {
    fn from(value: [u8; 6]) -> Self {
        u48(value)
    }
}

impl AsRef<[u8; 6]> for u48 {
    fn as_ref(&self) -> &[u8; 6] {
        &self.0
    }
}

// The top N bits are the integer part, the bottom N bits are the fractional part.
// Somebody who cares should implement some math stuff.
#[derive(Copy, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FixedPoint<T> {
    int: T,
    dec: T,
}

impl<T: Copy> FixedPoint<T> {
    pub fn new(int: T, dec: T) -> Self {
        Self { int, dec }
    }

    pub fn integer(&self) -> T {
        self.int
    }

    pub fn decimal(&self) -> T {
        self.dec
    }
}

impl<T: Decode> Decode for FixedPoint<T> {
    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
        Ok(Self {
            int: T::decode(buf)?,
            dec: T::decode(buf)?,
        })
    }
}

impl<T: Encode> Encode for FixedPoint<T> {
    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
        self.int.encode(buf)?;
        self.dec.encode(buf)
    }
}

impl<T: num::Zero> From<T> for FixedPoint<T> {
    fn from(value: T) -> Self {
        Self {
            int: value,
            dec: T::zero(),
        }
    }
}

impl<T> fmt::Debug for FixedPoint<T>
where
    T: num::Zero + fmt::Debug + PartialEq + Copy,
    f64: From<T>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.dec.is_zero() {
            write!(f, "{:?}", self.int)
        } else {
            write!(f, "{:?}", f64::from(self.int) / f64::from(self.dec))
        }
    }
}

// 32 bytes max
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Compressor(String);

impl From<&str> for Compressor {
    fn from(value: &str) -> Self {
        Self(value.to_string())
    }
}

impl From<String> for Compressor {
    fn from(value: String) -> Self {
        Self(value)
    }
}

impl From<Compressor> for String {
    fn from(value: Compressor) -> Self {
        value.0
    }
}

impl AsRef<str> for Compressor {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Encode for Compressor {
    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
        let name = self.0.as_bytes();
        let len = name.len();
        if len > 31 {
            return Err(Error::InvalidSize);
        }
        (len as u8).encode(buf)?;
        (&name[..len]).encode(buf)?;

        let zero = [0u8; 31];
        (&zero[..31 - len]).encode(buf)
    }
}

impl Decode for Compressor {
    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
        let compressor_name_bytes = <[u8; 32]>::decode(buf)?;
        match compressor_name_bytes[0] {
            0 => Ok(Self(String::new())),
            1..=31 => {
                let start_bytes = 1;
                let end_bytes = start_bytes + compressor_name_bytes[0] as usize;
                let name = String::from_utf8_lossy(&compressor_name_bytes[start_bytes..end_bytes])
                    .trim_end_matches('\0')
                    .to_string();
                Ok(Self(name))
            }
            _ => {
                // try reading as a string
                let name = String::from_utf8_lossy(&compressor_name_bytes)
                    .trim_end_matches('\0')
                    .to_string();
                Ok(Self(name))
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Zeroed {
    pub size: usize,
}

impl Zeroed {
    pub fn new(size: usize) -> Self {
        Self { size }
    }
}

impl Encode for Zeroed {
    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
        let zero = [0u8; 32];
        let mut size = self.size;

        while size > 0 {
            let len = zero.len().min(size);
            (&zero[..len]).encode(buf)?;
            size -= len;
        }

        Ok(())
    }
}

impl Decode for Zeroed {
    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
        let size = buf.remaining();
        buf.advance(size);
        Ok(Self { size })
    }
}

impl From<usize> for Zeroed {
    fn from(size: usize) -> Self {
        Self { size }
    }
}

#[cfg(test)]
mod tests {
    use crate::{Compressor, Decode as _, Encode as _};

    #[test]
    fn check_compressor_encode_minimal() {
        let compressor = Compressor::from("A");
        let mut buf = Vec::new();
        compressor.encode(&mut buf).unwrap();
        assert_eq!(buf.len(), 32);
        assert_eq!(
            buf,
            vec![
                0x01, b'A', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00,
            ]
        );
        let decode = Compressor::decode(&mut buf.as_ref()).unwrap();
        assert_eq!(decode, compressor);
    }

    #[test]
    fn check_compressor_encode_empty() {
        let compressor = Compressor::from("");
        let mut buf = Vec::new();
        compressor.encode(&mut buf).unwrap();
        assert_eq!(buf.len(), 32);
        assert_eq!(
            buf,
            vec![
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00,
            ]
        );
        let decode = Compressor::decode(&mut buf.as_ref()).unwrap();
        assert_eq!(decode, compressor);
    }

    #[test]
    fn check_compressor_encode_maximal() {
        let compressor = Compressor::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde");
        let mut buf = Vec::new();
        compressor.encode(&mut buf).unwrap();
        assert_eq!(buf.len(), 32);
        assert_eq!(
            buf,
            vec![
                0x1F, b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M',
                b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a',
                b'b', b'c', b'd', b'e'
            ]
        );
        let decode = Compressor::decode(&mut buf.as_ref()).unwrap();
        assert_eq!(decode, compressor);
    }

    #[test]
    fn check_compressor_encode_too_long() {
        let compressor = Compressor::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef");
        let mut buf = Vec::new();
        let result = compressor.encode(&mut buf);
        assert!(result.is_err());
    }
}
