use std::{
    fmt,
    ops::{Deref, DerefMut},
};

use http::Response;

use crate::{body::HttpBody, connection::HttpConnection, Bytes, HttpError};

pub struct HttpResponse<C: HttpConnection, T: HttpBody> {
    pub(crate) connection: C,
    pub(crate) position: usize,
    pub(crate) inner: Response<T>,
}

impl<C: HttpConnection, T: HttpBody + fmt::Debug> fmt::Debug for HttpResponse<C, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

impl<C: HttpConnection> HttpResponse<C, Bytes> {
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, HttpError<C::Error>> {
        let amount = match self.inner.body().content_length {
            Some(len) => core::cmp::min(len - self.position, buffer.len()),
            None => buffer.len(),
        };
        if amount == 0 {
            return Ok(0);
        }
        let len = self
            .connection
            .recv(&mut buffer[..amount])
            .map_err(HttpError::ConnectionError)?;
        self.position += len;
        Ok(len)
    }
}

impl<C: HttpConnection, T: HttpBody> Deref for HttpResponse<C, T> {
    type Target = Response<T>;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<C: HttpConnection, T: HttpBody> DerefMut for HttpResponse<C, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}
