//! Traits which provides the serializations
//! into the byte sequences used by the Bitmessage network,
//! and traits which provides the deserializations
//! into the data types used by the Bitmessage protocol.

use std::{
    fmt,
    io::{self, Cursor, Read, Write},
    mem::size_of,
};

pub use crate::error::TooLongError;

/// Provides a method that writes this object as a Bitmessage entity to a writer.
pub trait WriteTo {
    /// Writes this object as a Bitmessage entity to a writer.
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()>;
}

/// Provides a method that reads this object as a Bitmessage entity from a reader.
pub trait ReadFrom {
    /// Reads this object as a Bitmessage entity from a reader.
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized;
}

impl WriteTo for u8 {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        let bytes = [*self];
        w.write_all(&bytes)
    }
}

impl ReadFrom for u8 {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        let mut buf = [0; 1];
        r.read_exact(&mut buf)?;
        Ok(buf[0])
    }
}

impl WriteTo for u16 {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        w.write_all(&self.to_be_bytes())
    }
}

impl ReadFrom for u16 {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        let mut buf = [0; 2];
        r.read_exact(&mut buf)?;
        Ok(Self::from_be_bytes(buf))
    }
}

impl WriteTo for u32 {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        w.write_all(&self.to_be_bytes())
    }
}

impl ReadFrom for u32 {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        let mut buf = [0; 4];
        r.read_exact(&mut buf)?;
        Ok(Self::from_be_bytes(buf))
    }
}

impl WriteTo for u64 {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        w.write_all(&self.to_be_bytes())
    }
}

impl ReadFrom for u64 {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        let mut buf = [0; 8];
        r.read_exact(&mut buf)?;
        Ok(Self::from_be_bytes(buf))
    }
}

impl WriteTo for [u8] {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        w.write_all(self)
    }
}

macro_rules! io_array {
    ($len:expr) => {
        impl ReadFrom for [u8; $len] {
            fn read_from(r: &mut dyn Read) -> io::Result<Self>
            where
                Self: Sized,
            {
                let mut buf = [0; $len];
                r.read_exact(&mut buf)?;
                Ok(buf)
            }
        }
    };
}

io_array!(4);
io_array!(6);
io_array!(10);
io_array!(12);
io_array!(16);
io_array!(18);
io_array!(32);

impl WriteTo for Vec<u8> {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        w.write_all(self)
    }
}

/// This error indicates
/// that some trailing bytes remained after reading had finished.
///
/// The length of the trailing bytes is retrievable
/// from the error object.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct TrailingBytesError(usize);

impl fmt::Display for TrailingBytesError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "found trailing {} bytes", self.0)
    }
}

impl std::error::Error for TrailingBytesError {}

impl TrailingBytesError {
    /// Constructs an error object from a length value.
    pub fn new(trailing_len: usize) -> Self {
        Self(trailing_len)
    }

    /// Returns the length of the trailing bytes.
    pub fn trailing_len(&self) -> usize {
        self.0
    }
}

/// Provides a method that reads this object as a Bitmessage entity from a reader
/// with a specified byte length.
pub trait SizedReadFrom {
    /// Reads this object as a Bitmessage entity from a reader
    /// with a specified byte length.
    fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
    where
        Self: Sized;
}

impl SizedReadFrom for Vec<u8> {
    fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        let mut r = r.take(len as u64);
        let mut bytes = Vec::with_capacity(len);
        r.read_to_end(&mut bytes)?;
        Ok(bytes)
    }
}

/// Provides a method that reads this object as a Bitmessage entity from a reader
/// with a limited item count.
pub trait LimitedReadFrom {
    /// Reads this object as a Bitmessage entity from a reader
    /// with a limited item count.
    fn limited_read_from(r: &mut dyn Read, max_len: usize) -> io::Result<Self>
    where
        Self: Sized;
}

/// Provides a method that reads this object as a Bitmessage entity
/// from a byte array with an exact length.
pub trait ReadFromExact {
    /// Reads this object as a Bitmessage entity
    /// from a byte array with an exact length.
    fn read_from_exact(bytes: impl AsRef<[u8]>) -> io::Result<Self>
    where
        Self: Sized;
}

impl<T> ReadFromExact for T
where
    T: ReadFrom,
{
    fn read_from_exact(bytes: impl AsRef<[u8]>) -> io::Result<Self>
    where
        Self: Sized,
    {
        let bytes = bytes.as_ref();
        let mut cur = Cursor::new(bytes);
        let v = Self::read_from(&mut cur)?;
        if cur.position() != bytes.len() as u64 {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                TrailingBytesError(bytes.len() - cur.position() as usize),
            ));
        }
        Ok(v)
    }
}

/// Provides a method that reads this object as a Bitmessage entity
/// from a byte array with an exact length.
pub trait SizedReadFromExact {
    /// Reads this object as a Bitmessage entity
    /// from a byte array with an exact length.
    fn sized_read_from_exact(bytes: impl AsRef<[u8]>) -> io::Result<Self>
    where
        Self: Sized;
}

impl<T> SizedReadFromExact for T
where
    T: SizedReadFrom,
{
    fn sized_read_from_exact(bytes: impl AsRef<[u8]>) -> io::Result<Self>
    where
        Self: Sized,
    {
        let bytes = bytes.as_ref();
        let mut cur = Cursor::new(bytes);
        Self::sized_read_from(&mut cur, bytes.len())
    }
}

/// Provides a method that returns the object's byte length
/// when serialized as a Bitmessage entity.
pub trait LenBm {
    /// Returns the object's byte length
    /// when serialized as a Bitmessage entity.
    fn len_bm(&self) -> usize;
}

impl LenBm for u32 {
    fn len_bm(&self) -> usize {
        size_of::<u32>()
    }
}

impl LenBm for u64 {
    fn len_bm(&self) -> usize {
        size_of::<u64>()
    }
}
