//! Monitor for object synchronization (monitorenter/monitorexit).
//!
//! Implements biased locking for uncontended synchronization:
//! - **Biased**: Object is biased toward a single thread; lock/unlock is nearly free.
//! - **Thin**: Lightweight CAS-based lock for low-contention scenarios.
//! - **Fat**: Full mutex for contended scenarios.

/// Lock state for biased locking optimization
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LockState {
    /// Object is unlocked and unbiased
    Unlocked,
    /// Object is biased toward a specific thread (fast path)
    Biased,
    /// Thin lock: held by a thread with a recursion count
    Thin,
    /// Fat lock: full monitor with waiters
    Fat,
}

/// Monitor for object synchronization with biased locking support
#[derive(Debug, Clone)]
pub struct Monitor {
    pub owner: Option<u32>,
    pub count: u32,
    pub waiters: Vec<u32>,
    /// Current lock state
    pub lock_state: LockState,
    /// Thread this object is biased toward (biased locking)
    pub biased_thread: Option<u32>,
    /// Number of times the bias was revoked (for statistics)
    pub bias_revocations: u32,
}

impl Monitor {
    pub fn new() -> Self {
        Self {
            owner: None,
            count: 0,
            waiters: Vec::new(),
            lock_state: LockState::Unlocked,
            biased_thread: None,
            bias_revocations: 0,
        }
    }

    /// Enter the monitor. Uses biased locking fast path when possible.
    pub fn enter(&mut self, thread_id: u32) -> bool {
        match self.lock_state {
            LockState::Unlocked => {
                // Bias the object toward this thread on first lock
                self.owner = Some(thread_id);
                self.count = 1;
                self.biased_thread = Some(thread_id);
                self.lock_state = LockState::Biased;
                true
            }
            LockState::Biased => {
                if self.biased_thread == Some(thread_id) {
                    // Fast path: biased toward this thread - no CAS needed
                    self.count += 1;
                    true
                } else {
                    // Bias revocation: another thread wants the lock
                    self.bias_revocations += 1;
                    self.biased_thread = None;
                    self.lock_state = LockState::Thin;
                    // Fall through to thin lock logic
                    if self.owner.is_none() {
                        self.owner = Some(thread_id);
                        self.count = 1;
                        true
                    } else {
                        self.waiters.push(thread_id);
                        self.lock_state = LockState::Fat;
                        false
                    }
                }
            }
            LockState::Thin => {
                if let Some(owner) = self.owner {
                    if owner == thread_id {
                        // Reentrant lock
                        self.count += 1;
                        true
                    } else {
                        // Contention: upgrade to fat lock
                        self.lock_state = LockState::Fat;
                        self.waiters.push(thread_id);
                        false
                    }
                } else {
                    self.owner = Some(thread_id);
                    self.count = 1;
                    true
                }
            }
            LockState::Fat => {
                if let Some(owner) = self.owner {
                    if owner == thread_id {
                        self.count += 1;
                        true
                    } else {
                        self.waiters.push(thread_id);
                        false
                    }
                } else {
                    self.owner = Some(thread_id);
                    self.count = 1;
                    true
                }
            }
        }
    }

    pub fn exit(&mut self, thread_id: u32) -> bool {
        if let Some(owner) = self.owner {
            if owner == thread_id {
                self.count -= 1;
                if self.count == 0 {
                    self.owner = None;
                    if self.waiters.is_empty() {
                        // Return to biased state if no contention
                        if self.lock_state == LockState::Thin {
                            self.lock_state = LockState::Unlocked;
                        } else if self.lock_state == LockState::Biased {
                            // Keep biased toward this thread
                            self.count = 0;
                        } else {
                            self.lock_state = LockState::Unlocked;
                        }
                    } else if let Some(next_thread) = self.waiters.first() {
                        let next_thread = *next_thread;
                        self.waiters.remove(0);
                        self.owner = Some(next_thread);
                        self.count = 1;
                    }
                }
                true
            } else {
                false
            }
        } else {
            false
        }
    }

    pub fn is_owned_by(&self, thread_id: u32) -> bool {
        self.owner.map_or(false, |owner| owner == thread_id)
    }

    /// Check if the monitor is biased toward the given thread (fast path available)
    pub fn is_biased_toward(&self, thread_id: u32) -> bool {
        self.lock_state == LockState::Biased && self.biased_thread == Some(thread_id)
    }

    /// Revoke bias (called when another thread needs the lock)
    pub fn revoke_bias(&mut self) {
        if self.lock_state == LockState::Biased {
            self.biased_thread = None;
            self.bias_revocations += 1;
            self.lock_state = if self.owner.is_some() {
                LockState::Thin
            } else {
                LockState::Unlocked
            };
        }
    }

    /// Get lock statistics
    pub fn lock_stats(&self) -> MonitorStats {
        MonitorStats {
            lock_state: self.lock_state,
            bias_revocations: self.bias_revocations,
            waiter_count: self.waiters.len(),
            recursion_depth: self.count,
        }
    }

    pub fn wait(&mut self, thread_id: u32) -> bool {
        if let Some(owner) = self.owner {
            if owner == thread_id {
                self.waiters.push(thread_id);
                let old_count = self.count;
                self.owner = None;
                self.count = 0;

                if let Some(next_thread) = self.waiters.first() {
                    let next_thread = *next_thread;
                    self.waiters.remove(0);
                    self.owner = Some(next_thread);
                    self.count = 1;
                }

                old_count > 0
            } else {
                false
            }
        } else {
            false
        }
    }

    pub fn notify(&mut self) -> bool {
        if self.waiters.is_empty() {
            false
        } else {
            if let Some(waiter) = self.waiters.first() {
                let waiter = *waiter;
                self.waiters.remove(0);
                self.waiters.push(waiter);
                true
            } else {
                false
            }
        }
    }

    pub fn notify_all(&mut self) -> usize {
        let count = self.waiters.len();
        if count > 0 {
            self.waiters.rotate_right(1);
        }
        count
    }
}

impl Default for Monitor {
    fn default() -> Self {
        Self::new()
    }
}

/// Monitor statistics for diagnostics
#[derive(Debug, Clone, Copy)]
pub struct MonitorStats {
    pub lock_state: LockState,
    pub bias_revocations: u32,
    pub waiter_count: usize,
    pub recursion_depth: u32,
}
