use crate::sync::atomic::{AtomicU32, Ordering};
use crate::sys::locks::Mutex;
use crate::time::Duration;
use yggdrasil_rt::{process::MutexOperation, sys};

pub struct Condvar {
    value: AtomicU32,
}

impl Condvar {
    #[inline]
    #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
    pub const fn new() -> Condvar {
        Condvar { value: AtomicU32::new(0) }
    }

    #[inline]
    pub fn notify_one(&self) {
        self.value.fetch_add(1, Ordering::Release);

        unsafe {
            sys::mutex(&self.value, &MutexOperation::Wake).ok();
        }
    }

    #[inline]
    pub fn notify_all(&self) {
        self.value.fetch_add(1, Ordering::Release);

        unsafe {
            sys::mutex(&self.value, &MutexOperation::WakeAll).ok();
        }
    }

    pub unsafe fn wait(&self, mutex: &Mutex) {
        // Load the old value while the lock is still held
        let compare_value = self.value.load(Ordering::Acquire);

        // Release the lock so others can now access the value
        mutex.unlock();

        // Wait for the value to change
        self.wait_inner(compare_value, None);

        // Re-lock the mutex
        mutex.lock();
    }

    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
        // Load the old value while the lock is still held
        let compare_value = self.value.load(Ordering::Acquire);

        // Release the lock so others can now access the value
        mutex.unlock();

        // Wait for the value to change
        let r = self.wait_inner(compare_value, Some(dur));

        // Re-lock the mutex
        mutex.lock();

        r
    }

    unsafe fn wait_inner(&self, value: u32, timeout: Option<Duration>) -> bool {
        sys::mutex(&self.value, &MutexOperation::Wait(value, timeout)).is_ok()
    }
}
