use bytes::BufMut;

use crate::{format::Format, Packable};

impl Packable for u8 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self <= 127 {
            buf.put_u8(self & Format::POSITIVE_FIXINT);
            1
        } else {
            buf.put_slice(&[Format::UINT8, *self]);
            2
        }
    }
}

impl Packable for u16 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self <= 127 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= u8::MAX as u16 {
            buf.put_slice(&[Format::UINT8, *self as u8]);
            2
        } else {
            let mut s = [0u8; 3];
            s[0] = Format::UINT16;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            3
        }
    }
}

impl Packable for u32 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self <= 127 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= u8::MAX as u32 {
            buf.put_slice(&[Format::UINT8, *self as u8]);
            2
        } else if *self <= u16::MAX as u32 {
            let mut s = [0u8; 3];
            s[0] = Format::UINT16;
            s[1..].copy_from_slice(&((*self) as u16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else {
            let mut s = [0u8; 5];
            s[0] = Format::UINT32;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            5
        }
    }
}

impl Packable for u64 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self <= 127 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= u8::MAX as u64 {
            buf.put_slice(&[Format::UINT8, *self as u8]);
            2
        } else if *self <= u16::MAX as u64 {
            let mut s = [0u8; 3];
            s[0] = Format::UINT16;
            s[1..].copy_from_slice(&((*self) as u16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= u32::MAX as u64 {
            let mut s = [0u8; 5];
            s[0] = Format::UINT32;
            s[1..].copy_from_slice(&((*self) as u32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else {
            let mut s = [0u8; 9];
            s[0] = Format::UINT64;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            9
        }
    }
}

impl Packable for u128 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self <= 127 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= u8::MAX as u128 {
            buf.put_slice(&[Format::UINT8, *self as u8]);
            2
        } else if *self <= u16::MAX as u128 {
            let mut s = [0u8; 3];
            s[0] = Format::UINT16;
            s[1..].copy_from_slice(&((*self) as u16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= u32::MAX as u128 {
            let mut s = [0u8; 5];
            s[0] = Format::UINT32;
            s[1..].copy_from_slice(&((*self) as u32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else if *self <= u64::MAX as u128 {
            let mut s = [0u8; 9];
            s[0] = Format::UINT64;
            s[1..].copy_from_slice(&((*self) as u64).to_be_bytes());
            buf.put_slice(&s);
            9
        } else {
            let mut s = [0u8; 18];
            s[0] = Format::BIN8;
            s[1] = 16;
            s[2..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            18
        }
    }
}

impl Packable for usize {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self <= 127 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= u8::MAX as usize {
            buf.put_slice(&[Format::UINT8, *self as u8]);
            2
        } else if *self <= u16::MAX as usize {
            let mut s = [0u8; 3];
            s[0] = Format::UINT16;
            s[1..].copy_from_slice(&((*self) as u16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= u32::MAX as usize {
            let mut s = [0u8; 5];
            s[0] = Format::UINT32;
            s[1..].copy_from_slice(&((*self) as u32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else {
            let mut s = [0u8; 9];
            s[0] = Format::UINT64;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            9
        }
    }
}

impl Packable for i8 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self <= -33 {
            buf.put_slice(&[Format::INT8, *self as u8]);
            2
        } else if *self <= -1 {
            buf.put_u8((*self | -32i8) as u8);
            1
        } else {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        }
    }
}

impl Packable for i16 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self < i8::MIN as i16 {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= -33 {
            buf.put_slice(&[Format::INT8, *self as u8]);
            2
        } else if *self <= -1 {
            buf.put_u8((*self | -32i16) as u8);
            1
        } else if *self <= i8::MAX as i16 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            3
        }
    }
}

impl Packable for i32 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self < i16::MIN as i32 {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            5
        } else if *self < i8::MIN as i32 {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= -33 {
            buf.put_slice(&[Format::INT8, *self as u8]);
            2
        } else if *self <= -1 {
            buf.put_u8((*self | -32i32) as u8);
            1
        } else if *self <= i8::MAX as i32 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= i16::MAX as i32 {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            5
        }
    }
}

impl Packable for i64 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self < i32::MIN as i64 {
            let mut s = [0u8; 9];
            s[0] = Format::INT64;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            9
        } else if *self < i16::MIN as i64 {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&((*self) as i32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else if *self < i8::MIN as i64 {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= -33 {
            buf.put_slice(&[Format::INT8, *self as u8]);
            2
        } else if *self <= -1 {
            buf.put_u8((*self | -32i64) as u8);
            1
        } else if *self <= i8::MAX as i64 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= i16::MAX as i64 {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= i32::MAX as i64 {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&((*self) as i32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else {
            let mut s = [0u8; 9];
            s[0] = Format::INT64;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            9
        }
    }
}

impl Packable for i128 {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self < i64::MIN as i128 {
            let mut s = [0u8; 18];
            s[0] = Format::BIN8;
            s[1] = 16;
            s[2..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            18
        } else if *self < i32::MIN as i128 {
            let mut s = [0u8; 9];
            s[0] = Format::INT64;
            s[1..].copy_from_slice(&((*self) as i64).to_be_bytes());
            buf.put_slice(&s);
            9
        } else if *self < i16::MIN as i128 {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&((*self) as i32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else if *self < i8::MIN as i128 {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= -33 {
            buf.put_slice(&[Format::INT8, *self as u8]);
            2
        } else if *self <= -1 {
            buf.put_u8((*self | -32i128) as u8);
            1
        } else if *self <= i8::MAX as i128 {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= i16::MAX as i128 {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= i32::MAX as i128 {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&((*self) as i32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else if *self <= i64::MAX as i128 {
            let mut s = [0u8; 9];
            s[0] = Format::INT64;
            s[1..].copy_from_slice(&((*self) as i64).to_be_bytes());
            buf.put_slice(&s);
            9
        } else {
            let mut s = [0u8; 18];
            s[0] = Format::BIN8;
            s[1] = 16;
            s[2..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            18
        }
    }
}

impl Packable for isize {
    fn pack<T>(&self, buf: &mut T) -> usize
    where
        T: BufMut,
    {
        if *self < i32::MIN as isize {
            let mut s = [0u8; 9];
            s[0] = Format::INT64;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            9
        } else if *self < i16::MIN as isize {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&((*self) as i32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else if *self < i8::MIN as isize {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= -33 {
            buf.put_slice(&[Format::INT8, *self as u8]);
            2
        } else if *self <= -1 {
            buf.put_u8((*self | -32isize) as u8);
            1
        } else if *self <= i8::MAX as isize {
            buf.put_u8(*self as u8 & Format::POSITIVE_FIXINT);
            1
        } else if *self <= i16::MAX as isize {
            let mut s = [0u8; 3];
            s[0] = Format::INT16;
            s[1..].copy_from_slice(&((*self) as i16).to_be_bytes());
            buf.put_slice(&s);
            3
        } else if *self <= i32::MAX as isize {
            let mut s = [0u8; 5];
            s[0] = Format::INT32;
            s[1..].copy_from_slice(&((*self) as i32).to_be_bytes());
            buf.put_slice(&s);
            5
        } else {
            let mut s = [0u8; 9];
            s[0] = Format::INT64;
            s[1..].copy_from_slice(&self.to_be_bytes());
            buf.put_slice(&s);
            9
        }
    }
}
