//! Event queue types for ARM SMMU v3
//!
//! Event queue management per ARM SMMU v3 specification Section 6.3.

use crate::types::SecurityState;

/// Event type enumeration
///
/// Defines the types of events that can be queued in the event queue.
///
/// Discriminant values are the exact event numbers from ARM IHI0070G.b §7.3.
/// Variants in the 0xE0–0xEF range are IMPLEMENTATION DEFINED per §7.3.21 and
/// are used by this software model for internal completion signalling that has
/// no direct equivalent in the hardware event queue.
#[repr(u8)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum EventType {
    // ── Spec-defined events (§7.3) ──────────────────────────────────────────
    /// §7.3.2  F_UUT — Unsupported Upstream Transaction (0x01)
    FUut = 0x01,
    /// §7.3.3  C_BAD_STREAMID — StreamID out of range (0x02)
    CBadStreamid = 0x02,
    /// §7.3.4  F_STE_FETCH — STE fetch caused external abort (0x03)
    FSteFetch = 0x03,
    /// §7.3.5  C_BAD_STE — Used STE invalid (0x04)
    CBadSte = 0x04,
    /// §7.3.6  F_BAD_ATS_TREQ — ATS Translation Request disallowed (0x05)
    FBadAtsTreq = 0x05,
    /// §7.3.7  F_STREAM_DISABLED — Non-substream transaction with stream disabled (0x06)
    FStreamDisabled = 0x06,
    /// §7.3.8  F_TRANSL_FORBIDDEN — ATS Translated transaction disallowed (0x07)
    FTranslForbidden = 0x07,
    /// §7.3.9  C_BAD_SUBSTREAMID — SubstreamID present but invalid (0x08)
    CBadSubstreamid = 0x08,
    /// §7.3.10 F_CD_FETCH — CD fetch caused external abort (0x09)
    FCdFetch = 0x09,
    /// §7.3.11 C_BAD_CD — Fetched CD invalid (0x0A)
    CBadCd = 0x0A,
    /// §7.3.12 F_WALK_EABT — External abort during translation table walk (0x0B)
    FWalkEabt = 0x0B,
    /// §7.3.13 F_TRANSLATION — Translation fault (0x10)
    FTranslation = 0x10,
    /// §7.3.14 F_ADDR_SIZE — Address size fault (0x11)
    FAddrSize = 0x11,
    /// §7.3.15 F_ACCESS — Access flag fault (0x12)
    FAccess = 0x12,
    /// §7.3.16 F_PERMISSION — Permission fault (0x13)
    FPermission = 0x13,
    /// §7.3.17 F_TLB_CONFLICT — TLB conflict (0x20)
    FTlbConflict = 0x20,
    /// §7.3.18 F_CFG_CONFLICT — Configuration cache conflict (0x21)
    FCfgConflict = 0x21,
    /// §7.3.19 E_PAGE_REQUEST — Page request hint (0x24)
    EPageRequest = 0x24,
    /// §7.3.20 F_VMS_FETCH — VMS fetch caused external abort (0x25)
    FVmsFetch = 0x25,
    // ── Implementation-defined events (§7.3.21, 0xE0–0xEF range) ───────────
    /// IMPDEF: Command SYNC completion — internal SW model signalling
    CommandSyncCompletion = 0xE0,
    /// IMPDEF: ATC invalidation completion — internal SW model signalling
    AtcInvalidateCompletion = 0xE1,
}

impl Default for EventType {
    fn default() -> Self {
        Self::FTranslation
    }
}

/// Event entry structure
///
/// Contains all information about a single event in the event queue.
/// Follows ARM SMMU v3 event record format.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct EventEntry {
    /// Type of event
    pub event_type: EventType,
    /// Source stream identifier (raw u32 for simpler access)
    pub stream_id: u32,
    /// Process Address Space ID (raw u32 for simpler access)
    pub pasid: u32,
    /// Faulting or relevant address (raw u64 for simpler access)
    pub address: u64,
    /// Security state context
    pub security_state: SecurityState,
    /// Event-specific error code
    pub error_code: u32,
    /// Event timestamp
    pub timestamp: u64,
    /// Whether this event corresponds to a stalled transaction (ARM IHI0070G.b §7.3)
    /// Must be true for events generated by stall-mode faults (§3.5.3).
    pub stall: bool,
    /// §3.12.2: Stall Tag — identifies the stalled transaction group for CMD_RESUME matching.
    /// Non-zero only when `stall == true`; zero for all non-stall events.
    pub stag: u16,

    // ── CONF-GAP-20: §7.3 event record wire format fields ───────────────────
    /// Intermediate Physical Address — for two-stage faults (§7.3).
    /// Zero for single-stage faults.
    pub ipa: u64,
    /// CLASS: 2-bit event class field per ARM IHI0070G.b §7.3.
    ///
    /// Defined only for translation-related F_* events:
    /// - `0b00` (0) = CD — fault during CD fetch
    /// - `0b01` (1) = TTD — fault during translation table descriptor fetch
    /// - `0b10` (2) = IN — fault on the input address itself (default for SW model F_* events)
    /// - `0b11` = Reserved
    ///
    /// C_* configuration events (C_BAD_STREAMID, C_BAD_STE, C_BAD_SUBSTREAMID, C_BAD_CD,
    /// F_CFG_CONFLICT) must leave this field at 0 — CLASS is not defined for them.
    pub event_class: u8,
    /// S2 flag — true if the fault occurred during Stage-2 translation (§7.3).
    pub s2: bool,
    /// RnW — true=write, false=read (§7.3).
    pub rnw: bool,
    /// InD — true=instruction fetch, false=data access (§7.3).
    pub ind: bool,
    /// PnU — true=privileged, false=unprivileged (§7.3).
    pub pnu: bool,
    /// NSIPA — true if the IPA is in the Non-Secure physical address space (§7.3).
    pub nsipa: bool,
    /// SSV — SubstreamID Valid; true when the transaction carried a non-zero PASID (§7.3).
    pub ssv: bool,
}

impl EventEntry {
    /// Return a zeroed/defaulted `EventEntry`.
    ///
    /// Provided as a `const fn` so it can be used as a struct-update base in
    /// both `const` and non-`const` contexts:
    ///
    /// ```rust
    /// use smmu::types::{EventEntry, EventType};
    /// let ev = EventEntry { event_type: EventType::FTranslation, stream_id: 1,
    ///     ..EventEntry::zeroed() };
    /// ```
    #[must_use]
    pub const fn zeroed() -> Self {
        Self {
            event_type: EventType::FTranslation,
            stream_id: 0,
            pasid: 0,
            address: 0,
            security_state: SecurityState::NonSecure,
            error_code: 0,
            timestamp: 0,
            stall: false,
            stag: 0,
            ipa: 0,
            event_class: 0,
            s2: false,
            rnw: false,
            ind: false,
            pnu: false,
            nsipa: false,
            ssv: false,
        }
    }

    /// Create a new event entry
    #[must_use]
    pub const fn new(event_type: EventType, stream_id: u32, pasid: u32, address: u64) -> Self {
        Self {
            event_type,
            stream_id,
            pasid,
            address,
            security_state: SecurityState::NonSecure,
            error_code: 0,
            timestamp: 0,
            stall: false,
            stag: 0,
            // CONF-GAP-20: wire-format fields default to zero/false.
            ipa: 0,
            event_class: 0,
            s2: false,
            rnw: false,
            ind: false,
            pnu: false,
            nsipa: false,
            ssv: false,
        }
    }
}

impl Default for EventEntry {
    fn default() -> Self {
        Self::zeroed()
    }
}
