use core::{ffi::c_void, ptr::null_mut};

use yggdrasil_rt::process::thread_local;

use crate::{
    error::{self, CIntZeroResult, ResultExt},
    headers::{errno::Errno, sys_types::pthread_key_t},
    util::PointerExt,
};

#[no_mangle]
unsafe extern "C" fn pthread_key_create(
    key: *mut pthread_key_t,
    _destructor: extern "C" fn(*mut c_void),
) -> CIntZeroResult {
    let key = key.ensure_mut();
    let dtv = thread_local::get_dtv();
    *key = dtv.new_specific();
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_key_delete(_key: pthread_key_t) -> CIntZeroResult {
    log::error!("TODO: pthread_key_delete()");
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_getspecific(key: pthread_key_t) -> *mut c_void {
    let dtv = thread_local::get_dtv();
    if key == 0 {
        error::set_errno(Errno::EINVAL);
        return null_mut();
    }
    match dtv.get_specific(key) {
        Some(value) => value,
        None => {
            error::set_errno(Errno::EINVAL);
            null_mut()
        }
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> CIntZeroResult {
    let dtv = thread_local::get_dtv();
    dtv.try_set_specific(key, value, false)
        .e_map_err(|_| Errno::EINVAL)?;
    CIntZeroResult::SUCCESS
}
