//! Kernel object tracking for HLE.

use std::collections::VecDeque;
use std::path::PathBuf;
use std::sync::Arc;

use xenia_cpu::ThreadRef;

/// Kernel object types tracked by handle.
///
/// Sync variants (`Event`, `Semaphore`, `Mutex`, `Thread`) carry an in-place
/// waiter list so wait/set/release sites keep invariants local — dropping the
/// object implicitly drops its waiters. Waiters are stored as `ThreadRef`
/// (post-Axis-1) — a bare `hw_id: u8` would have been ambiguous under per-slot
/// runqueues where multiple guest threads share one HW slot.
#[derive(Debug)]
pub enum KernelObject {
    Event {
        manual_reset: bool,
        signaled: bool,
        /// Guest threads parked on this event.
        waiters: Vec<ThreadRef>,
    },
    Semaphore {
        count: i32,
        max: i32,
        waiters: Vec<ThreadRef>,
    },
    File {
        /// Normalized VFS path (e.g. "default.xex", "media/shared/foo.pkg").
        path: String,
        /// Full file size in bytes.
        size: u64,
        /// Current read/write cursor.
        position: u64,
        /// Whole-file buffer — VFS reads the entire file up front so
        /// subsequent NtReadFile calls are O(1) slice copies.
        /// `Arc<Vec<u8>>` so duplicate handles could share backing storage.
        data: Arc<Vec<u8>>,
        /// Directory-enumeration cursor consumed by `NtQueryDirectoryFile`.
        /// `None` before the first call; `Some(N)` = next VFS entry index
        /// to emit. Reset to `Some(0)` when the guest passes
        /// `restart_scan=1`. Unused on non-directory files.
        dir_enum_pos: Option<usize>,
        /// AUDIT-038 — when `Some`, this file is backed by a real host-FS
        /// path (the cache: persistent VFS) rather than the in-memory
        /// `data` buffer. NtReadFile / NtWriteFile / NtSetInformationFile
        /// route through `std::fs` against this path. Mirrors canary's
        /// `HostPathDevice` (xenia-canary/src/xenia/vfs/devices/
        /// host_path_device.cc) which symlinks `cache:` → `\CACHE`.
        /// `None` for disc-VFS reads, root-of-device opens, and synth
        /// stubs (those keep the in-memory zero-byte semantics).
        host_path: Option<PathBuf>,
    },
    Thread {
        id: u32,
        /// HW thread slot currently running this guest thread (None once exited
        /// — `exit_code` becomes Some).
        hw_id: Option<u8>,
        /// None while the thread is running; populated on ExTerminateThread
        /// or halt-sentinel return.
        exit_code: Option<u32>,
        /// Guest threads parked in KeWaitForSingleObject on this thread handle.
        waiters: Vec<ThreadRef>,
    },
    Timer {
        /// Xbox 360 timer_type 0 = NotificationTimer (manual-reset),
        /// 1 = SynchronizationTimer (auto-reset). Same shape as Event.
        manual_reset: bool,
        signaled: bool,
        /// Absolute tick-space deadline; None when disarmed.
        deadline: Option<u64>,
        /// Period in ticks (same units as `deadline`); 0 = one-shot.
        period_ticks: u64,
        /// Original ms value (canary's SetTimer keeps it for diagnostics).
        period_ms: u32,
        /// APC routine (deferred — see `timer_apc` warn in nt_set_timer_ex).
        callback_routine: u32,
        callback_arg: u32,
        waiters: Vec<ThreadRef>,
    },
    Mutex {
        /// HW thread id currently holding the mutex; None when free.
        owner: Option<u8>,
        recursion: u32,
        waiters: Vec<ThreadRef>,
    },
    NotifyListener {
        mask: u64,
        max_version: u32,
        queue: VecDeque<(u32, u32)>,
        waiters: Vec<ThreadRef>,
    },
}

impl KernelObject {
    /// Returns the per-object waiter list for the 5 sync variants (Event,
    /// Semaphore, Thread, Timer, Mutex) and `None` for `File`. Used by
    /// deadline-expiry scrub in `KernelState::handle_timeout_wake` so a
    /// timed-out waiter isn't left stranded in a handle's waiters list.
    pub fn waiters_mut(&mut self) -> Option<&mut Vec<ThreadRef>> {
        match self {
            KernelObject::Event { waiters, .. }
            | KernelObject::Semaphore { waiters, .. }
            | KernelObject::Thread { waiters, .. }
            | KernelObject::Timer { waiters, .. }
            | KernelObject::Mutex { waiters, .. }
            | KernelObject::NotifyListener { waiters, .. } => Some(waiters),
            KernelObject::File { .. } => None,
        }
    }
}
