// PTHREAD_COND_INITIALIZER (macro)

use core::{ffi::c_int, ptr::NonNull, sync::atomic::Ordering};

use yggdrasil_rt::process::MutexOperation;

use crate::{
    error::{self, CIntZeroResult, CResult, EResult},
    headers::{
        errno::Errno,
        sys_time::__ygg_timespec_t,
        sys_types::{clockid_t, pthread_cond_t, pthread_condattr_t, pthread_mutex_t},
        time::CLOCK_MONOTONIC,
    },
    util::PointerExt,
};

use super::PTHREAD_PROCESS_PRIVATE;

impl pthread_cond_t {
    fn wait(&self, mutex: &pthread_mutex_t, deadline: Option<&__ygg_timespec_t>) -> EResult<()> {
        // mutex is locked on entry
        let value = self.__write.load(Ordering::Acquire);
        self.__read.store(value, Ordering::Release);

        unsafe { mutex.release() }?;

        // Wait
        match deadline {
            Some(_deadline) => {
                todo!()
            }
            None => {
                unsafe {
                    yggdrasil_rt::sys::mutex(
                        &self.__write,
                        &MutexOperation::WaitWhileEqual(value, None),
                    )
                }
                .expect("Mutex wait failed");
                // Re-acquire the mutex
                mutex.lock()?;
            }
        }

        EResult::Ok(())
    }

    fn signal(&self, n: u32) {
        // this is guarded by a mutex on entry
        let value = self.__read.load(Ordering::Acquire);
        self.__write.store(value.wrapping_add(1), Ordering::Release);
        unsafe { yggdrasil_rt::sys::mutex(&self.__write, &MutexOperation::Wake(n)).ok() };
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_cond_init(
    cond: *mut pthread_cond_t,
    _attr: *const pthread_condattr_t,
) -> CIntZeroResult {
    let cond = cond.ensure_mut();
    cond.__read.store(0, Ordering::Release);
    cond.__write.store(0, Ordering::Release);
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_cond_destroy(_cond: *mut pthread_cond_t) -> CIntZeroResult {
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_cond_wait(
    cond: *mut pthread_cond_t,
    mutex: *mut pthread_mutex_t,
) -> CIntZeroResult {
    let cond = cond.ensure_mut();
    let mutex = mutex.ensure_mut();
    cond.wait(mutex, None)?;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_cond_timedwait(
    cond: *mut pthread_cond_t,
    mutex: *mut pthread_mutex_t,
    timeout: *const __ygg_timespec_t,
) -> CIntZeroResult {
    let cond = cond.ensure_mut();
    let mutex = mutex.ensure_mut();
    let timeout = timeout.ensure();
    cond.wait(mutex, Some(timeout))?;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_cond_signal(cond: *mut pthread_cond_t) -> CIntZeroResult {
    let cond = cond.ensure_mut();
    cond.signal(1);
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> CIntZeroResult {
    let cond = cond.ensure_mut();
    cond.signal(u32::MAX);
    CIntZeroResult::SUCCESS
}

// Attributes

#[no_mangle]
unsafe extern "C" fn pthread_condattr_init(_attr: *mut pthread_condattr_t) -> CIntZeroResult {
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_condattr_destroy(_attr: *mut pthread_condattr_t) -> CIntZeroResult {
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_condattr_getclock(
    _attr: *const pthread_condattr_t,
    clockid: *mut clockid_t,
) -> CIntZeroResult {
    if let Some(clockid) = NonNull::new(clockid) {
        clockid.write(CLOCK_MONOTONIC);
    }
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_condattr_getpshared(
    _attr: *const pthread_condattr_t,
    shared: *mut c_int,
) -> CIntZeroResult {
    if let Some(shared) = NonNull::new(shared) {
        shared.write(PTHREAD_PROCESS_PRIVATE);
    }
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_condattr_setclock(
    _attr: *mut pthread_condattr_t,
    clockid: clockid_t,
) -> CIntZeroResult {
    if clockid == CLOCK_MONOTONIC {
        CIntZeroResult::SUCCESS
    } else {
        log::error!("TODO: pthread_condattr_setclock()");
        error::set_errno(Errno::EINVAL);
        CIntZeroResult::ERROR
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_condattr_setpshared(
    _attr: *mut pthread_condattr_t,
    shared: c_int,
) -> CIntZeroResult {
    if shared == PTHREAD_PROCESS_PRIVATE {
        CIntZeroResult::SUCCESS
    } else {
        log::error!("TODO: pthread_condattr_setpshared()");
        error::set_errno(Errno::EINVAL);
        CIntZeroResult::ERROR
    }
}
