use dambi::{ByteBufferMut, Bytes};

/// Per-connection ingress ring of length-bounded `CAP`.
///
/// `FrameRing` is parser-agnostic — it owns the bytes, the caller owns the
/// parsing logic. `drain` takes a closure that pulls one frame at a time.
#[repr(C)]
pub(crate) struct FrameRing<const CAP: usize> {
    buf: ByteBufferMut,
    head: u16,
    tail: u16,
}

impl<const CAP: usize> FrameRing<CAP> {
    #[inline(always)]
    pub(crate) fn new() -> Self {
        const {
            assert!(CAP <= u16::MAX as usize, "FrameRing CAP must fit u16");
        }
        Self {
            buf: ByteBufferMut::with_capacity(CAP),
            head: 0,
            tail: 0,
        }
    }

    #[must_use = "remainder dropped if ignored"]
    #[inline(always)]
    pub(crate) fn append(&mut self, src: &[u8]) -> usize {
        let head = self.head as usize;
        let n = src.len().min(CAP - head);
        if n > 0 {
            self.buf.ensure_unique_for_mutate(head);

            unsafe {
                std::ptr::copy_nonoverlapping(src.as_ptr(), self.buf.data_mut_ptr().add(head), n);
            }
            self.head = (head + n) as u16;
        }
        n
    }

    /// Drain frames. The closure receives pending bytes and returns
    /// `Some(consumed)` if it parsed and handled one frame (advancing the
    /// ring by `consumed` bytes), or `None` to stop (need more bytes). A
    /// single closure body keeps borrows of caller-side state simple — both
    /// parse and handle use the same `&mut state` lifetime.
    #[inline(always)]
    pub(crate) fn drain<F>(&mut self, mut f: F)
    where
        F: FnMut(&Bytes) -> Option<usize>,
    {
        loop {
            let t = self.tail as usize;
            let h = self.head as usize;
            if h <= t {
                break;
            }
            let pending = Bytes::from_byte_buffer_range(self.buf.share(), t as u32, (h - t) as u32);
            let Some(consumed) = f(&pending) else {
                break;
            };
            if consumed > h - t {
                break;
            }
            self.tail = (t + consumed) as u16;
        }
        self.compact();
    }

    fn compact(&mut self) {
        let t = self.tail as usize;
        let h = self.head as usize;
        if t == 0 {
            return;
        }
        if t >= h {
            self.head = 0;
            self.tail = 0;
            return;
        }
        let unparsed = h - t;
        self.buf.ensure_unique_for_mutate(h);

        unsafe {
            std::ptr::copy(
                self.buf.data_ptr().add(t),
                self.buf.data_mut_ptr(),
                unparsed,
            );
        }
        self.head = unparsed as u16;
        self.tail = 0;
    }
}

impl<const CAP: usize> Default for FrameRing<CAP> {
    #[inline(always)]
    fn default() -> Self {
        Self::new()
    }
}
