//! The implementation of serializable for LEB128 types.

use std::hash::Hash;
use std::io::Write;

use crate::{Error, ReadSeek, Result, Serializable};

/// Signed LEB128, variable-length
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Sleb128(pub i32);

/// Unsigned LEB128, variable-length
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Uleb128(pub u32);

/// Unsigned LEB128 plus 1, variable-length
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Uleb128p1(pub u32);

impl Sleb128 {
    fn get_serialized_bytes(&self) -> Vec<u8> {
        let Self(mut val) = self;
        let mut bytes = vec![];
        loop {
            let byte = val as u8 & 0b0111_1111;
            val >>= 7;
            if (val == 0 && (byte & 0b0100_0000) == 0) || (val == -1 && (byte & 0b0100_0000) != 0) {
                bytes.push(byte);
                return bytes;
            }
            bytes.push(byte | 0b1000_0000);
        }
    }
}

impl Serializable for Sleb128 {
    fn serialize(&self, output: &mut dyn Write) -> Result<()> {
        output
            .write_all(&self.get_serialized_bytes())
            .map_err(|err| {
                Error::SerializationError(format!(
                    "Failed to write serialize Sleb128 to output: {err}"
                ))
            })?;
        Ok(())
    }

    fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
        let mut shift = 0;
        let mut uvalue: u64 = 0;
        let mut buffer = [0u8; 1];
        for _ in 0..5 {
            input.read_exact(&mut buffer).map_err(|_| {
                Error::InputTooSmall("Failed to read all bytes for Sleb128 from the input".into())
            })?;
            let byte = buffer[0];
            let is_last_byte = byte & 0b1000_0000 == 0;

            if is_last_byte {
                uvalue += ((byte & 0b0011_1111) as u64) << shift;
                let sign = ((byte & 0b0100_0000) as u64) << shift;
                let value = uvalue as i64 - sign as i64;
                let value: i32 = value.try_into().map_err(|err| Error::DeserializationError(
                        format!("Failed to parse the Sleb128 value, the encoded value is supposed to fit in 32 bits: {err}")
                ))?;
                return Ok(Self(value));
            } else {
                uvalue |= ((byte & 0b0111_1111) as u64) << shift;
                shift += 7;
            }
        }
        Err(Error::DeserializationError(
            "Malfored LEB128 encoding: the value cannot be longer than 5 bytes".into(),
        ))
    }

    fn size(&self) -> usize {
        self.get_serialized_bytes().len()
    }
}

impl Uleb128 {
    fn get_serialized_bytes(&self) -> Vec<u8> {
        let Self(mut val) = self;
        let mut bytes = vec![];
        loop {
            let byte = val as u8 & 0b0111_1111;
            val /= 128;
            if val == 0 {
                bytes.push(byte);
                return bytes;
            }
            bytes.push(byte | 0b1000_0000);
        }
    }
}

impl Serializable for Uleb128 {
    fn serialize(&self, output: &mut dyn Write) -> Result<()> {
        output
            .write_all(&self.get_serialized_bytes())
            .map_err(|err| {
                Error::SerializationError(format!(
                    "Failed to write serialize Uleb128 to output: {err}"
                ))
            })?;
        Ok(())
    }

    fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
        let mut shift = 0;
        let mut value: u32 = 0;
        let mut buffer = [0u8; 1];
        for _ in 0..5 {
            input.read_exact(&mut buffer).map_err(|_| {
                Error::InputTooSmall("Failed to read all bytes for Uleb128 from the input".into())
            })?;
            let byte = buffer[0];
            let is_last_byte = byte & 0b1000_0000 == 0;

            value += ((byte & 0b0111_1111) as u32) << shift;
            if is_last_byte {
                return Ok(Self(value));
            }
            shift += 7;
        }
        Err(Error::DeserializationError(
            "Malfored LEB128 encoding: the value cannot be longer than 5 bytes".into(),
        ))
    }

    fn size(&self) -> usize {
        self.get_serialized_bytes().len()
    }
}

impl Uleb128p1 {
    pub const MINUS_ONE: Uleb128p1 = Uleb128p1(0xFFFFFFFF);
}

impl Serializable for Uleb128p1 {
    fn serialize(&self, output: &mut dyn Write) -> Result<()> {
        let Self(val) = self;
        Uleb128(val.overflowing_add(1).0).serialize(output)
    }

    fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
        let Uleb128(val) = Uleb128::deserialize(input)?;
        Ok(Self(val.overflowing_sub(1).0))
    }

    fn size(&self) -> usize {
        let Self(val) = self;
        Uleb128(val.overflowing_add(1).0).size()
    }
}

impl From<i32> for Sleb128 {
    fn from(item: i32) -> Self {
        Self(item)
    }
}

impl From<u32> for Uleb128 {
    fn from(item: u32) -> Self {
        Self(item)
    }
}

impl From<u32> for Uleb128p1 {
    fn from(item: u32) -> Self {
        Self(item)
    }
}

impl From<Sleb128> for i32 {
    fn from(item: Sleb128) -> Self {
        item.0
    }
}

impl From<Uleb128> for u32 {
    fn from(item: Uleb128) -> Self {
        item.0
    }
}

impl From<Uleb128p1> for u32 {
    fn from(item: Uleb128p1) -> Self {
        item.0
    }
}

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

    #[test]
    fn serialize_sleb128() {
        assert_eq!(Sleb128(0).serialize_to_vec().unwrap(), vec![0x00u8]);
        assert_eq!(Sleb128(1).serialize_to_vec().unwrap(), vec![0x01u8]);
        assert_eq!(Sleb128(-1).serialize_to_vec().unwrap(), vec![0x7Fu8]);
        assert_eq!(Sleb128(-1551).serialize_to_vec().unwrap(), vec![0xf1, 0x73]);
        assert_eq!(
            Sleb128(-128).serialize_to_vec().unwrap(),
            vec![0x80u8, 0x7F]
        );
    }

    #[test]
    fn deserialize_sleb128() {
        assert_eq!(
            Sleb128::deserialize_from_slice(&[0x00u8]).unwrap(),
            Sleb128(0)
        );
        assert_eq!(
            Sleb128::deserialize_from_slice(&[0x01u8]).unwrap(),
            Sleb128(1)
        );
        assert_eq!(
            Sleb128::deserialize_from_slice(&[0x7Fu8]).unwrap(),
            Sleb128(-1)
        );
        assert_eq!(
            Sleb128::deserialize_from_slice(&[0xf1, 0x73]).unwrap(),
            Sleb128(-1551)
        );
        assert_eq!(
            Sleb128::deserialize_from_slice(&[0x80u8, 0x7Fu8]).unwrap(),
            Sleb128(-128)
        );
    }

    #[test]
    fn size_sleb128() {
        assert_eq!(Sleb128(0).size(), 1);
        assert_eq!(Sleb128(1).size(), 1);
        assert_eq!(Sleb128(-1).size(), 1);
        assert_eq!(Sleb128(-128).size(), 2);
    }

    #[test]
    fn serialize_uleb128() {
        assert_eq!(Uleb128(0).serialize_to_vec().unwrap(), vec![0x00u8]);
        assert_eq!(Uleb128(1).serialize_to_vec().unwrap(), vec![0x01u8]);
        assert_eq!(Uleb128(127).serialize_to_vec().unwrap(), vec![0x7Fu8]);
        assert_eq!(
            Uleb128(16256).serialize_to_vec().unwrap(),
            vec![0x80u8, 0x7F]
        );
    }

    #[test]
    fn deserialize_uleb128() {
        assert_eq!(
            Uleb128::deserialize_from_slice(&[0x00u8]).unwrap(),
            Uleb128(0)
        );
        assert_eq!(
            Uleb128::deserialize_from_slice(&[0x01u8]).unwrap(),
            Uleb128(1)
        );
        assert_eq!(
            Uleb128::deserialize_from_slice(&[0x7Fu8]).unwrap(),
            Uleb128(127)
        );
        assert_eq!(
            Uleb128::deserialize_from_slice(&[0x80u8, 0x7Fu8]).unwrap(),
            Uleb128(16256)
        );
    }

    #[test]
    fn size_uleb128() {
        assert_eq!(Uleb128(0).size(), 1);
        assert_eq!(Uleb128(1).size(), 1);
        assert_eq!(Uleb128(127).size(), 1);
        assert_eq!(Uleb128(16256).size(), 2);
    }

    #[test]
    fn serialize_uleb128p1() {
        assert_eq!(
            Uleb128p1::MINUS_ONE.serialize_to_vec().unwrap(),
            vec![0x00u8]
        );
        assert_eq!(Uleb128p1(0).serialize_to_vec().unwrap(), vec![0x01u8]);
        assert_eq!(Uleb128p1(126).serialize_to_vec().unwrap(), vec![0x7Fu8]);
        assert_eq!(
            Uleb128p1(16255).serialize_to_vec().unwrap(),
            vec![0x80u8, 0x7F]
        );
    }

    #[test]
    fn deserialize_uleb128p1() {
        assert_eq!(
            Uleb128p1::deserialize_from_slice(&[0x00u8]).unwrap(),
            Uleb128p1::MINUS_ONE
        );
        assert_eq!(
            Uleb128p1::deserialize_from_slice(&[0x01u8]).unwrap(),
            Uleb128p1(0)
        );
        assert_eq!(
            Uleb128p1::deserialize_from_slice(&[0x7Fu8]).unwrap(),
            Uleb128p1(126)
        );
        assert_eq!(
            Uleb128p1::deserialize_from_slice(&[0x80u8, 0x7Fu8]).unwrap(),
            Uleb128p1(16255)
        );
    }

    #[test]
    fn size_uleb128p1() {
        assert_eq!(Uleb128p1::MINUS_ONE.size(), 1);
        assert_eq!(Uleb128p1(0).size(), 1);
        assert_eq!(Uleb128p1(126).size(), 1);
        assert_eq!(Uleb128p1(16255).size(), 2);
    }
}
