use crate::ffi::CStr;
use crate::io;
use crate::num::NonZeroUsize;
use crate::sys::cvt_io;
use crate::time::Duration;

use yggdrasil_rt::{process::ThreadSpawnOptions, sys};

#[repr(C)]
pub struct Thread {
    id: u32,
}

pub const DEFAULT_MIN_STACK_SIZE: usize = 8192;

impl Thread {
    pub unsafe fn new(stack_size: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
        // Allocate the stack
        let (stack_bottom, _, _) = Vec::into_raw_parts(vec![0u8; stack_size]);
        let stack_bottom = stack_bottom.addr();
        let stack_top = stack_bottom + stack_size - 8;

        // Get the "argument"
        let argument = Box::into_raw(Box::new(p)).addr() as u64;

        let options = ThreadSpawnOptions { entry: Thread::entry, argument, stack_top };
        let id = cvt_io(sys::spawn_thread(&options))?;

        Ok(Thread { id })
    }

    pub fn yield_now() {
        unsafe {
            yggdrasil_rt::sys::debug_trace("Thread::yield_now()");
        }
    }

    pub fn set_name(_name: &CStr) {
        unsafe {
            yggdrasil_rt::sys::debug_trace("Thread::set_name()");
        }
    }

    pub fn sleep(dur: Duration) {
        unsafe { yggdrasil_rt::sys::nanosleep(dur) }
    }

    pub fn join(self) {
        unsafe {
            yggdrasil_rt::sys::debug_trace("Thread::join()");
        }
        loop {}
    }

    extern "C" fn entry(arg: u64) -> ! {
        use yggdrasil_rt::{debug_trace, process::ExitCode, sys};

        unsafe {
            #[allow(fuzzy_provenance_casts)]
            let p: Box<Box<dyn FnOnce()>> = Box::from_raw(arg as *mut _);

            debug_trace!("Execute thread");
            p();
            debug_trace!("Thread finish");

            sys::exit_thread(ExitCode::Exited(0));
        }
    }
}

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    unsafe {
        yggdrasil_rt::sys::debug_trace("thread::available_parallelism()");
    }
    loop {}
}

pub mod guard {
    pub type Guard = !;

    pub unsafe fn current() -> Option<Guard> {
        None
    }

    pub unsafe fn init() -> Option<Guard> {
        None
    }
}
