//! Read/write primitive unsigned integers from/to byte readers (little endian)
//! There is also byteorder crate...

use std::{
    io::{
        Read,
        Write
    },
    mem
};

pub trait ReadBytes {
    fn read_bytes<R: Read>(reader: &mut R) -> Result<Self, String>
    where Self: Sized;
}

pub trait ReadBytesVec {
    fn read_bytes_vec<R: Read>(reader: &mut R, num: usize) -> Result<Vec<Self>, String>
    where Self: Sized;
}

pub trait WriteBytes {
    fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), String>;
}

// ----------------------------------------------------------------

// TODO: factor out, maybe using macros...

impl ReadBytes for u8 {
    fn read_bytes<R: Read>(reader: &mut R) -> Result<Self, String> {
        let mut bytes = [0u8; mem::size_of::<Self>()];
        reader.read_exact(&mut bytes).map_err(|e| e.to_string())?;
        Ok(Self::from_le_bytes(bytes))
    }
}

impl ReadBytes for u16 {
    fn read_bytes<R: Read>(reader: &mut R) -> Result<Self, String> {
        let mut bytes = [0u8; mem::size_of::<Self>()];
        reader.read_exact(&mut bytes).map_err(|e| e.to_string())?;
        Ok(Self::from_le_bytes(bytes))
    }
}

impl ReadBytes for u32 {
    fn read_bytes<R: Read>(reader: &mut R) -> Result<Self, String> {
        let mut bytes = [0u8; mem::size_of::<Self>()];
        reader.read_exact(&mut bytes).map_err(|e| e.to_string())?;
        Ok(Self::from_le_bytes(bytes))
    }
}

impl ReadBytes for u64 {
    fn read_bytes<R: Read>(reader: &mut R) -> Result<Self, String> {
        let mut bytes = [0u8; mem::size_of::<Self>()];
        reader.read_exact(&mut bytes).map_err(|e| e.to_string())?;
        Ok(Self::from_le_bytes(bytes))
    }
}

impl ReadBytes for u128 {
    fn read_bytes<R: Read>(reader: &mut R) -> Result<Self, String> {
        let mut bytes = [0u8; mem::size_of::<Self>()];
        reader.read_exact(&mut bytes).map_err(|e| e.to_string())?;
        Ok(Self::from_le_bytes(bytes))
    }
}

// ----------------------------------------------------------------

impl<T: Sized + ReadBytes> ReadBytesVec for T {
    fn read_bytes_vec<R: Read>(reader: &mut R, num: usize) -> Result<Vec<Self>, String> {
        let mut values = Vec::<Self>::with_capacity(num);
        for _ in 0..num {
            values.push(Self::read_bytes(reader).map_err(|e| e.to_string())?);
        }
        Ok(values)
    }
}

// ----------------------------------------------------------------

// TODO: factor out...

impl WriteBytes for u8 {
    fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), String> {
        writer.write_all(&mut self.to_le_bytes()).map_err(|e| e.to_string())
    }
}

impl WriteBytes for u16 {
    fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), String> {
        writer.write_all(&mut self.to_le_bytes()).map_err(|e| e.to_string())
    }
}

impl WriteBytes for u32 {
    fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), String> {
        writer.write_all(&mut self.to_le_bytes()).map_err(|e| e.to_string())
    }
}

impl WriteBytes for u64 {
    fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), String> {
        writer.write_all(&mut self.to_le_bytes()).map_err(|e| e.to_string())
    }
}

impl WriteBytes for u128 {
    fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), String> {
        writer.write_all(&mut self.to_le_bytes()).map_err(|e| e.to_string())
    }
}

// ----------------------------------------------------------------

impl<T: WriteBytes> WriteBytes for Vec<T> { // and thus for Vec<Vec<T>>, Vec<Vec<Vec<T>>>, etc.
    fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), String> {
        for v in self {
            v.write_bytes(writer)?;
        }
        Ok(())
    }
}
