// Status: minimal — bounded RX-intent queue. Producers (muxer +
// proxies) push RxPackets here; drained into virtq RX descriptors
// when available. The vsock device's own pending_rx field is
// effectively the same idea; the muxer wraps it for API parity.

use super::packet::RxPacket;
use std::collections::VecDeque;

pub const MUXER_RXQ_SIZE: usize = 16 * 1024;

pub struct MuxerRxQ {
    queue: VecDeque<RxPacket>,
}

impl MuxerRxQ {
    pub fn new() -> Self {
        Self {
            queue: VecDeque::with_capacity(MUXER_RXQ_SIZE),
        }
    }
    pub fn push(&mut self, pkt: RxPacket) -> bool {
        if self.queue.len() >= MUXER_RXQ_SIZE {
            return false;
        }
        self.queue.push_back(pkt);
        true
    }

    /// Push and report whether the queue was empty *before* the
    /// push. Callers that route this into the device-kick path
    /// only need to raise the IRQ when transitioning from empty
    /// → non-empty: a follow-up push during the in-flight drain
    /// is already visible to the vCPU's RX-loop. Saves one
    /// `hv_gic_set_spi` syscall per coalesced packet under burst.
    pub fn push_was_empty(&mut self, pkt: RxPacket) -> Option<bool> {
        if self.queue.len() >= MUXER_RXQ_SIZE {
            return None;
        }
        let was_empty = self.queue.is_empty();
        self.queue.push_back(pkt);
        Some(was_empty)
    }
    pub fn pop(&mut self) -> Option<RxPacket> {
        self.queue.pop_front()
    }
    pub fn peek(&self) -> Option<&RxPacket> {
        self.queue.front()
    }
    pub fn is_empty(&self) -> bool {
        self.queue.is_empty()
    }
    pub fn len(&self) -> usize {
        self.queue.len()
    }
    pub fn drain(&mut self) {
        self.queue.clear();
    }
}
