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

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ProtocolWriterError {
    #[error("error using underlying writer")]
    Io(#[from] io::Error)
}

pub type ProtocolWriterResult<T> = Result<T, ProtocolWriterError>;

pub struct ProtocolWriter<W: Write> {
    writer: W
}

#[derive(Error, Debug)]
pub enum ProtocolReaderError {
    #[error("error using underlying reader")]
    Io(#[from] io::Error),
}

pub type ProtocolReaderResult<T> = Result<T, ProtocolReaderError>;

pub struct ProtocolReader<R: Read> {
    reader: R
}

impl<W: Write> ProtocolWriter<W> {
    pub fn new(writer: W) -> Self {
        Self { writer }
    }

    pub fn destroy(self) -> W {
        self.writer
    }

    pub fn write_u8(&mut self, value: u8) -> ProtocolWriterResult<()> {
        self.writer.write_all(&value.to_le_bytes())?;
        Ok(())
    }

    pub fn write_u16(&mut self, value: u16) -> ProtocolWriterResult<()> {
        self.writer.write_all(&value.to_le_bytes())?;
        Ok(())
    }

}
impl<R: Read> ProtocolReader<R> {
    pub fn new(reader: R) -> Self {
        Self { reader }
    }

    pub fn read_u8(&mut self) -> ProtocolReaderResult<u8> {
        let mut u8_buf = [0; 1];
        self.reader.read_exact(&mut u8_buf)?;

        Ok(u8::from_le_bytes(u8_buf))
    }

    pub fn read_u16(&mut self) -> ProtocolReaderResult<u16> {
        let mut u16_buf = [0; 2];
        self.reader.read_exact(&mut u16_buf)?;

        Ok(u16::from_le_bytes(u16_buf))
    }
}