//! Bytes a connection writes itself, a status line and headers, held until
//! they have gone.

/// The most bytes a head the connection writes may have: its first line
/// and headers, or a status page with them.
pub const MAX_OWN: usize = 512;

/// Why a head cannot be composed.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RespondError {
    /// It is not the time for one: there is nothing to answer, or it is
    /// answered already.
    NotNow,
    /// The head does not fit in [`MAX_OWN`].
    TooLong,
}

impl core::fmt::Display for RespondError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(match self {
            Self::NotNow => "not the time for a head",
            Self::TooLong => "the head is too long",
        })
    }
}

impl core::error::Error for RespondError {}

/// Bytes the connection writes itself, and how many of them have gone.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Own {
    pub(crate) buf: [u8; MAX_OWN],
    pub(crate) len: usize,
    sent: usize,
}

impl Own {
    pub(crate) const fn new() -> Self {
        Self {
            buf: [0; MAX_OWN],
            len: 0,
            sent: 0,
        }
    }

    pub(crate) fn push(&mut self, b: &[u8]) -> Result<(), RespondError> {
        let end = self.len.checked_add(b.len()).ok_or(RespondError::TooLong)?;
        let dst = self
            .buf
            .get_mut(self.len..end)
            .ok_or(RespondError::TooLong)?;
        dst.copy_from_slice(b);
        self.len = end;
        Ok(())
    }

    pub(crate) fn push_u64(&mut self, v: u64) -> Result<(), RespondError> {
        let mut d = [0u8; 20];
        let mut n = d.len();
        let mut v = v;
        loop {
            n = n.saturating_sub(1);
            if let Some(slot) = d.get_mut(n) {
                // v % 10 < 10
                *slot = b'0'.saturating_add(u8::try_from(v % 10).unwrap_or(0));
            }
            v /= 10;
            if v == 0 {
                break;
            }
        }
        self.push(d.get(n..).unwrap_or_default())
    }

    pub(crate) const fn pending(&self) -> bool {
        self.sent < self.len
    }

    /// Writes what is left into `out`, returning how much.
    pub(crate) fn drain(&mut self, out: &mut [u8]) -> usize {
        let rest = self.buf.get(self.sent..self.len).unwrap_or_default();
        let n = rest.len().min(out.len());
        if let (Some(d), Some(s)) = (out.get_mut(..n), rest.get(..n)) {
            d.copy_from_slice(s);
        }
        self.sent = self.sent.saturating_add(n);
        n
    }
}