use embedded_io::Error;
use crate::io::{Read, Write};
pub fn try_read_full<R: Read>(mut read: R, buf: &mut [u8]) -> Result<usize, (R::Error, usize)> {
    let mut offset = 0;
    let mut size = 0;
    loop {
        let size_read = read.read(&mut buf[offset..]).map_err(|e| (e, size))?;
        offset += size_read;
        size += size_read;
        if size_read == 0 || size == buf.len() {
            break;
        }
    }
    Ok(size)
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CopyError<R, W> {
    Read(R),
    Write(W),
}
impl<R: core::fmt::Debug, W: core::fmt::Debug> core::fmt::Display for CopyError<R, W> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{self:?}")
    }
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<R: core::fmt::Debug, W: core::fmt::Debug> std::error::Error for CopyError<R, W> {}
impl<R, W> Error for CopyError<R, W>
where
    R: Error,
    W: Error,
{
    fn kind(&self) -> embedded_io::ErrorKind {
        match self {
            Self::Read(e) => e.kind(),
            Self::Write(e) => e.kind(),
        }
    }
}
pub fn copy<R, W>(read: R, write: W, buf: &mut [u8]) -> Result<u64, CopyError<R::Error, W::Error>>
where
    R: Read,
    W: Write,
{
    copy_len(read, write, buf, u64::MAX)
}
pub fn copy_len<R, W>(
    read: R,
    write: W,
    buf: &mut [u8],
    len: u64,
) -> Result<u64, CopyError<R::Error, W::Error>>
where
    R: Read,
    W: Write,
{
    copy_len_with_progress(read, write, buf, len, |_, _| {})
}
pub fn copy_len_with_progress<R, W, P>(
    mut read: R,
    mut write: W,
    buf: &mut [u8],
    mut len: u64,
    progress: P,
) -> Result<u64, CopyError<R::Error, W::Error>>
where
    R: Read,
    W: Write,
    P: Fn(u64, u64),
{
    let mut copied = 0;
    while len > 0 {
        progress(copied, len);
        let size_read = read.read(buf).map_err(CopyError::Read)?;
        if size_read == 0 {
            break;
        }
        write
            .write_all(&buf[0..size_read])
            .map_err(CopyError::Write)?;
        copied += size_read as u64;
        len -= size_read as u64;
    }
    progress(copied, len);
    Ok(copied)
}
pub mod asynch {
    use crate::io::asynch::{Read, Write};
    pub use super::CopyError;
    pub async fn try_read_full<R: Read>(
        mut read: R,
        buf: &mut [u8],
    ) -> Result<usize, (R::Error, usize)> {
        let mut offset = 0;
        let mut size = 0;
        loop {
            let size_read = read.read(&mut buf[offset..]).await.map_err(|e| (e, size))?;
            offset += size_read;
            size += size_read;
            if size_read == 0 || size == buf.len() {
                break;
            }
        }
        Ok(size)
    }
    pub async fn copy<R, W>(
        read: R,
        write: W,
        buf: &mut [u8],
    ) -> Result<u64, CopyError<R::Error, W::Error>>
    where
        R: Read,
        W: Write,
    {
        copy_len(read, write, buf, u64::MAX).await
    }
    pub async fn copy_len<R, W>(
        read: R,
        write: W,
        buf: &mut [u8],
        len: u64,
    ) -> Result<u64, CopyError<R::Error, W::Error>>
    where
        R: Read,
        W: Write,
    {
        copy_len_with_progress(read, write, buf, len, |_, _| {}).await
    }
    pub async fn copy_len_with_progress<R, W, P>(
        mut read: R,
        mut write: W,
        buf: &mut [u8],
        mut len: u64,
        progress: P,
    ) -> Result<u64, CopyError<R::Error, W::Error>>
    where
        R: Read,
        W: Write,
        P: Fn(u64, u64),
    {
        let mut copied = 0;
        while len > 0 {
            progress(copied, len);
            let size_read = read.read(buf).await.map_err(CopyError::Read)?;
            if size_read == 0 {
                break;
            }
            write
                .write_all(&buf[0..size_read])
                .await
                .map_err(CopyError::Write)?;
            copied += size_read as u64;
            len -= size_read as u64;
        }
        progress(copied, len);
        Ok(copied)
    }
}