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

use crate::{
    error::{self, CIntZeroResult, CResult, OptionExt},
    headers::{
        errno::Errno,
        pthread::{PTHREAD_CREATE_JOINABLE, PTHREAD_INHERIT_SCHED, PTHREAD_SCOPE_PROCESS},
        sched::{__ygg_sched_param_t, SCHED_RR},
        sys_time::timespec,
        sys_types::pthread_attr_t,
    },
    thread,
    util::PointerExt,
};

#[no_mangle]
unsafe extern "C" fn pthread_attr_destroy(_attr: *mut pthread_attr_t) -> c_int {
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getdetachstate(
    attr: *const pthread_attr_t,
    state: *mut c_int,
) -> c_int {
    let _attr = attr.ensure();
    if let Some(state) = NonNull::new(state) {
        state.write(PTHREAD_CREATE_JOINABLE);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getguardsize(
    attr: *const pthread_attr_t,
    size: *mut usize,
) -> c_int {
    let _attr = attr.ensure();
    if let Some(size) = NonNull::new(size) {
        size.write(0);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getinheritsched(
    attr: *const pthread_attr_t,
    inherit: *mut c_int,
) -> c_int {
    let _attr = attr.ensure();
    if let Some(inherit) = NonNull::new(inherit) {
        inherit.write(PTHREAD_INHERIT_SCHED);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getschedparam(
    attr: *const pthread_attr_t,
    param: *mut __ygg_sched_param_t,
) -> c_int {
    let _attr = attr.ensure();
    if let Some(param) = param.as_mut() {
        param.sched_priority = 0;
        param.sched_ss_max_repl = 0;
        param.sched_ss_repl_period = timespec::zero();
        param.sched_ss_init_budget = timespec::zero();
        param.sched_ss_low_priority = 0;
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getschedpolicy(
    attr: *const pthread_attr_t,
    policy: *mut c_int,
) -> c_int {
    let _attr = attr.ensure();
    if let Some(policy) = NonNull::new(policy) {
        policy.write(SCHED_RR);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getscope(
    attr: *const pthread_attr_t,
    scope: *mut c_int,
) -> c_int {
    let _attr = attr.ensure();
    if let Some(scope) = NonNull::new(scope) {
        scope.write(PTHREAD_SCOPE_PROCESS);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getstack(
    attr: *const pthread_attr_t,
    stack: *mut *mut c_void,
    size: *mut usize,
) -> c_int {
    let attr = attr.ensure();
    if let Some(stack) = NonNull::new(stack) {
        match attr.stack {
            Some(ptr) => stack.write(ptr.as_ptr()),
            None => stack.write(null_mut()),
        }
    }
    if let Some(size) = NonNull::new(size) {
        size.write(attr.stack_size);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_getstacksize(
    attr: *const pthread_attr_t,
    size: *mut usize,
) -> c_int {
    let attr = attr.ensure();
    if let Some(size) = NonNull::new(size) {
        size.write(attr.stack_size);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int {
    let attr = attr.ensure_mut();
    *attr = pthread_attr_t {
        stack_size: thread::DEFAULT_STACK_SIZE,
        stack: None,
    };
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setdetachstate(
    _attr: *mut pthread_attr_t,
    _detach: c_int,
) -> c_int {
    todo!("pthread_attr_setdetachstate()")
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setguardsize(_attr: *mut pthread_attr_t, _size: usize) -> c_int {
    log::error!("TODO: pthread_attr_setguardsize()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setinheritsched(
    _attr: *mut pthread_attr_t,
    _inherit: c_int,
) -> c_int {
    log::error!("TODO: pthread_attr_setinheritsched()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setschedparam(
    _attr: *mut pthread_attr_t,
    _param: *const __ygg_sched_param_t,
) -> c_int {
    log::error!("TODO: pthread_attr_setschedparam()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setschedpolicy(
    _attr: *mut pthread_attr_t,
    _policy: c_int,
) -> c_int {
    log::error!("TODO: pthread_attr_setschedpolicy()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setscope(_attr: *mut pthread_attr_t, _scope: c_int) -> c_int {
    log::error!("TODO: pthread_attr_setscope()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setstack(
    attr: *mut pthread_attr_t,
    stack: *mut c_void,
    size: usize,
) -> CIntZeroResult {
    let attr = attr.ensure_mut();
    let stack = NonNull::new(stack).e_ok_or(Errno::EINVAL)?;
    if size >= thread::MIN_STACK_SIZE {
        attr.stack = Some(stack);
        attr.stack_size = size;
        CIntZeroResult::SUCCESS
    } else {
        error::set_errno(Errno::EINVAL);
        CIntZeroResult::ERROR
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_attr_setstacksize(
    attr: *mut pthread_attr_t,
    size: usize,
) -> CIntZeroResult {
    let attr = attr.ensure_mut();
    if size >= thread::MIN_STACK_SIZE {
        attr.stack_size = size;
        CIntZeroResult::SUCCESS
    } else {
        error::set_errno(Errno::EINVAL);
        CIntZeroResult::ERROR
    }
}
