use core::convert::Infallible;
use std::io::Write;
use crate::{
    bytes::{buf::Chain, Buf, BufMut, BufMutWriter, Bytes, BytesMut, EitherBuf},
    util::buffered::{BufWrite, ListWriteBuf, WriteBuf},
};
pub trait H1BufWrite: BufWrite {
    #[inline]
    fn write_buf_head<F, T, E>(&mut self, func: F) -> Result<T, E>
    where
        F: FnOnce(&mut BytesMut) -> Result<T, E>,
    {
        self.write_buf(func)
    }
    fn write_buf_static(&mut self, bytes: &'static [u8]) {
        let _ = self.write_buf(|buf| {
            buf.put_slice(bytes);
            Ok::<_, Infallible>(())
        });
    }
    fn write_buf_bytes(&mut self, bytes: Bytes) {
        let _ = self.write_buf(|buf| {
            buf.put_slice(bytes.as_ref());
            Ok::<_, Infallible>(())
        });
    }
    fn write_buf_bytes_chunked(&mut self, bytes: Bytes) {
        let _ = self.write_buf(|buf| {
            write!(BufMutWriter(buf), "{:X}\r\n", bytes.len()).unwrap();
            buf.reserve(bytes.len() + 2);
            buf.put_slice(bytes.as_ref());
            buf.put_slice(b"\r\n");
            Ok::<_, Infallible>(())
        });
    }
}
impl H1BufWrite for BytesMut {}
impl<const BUF_LIMIT: usize> H1BufWrite for WriteBuf<BUF_LIMIT> {}
type Eof = Chain<Chain<Bytes, Bytes>, &'static [u8]>;
type EncodedBuf<B, B2> = EitherBuf<B, EitherBuf<B2, &'static [u8]>>;
impl<const BUF_LIMIT: usize> H1BufWrite for ListWriteBuf<EncodedBuf<Bytes, Eof>, BUF_LIMIT> {
    fn write_buf_head<F, T, E>(&mut self, func: F) -> Result<T, E>
    where
        F: FnOnce(&mut BytesMut) -> Result<T, E>,
    {
        self.write_buf(func).map(|t| {
            let bytes = self.split_buf().freeze();
            self.buffer(EitherBuf::Left(bytes));
            t
        })
    }
    #[inline]
    fn write_buf_static(&mut self, bytes: &'static [u8]) {
        self.buffer(EitherBuf::Right(EitherBuf::Right(bytes)));
    }
    #[inline]
    fn write_buf_bytes(&mut self, bytes: Bytes) {
        self.buffer(EitherBuf::Left(bytes));
    }
    #[inline]
    fn write_buf_bytes_chunked(&mut self, bytes: Bytes) {
        let chunk = Bytes::from(format!("{:X}\r\n", bytes.len()))
            .chain(bytes)
            .chain(b"\r\n" as &'static [u8]);
        self.buffer(EitherBuf::Right(EitherBuf::Left(chunk)));
    }
}
impl<L, R> H1BufWrite for EitherBuf<L, R>
where
    L: H1BufWrite,
    R: H1BufWrite,
{
    #[inline]
    fn write_buf_head<F, T, E>(&mut self, func: F) -> Result<T, E>
    where
        F: FnOnce(&mut BytesMut) -> Result<T, E>,
    {
        match *self {
            Self::Left(ref mut l) => l.write_buf_head(func),
            Self::Right(ref mut r) => r.write_buf_head(func),
        }
    }
    #[inline]
    fn write_buf_static(&mut self, bytes: &'static [u8]) {
        match *self {
            Self::Left(ref mut l) => l.write_buf_static(bytes),
            Self::Right(ref mut r) => r.write_buf_static(bytes),
        }
    }
    #[inline]
    fn write_buf_bytes(&mut self, bytes: Bytes) {
        match *self {
            Self::Left(ref mut l) => l.write_buf_bytes(bytes),
            Self::Right(ref mut r) => r.write_buf_bytes(bytes),
        }
    }
    #[inline]
    fn write_buf_bytes_chunked(&mut self, bytes: Bytes) {
        match *self {
            Self::Left(ref mut l) => l.write_buf_bytes_chunked(bytes),
            Self::Right(ref mut r) => r.write_buf_bytes_chunked(bytes),
        }
    }
}