//! Manual implementations of representations for `#![no_std]`

use alloc::boxed::Box;

#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ErrorKind {
    Interrupted,
    UnexpectedEof,
    WouldBlock,
    Other,
}

impl ErrorKind {
    fn as_str(&self) -> &'static str {
        use ErrorKind::*;
        match *self {
            Interrupted => "operation interrupted",
            UnexpectedEof => "unexpected end of file",
            WouldBlock => "operation would block",
            Other => "other error",
        }
    }
}

impl core::fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(self.as_str())
    }
}

pub struct Error {
    kind: ErrorKind,
    err: Option<Box<dyn core::fmt::Display + Send + Sync + 'static>>,
}

impl alloc::fmt::Debug for Error {
    fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> Result<(), alloc::fmt::Error> {
        let mut s = f.debug_struct("Error");
        s.field("kind", &self.kind);
        if let Some(err) = self.err.as_ref() {
            s.field("err", &alloc::format!("{err}"));
        }
        s.finish()
    }
}

impl Error {
    pub fn new(kind: ErrorKind, err: Box<dyn core::fmt::Display + Send + Sync + 'static>) -> Self {
        Self {
            kind,
            err: Some(err),
        }
    }

    pub fn from(kind: ErrorKind) -> Self {
        Self { kind, err: None }
    }

    pub fn kind(&self) -> ErrorKind {
        self.kind
    }

    pub fn get_ref(&self) -> Option<&(dyn core::fmt::Display + Send + Sync)> {
        self.err.as_ref().map(|e| e.as_ref())
    }

    pub fn into_inner(self) -> Option<Box<dyn core::fmt::Display + Send + Sync + 'static>> {
        self.err
    }
}

impl core::fmt::Display for Error {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(self.kind.as_str())?;

        if let Some(ref e) = self.err {
            e.fmt(f)?;
        }

        Ok(())
    }
}

impl From<ErrorKind> for Error {
    fn from(value: ErrorKind) -> Self {
        Self::from(value)
    }
}

pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error>;

    fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Error> {
        while !buf.is_empty() {
            match self.read(buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        if !buf.is_empty() {
            Err(Error::from(ErrorKind::UnexpectedEof))
        } else {
            Ok(())
        }
    }
}

impl Read for &[u8] {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        let size = core::cmp::min(self.len(), buf.len());
        let (to_copy, rest) = self.split_at(size);

        if size == 1 {
            buf[0] = to_copy[0];
        } else {
            buf[..size].copy_from_slice(to_copy);
        }

        *self = rest;
        Ok(size)
    }
}

impl<'a, T> Read for &'a mut T
where
    T: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        (*self).read(buf)
    }
}

pub trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
    fn flush(&mut self) -> Result<(), Error>;
}

impl<'a, T> Write for &'a mut T
where
    T: Write,
{
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
        (*self).write(buf)
    }

    fn flush(&mut self) -> Result<(), Error> {
        (*self).flush()
    }
}

impl Write for &mut [u8] {
    #[inline]
    fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
        let amt = core::cmp::min(data.len(), self.len());
        let (a, b) = core::mem::take(self).split_at_mut(amt);
        a.copy_from_slice(&data[..amt]);
        *self = b;
        Ok(amt)
    }

    fn flush(&mut self) -> Result<(), Error> {
        Ok(())
    }
}
