//! Page Request Interface types for ARM SMMU v3
//!
//! PRI queue management per ARM SMMU v3 specification Section 7.

use crate::types::{AccessType, SecurityState};

/// Page Request Interface entry
///
/// Contains information about a page request in the PRI queue.
/// Follows ARM SMMU v3 PRI format.
///
/// # ARM SMMU v3 Compliance
///
/// The `prg_index` field is the Page Request Group Index (ARM §8.2).  Every
/// page request belongs to a Page Request Group (PRG) identified by this value.
/// Software must echo the same `prg_index` back in `CMD_PRI_RESP` (ARM §8.3)
/// so that the SMMU can locate and retire the pending request.
///
/// The `security_state` field records the security context of the request.
/// Per ARM §7.3.19 / FINDING-NEW-32 the E_PAGE_REQUEST event generated by
/// `process_pri_queue()` must carry this field rather than a hardcoded NonSecure.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PRIEntry {
    /// Source stream identifier (raw u32 for simpler access)
    pub stream_id: u32,
    /// Process Address Space ID (raw u32 for simpler access)
    pub pasid: u32,
    /// Requested page address (raw u64 for simpler access)
    pub requested_address: u64,
    /// Access type requested (Read/Write/Execute)
    pub access_type: AccessType,
    /// True if this is the last request in a group
    pub is_last_request: bool,
    /// Request timestamp
    pub timestamp: u64,
    /// Page Request Group index (ARM §8.2).
    ///
    /// Identifies the Page Request Group (PRG) this entry belongs to.
    /// Software must echo this value in `CMD_PRI_RESP` to complete the
    /// response cycle (ARM §8.3).  Defaults to 0.
    pub prg_index: u16,
    /// Security state of the page request (ARM §7.3.19 / FINDING-NEW-32).
    ///
    /// The E_PAGE_REQUEST event must carry the security state of the originating
    /// transaction.  Defaults to `SecurityState::NonSecure`.
    pub security_state: SecurityState,
}

impl PRIEntry {
    /// Create a new PRI entry with stream ID and PASID only.
    ///
    /// Defaults `requested_address` to 0 and `access_type` to `AccessType::Read`.
    /// Use [`PRIEntry::with_address`] when you need to specify the full set of fields.
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::PRIEntry;
    ///
    /// let entry = PRIEntry::new(1, 0);
    /// assert_eq!(entry.stream_id, 1);
    /// assert_eq!(entry.pasid, 0);
    /// assert_eq!(entry.prg_index, 0);
    /// ```
    #[must_use]
    pub const fn new(stream_id: u32, pasid: u32) -> Self {
        Self {
            stream_id,
            pasid,
            requested_address: 0,
            access_type: AccessType::Read,
            is_last_request: false,
            timestamp: 0,
            prg_index: 0,
            security_state: SecurityState::NonSecure,
        }
    }

    /// Create a new PRI entry with a full set of fields.
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry};
    ///
    /// let entry = PRIEntry::with_address(1, 0, 0x1000, AccessType::Read);
    /// assert_eq!(entry.requested_address, 0x1000);
    /// assert_eq!(entry.prg_index, 0);
    /// ```
    #[must_use]
    pub const fn with_address(stream_id: u32, pasid: u32, requested_address: u64, access_type: AccessType) -> Self {
        Self {
            stream_id,
            pasid,
            requested_address,
            access_type,
            is_last_request: false,
            timestamp: 0,
            prg_index: 0,
            security_state: SecurityState::NonSecure,
        }
    }

    /// Set the Page Request Group index and return `self` (builder pattern).
    ///
    /// Used to associate this entry with a specific PRG so that the matching
    /// `CMD_PRI_RESP` can retire it (ARM §8.2, §8.3).
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry};
    ///
    /// let entry = PRIEntry::with_address(1, 0, 0x1000, AccessType::Read)
    ///     .with_prg_index(42);
    /// assert_eq!(entry.prg_index, 42);
    /// ```
    #[must_use]
    pub const fn with_prg_index(mut self, prg_index: u16) -> Self {
        self.prg_index = prg_index;
        self
    }

    /// Set the security state and return `self` (builder pattern).
    ///
    /// Used to specify the security context of the page request, so that the
    /// generated `E_PAGE_REQUEST` event correctly reflects the transaction's
    /// security state (ARM §7.3.19 / FINDING-NEW-32).
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry, SecurityState};
    ///
    /// let entry = PRIEntry::with_address(1, 0, 0x1000, AccessType::Read)
    ///     .with_security_state(SecurityState::Secure);
    /// assert_eq!(entry.security_state, SecurityState::Secure);
    /// ```
    #[must_use]
    pub const fn with_security_state(mut self, security_state: SecurityState) -> Self {
        self.security_state = security_state;
        self
    }
}
