// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause

use crate::rxops::RxOps;

#[derive(Debug, Eq, PartialEq)]
pub(crate) struct RxQueue {
    /// Bitmap of rx operations.
    queue: u8,
}

impl RxQueue {
    /// New instance of RxQueue.
    pub fn new() -> Self {
        RxQueue { queue: 0_u8 }
    }

    /// Enqueue a new rx operation into the queue.
    pub fn enqueue(&mut self, op: RxOps) {
        self.queue |= op.bitmask();
    }

    /// Dequeue an rx operation from the queue.
    pub fn dequeue(&mut self) -> Option<RxOps> {
        match self.peek() {
            Some(req) => {
                self.queue &= !req.bitmask();
                Some(req)
            }
            None => None,
        }
    }

    /// Peek into the queue to check if it contains an rx operation.
    pub fn peek(&self) -> Option<RxOps> {
        if self.contains(RxOps::Request.bitmask()) {
            return Some(RxOps::Request);
        }
        if self.contains(RxOps::Rw.bitmask()) {
            return Some(RxOps::Rw);
        }
        if self.contains(RxOps::Response.bitmask()) {
            return Some(RxOps::Response);
        }
        if self.contains(RxOps::CreditUpdate.bitmask()) {
            return Some(RxOps::CreditUpdate);
        }
        if self.contains(RxOps::Reset.bitmask()) {
            Some(RxOps::Reset)
        } else {
            None
        }
    }

    /// Check if the queue contains a particular rx operation.
    pub fn contains(&self, op: u8) -> bool {
        (self.queue & op) != 0
    }

    /// Check if there are any pending rx operations in the queue.
    pub fn pending_rx(&self) -> bool {
        self.queue != 0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_contains() {
        let mut rxqueue = RxQueue::new();
        rxqueue.queue = 31;

        assert!(rxqueue.contains(RxOps::Request.bitmask()));
        assert!(rxqueue.contains(RxOps::Rw.bitmask()));
        assert!(rxqueue.contains(RxOps::Response.bitmask()));
        assert!(rxqueue.contains(RxOps::CreditUpdate.bitmask()));
        assert!(rxqueue.contains(RxOps::Reset.bitmask()));

        rxqueue.queue = 0;
        assert!(!rxqueue.contains(RxOps::Request.bitmask()));
        assert!(!rxqueue.contains(RxOps::Rw.bitmask()));
        assert!(!rxqueue.contains(RxOps::Response.bitmask()));
        assert!(!rxqueue.contains(RxOps::CreditUpdate.bitmask()));
        assert!(!rxqueue.contains(RxOps::Reset.bitmask()));
    }

    #[test]
    fn test_enqueue() {
        let mut rxqueue = RxQueue::new();

        rxqueue.enqueue(RxOps::Request);
        assert!(rxqueue.contains(RxOps::Request.bitmask()));

        rxqueue.enqueue(RxOps::Rw);
        assert!(rxqueue.contains(RxOps::Rw.bitmask()));

        rxqueue.enqueue(RxOps::Response);
        assert!(rxqueue.contains(RxOps::Response.bitmask()));

        rxqueue.enqueue(RxOps::CreditUpdate);
        assert!(rxqueue.contains(RxOps::CreditUpdate.bitmask()));

        rxqueue.enqueue(RxOps::Reset);
        assert!(rxqueue.contains(RxOps::Reset.bitmask()));
    }

    #[test]
    fn test_peek() {
        let mut rxqueue = RxQueue::new();

        rxqueue.queue = 31;
        assert_eq!(rxqueue.peek(), Some(RxOps::Request));

        rxqueue.queue = 30;
        assert_eq!(rxqueue.peek(), Some(RxOps::Rw));

        rxqueue.queue = 28;
        assert_eq!(rxqueue.peek(), Some(RxOps::Response));

        rxqueue.queue = 24;
        assert_eq!(rxqueue.peek(), Some(RxOps::CreditUpdate));

        rxqueue.queue = 16;
        assert_eq!(rxqueue.peek(), Some(RxOps::Reset));
    }

    #[test]
    fn test_dequeue() {
        let mut rxqueue = RxQueue::new();
        rxqueue.queue = 31;

        assert_eq!(rxqueue.dequeue(), Some(RxOps::Request));
        assert!(!rxqueue.contains(RxOps::Request.bitmask()));

        assert_eq!(rxqueue.dequeue(), Some(RxOps::Rw));
        assert!(!rxqueue.contains(RxOps::Rw.bitmask()));

        assert_eq!(rxqueue.dequeue(), Some(RxOps::Response));
        assert!(!rxqueue.contains(RxOps::Response.bitmask()));

        assert_eq!(rxqueue.dequeue(), Some(RxOps::CreditUpdate));
        assert!(!rxqueue.contains(RxOps::CreditUpdate.bitmask()));

        assert_eq!(rxqueue.dequeue(), Some(RxOps::Reset));
        assert!(!rxqueue.contains(RxOps::Reset.bitmask()));
    }

    #[test]
    fn test_pending_rx() {
        let mut rxqueue = RxQueue::new();
        assert!(!rxqueue.pending_rx());

        rxqueue.queue = 1;
        assert!(rxqueue.pending_rx());
    }

    #[test]
    fn test_debug() {
        let rxqueue = RxQueue::new();

        assert_eq!(format!("{rxqueue:?}"), "RxQueue { queue: 0 }");
    }
}
