use core::ops::{BitAnd, BitXor, Neg, Shl, Shr};
use musli::Context;
use crate::int::ByteOrder;
use crate::reader::Reader;
use crate::writer::Writer;
pub trait Unsigned:
    Sized
    + Copy
    + Shr<u32, Output = Self>
    + Shl<u32, Output = Self>
    + BitXor<Self, Output = Self>
    + BitAnd<Self, Output = Self>
    + PartialOrd<Self>
    + Ord
{
    const ONE: Self;
    const BYTES: u8;
    const BITS: u32;
    type Signed: Signed;
    fn signed(self) -> Self::Signed;
    fn from_byte(byte: u8) -> Self;
    fn as_byte(self) -> u8;
    fn is_smaller_than(self, byte: u8) -> bool;
    fn is_zero(self) -> bool;
    fn checked_shr(self, value: u32) -> Option<Self>;
    fn checked_shl(self, value: u32) -> Option<Self>;
    fn wrapping_shl(self, value: u32) -> Self;
    fn checked_add(self, value: Self) -> Option<Self>;
    fn wrapping_add(self, value: Self) -> Self;
}
pub trait ByteOrderIo: Unsigned {
    fn write_bytes_unsigned<C, W, B>(self, cx: &mut C, writer: W) -> Result<(), C::Error>
    where
        C: Context<Input = W::Error>,
        W: Writer,
        B: ByteOrder;
    fn read_bytes_unsigned<'de, C, R, B>(cx: &mut C, reader: R) -> Result<Self, C::Error>
    where
        C: Context<Input = R::Error>,
        R: Reader<'de>,
        B: ByteOrder;
}
pub trait Signed:
    Sized
    + Copy
    + Neg<Output = Self>
    + Shr<u32, Output = Self>
    + Shl<u32, Output = Self>
    + BitXor<Self, Output = Self>
{
    const BITS: u32;
    type Unsigned: Unsigned;
    fn unsigned(self) -> Self::Unsigned;
}
macro_rules! implement {
    ($signed:ty, $unsigned:ty) => {
        impl Signed for $signed {
            const BITS: u32 = <$signed>::BITS;
            type Unsigned = $unsigned;
            fn unsigned(self) -> Self::Unsigned {
                self as $unsigned
            }
        }
        impl Unsigned for $unsigned {
            const ONE: Self = 1;
            const BYTES: u8 = (<$unsigned>::BITS / 8) as u8;
            const BITS: u32 = <$signed>::BITS;
            type Signed = $signed;
            #[inline]
            fn signed(self) -> Self::Signed {
                self as $signed
            }
            #[inline]
            fn from_byte(byte: u8) -> Self {
                byte as $unsigned
            }
            #[inline]
            fn as_byte(self) -> u8 {
                self as u8
            }
            #[inline]
            fn is_smaller_than(self, b: u8) -> bool {
                self < b as $unsigned
            }
            #[inline]
            fn is_zero(self) -> bool {
                self == 0
            }
            #[inline]
            fn checked_shr(self, value: u32) -> Option<Self> {
                self.checked_shr(value)
            }
            #[inline]
            fn checked_shl(self, value: u32) -> Option<Self> {
                self.checked_shl(value)
            }
            #[inline]
            fn wrapping_shl(self, value: u32) -> Self {
                self.wrapping_shl(value)
            }
            #[inline]
            fn checked_add(self, value: Self) -> Option<Self> {
                self.checked_add(value)
            }
            #[inline]
            fn wrapping_add(self, value: Self) -> Self {
                self.wrapping_add(value)
            }
        }
    };
}
macro_rules! implement_io {
    ($signed:ty, $unsigned:ty, $read:ident, $write:ident) => {
        implement!($signed, $unsigned);
        impl ByteOrderIo for $unsigned {
            #[inline]
            fn write_bytes_unsigned<C, W, B>(
                self,
                cx: &mut C,
                mut writer: W,
            ) -> Result<(), C::Error>
            where
                C: Context<Input = W::Error>,
                W: Writer,
                B: ByteOrder,
            {
                writer.write_array(cx, B::$write(self))
            }
            #[inline]
            fn read_bytes_unsigned<'de, C, R, B>(
                cx: &mut C,
                mut reader: R,
            ) -> Result<Self, C::Error>
            where
                C: Context<Input = R::Error>,
                R: Reader<'de>,
                B: ByteOrder,
            {
                Ok(B::$read(reader.read_array(cx)?))
            }
        }
    };
}
implement_io!(i8, u8, read_u8, write_u8);
implement_io!(i16, u16, read_u16, write_u16);
implement_io!(i32, u32, read_u32, write_u32);
implement_io!(i64, u64, read_u64, write_u64);
implement_io!(i128, u128, read_u128, write_u128);
implement!(isize, usize);