//! Multitasking and process/thread management interfaces

#![allow(dead_code)]

use abi::error::Error;
use alloc::{string::String, vec::Vec};
use kernel_arch::{
    task::{Scheduler, TaskContext, Termination},
    Architecture, ArchitectureImpl,
};
use libk::{
    arch::Cpu,
    task::{
        runtime,
        sched::{init_queues, CpuQueue},
        thread::Thread,
        TaskContextImpl,
    },
};
use libk_util::sync::SpinFence;

/// Creates a new kernel-space process to execute a closure and queues it to some CPU
pub fn spawn_kernel_closure<S: Into<String>, T: Termination, F: Fn() -> T + Send + 'static>(
    name: S,
    f: F,
) -> Result<(), Error> {
    let thread = Thread::new_kthread(
        name,
        TaskContextImpl::kernel_closure(move || {
            let result = f();
            Thread::current().exit(result.into_exit_code());
        })?,
    );
    thread.enqueue();

    Ok(())
}

/// Sets up CPU queues and gives them some processes to run
pub fn init() -> Result<(), Error> {
    let cpu_count = ArchitectureImpl::cpu_count();

    // Create a queue for each CPU
    init_queues(Vec::from_iter((0..cpu_count).map(CpuQueue::new)));

    // Spawn async workers
    (0..cpu_count).for_each(|index| {
        log::debug!("Spawn async worker #{}", index);
        runtime::spawn_async_worker(index).unwrap();
    });

    Ok(())
}

/// Sets up the local CPU queue and switches to some task in it for execution.
///
/// # Note
///
/// Any locks held at this point will not be dropped properly, which may lead to a deadlock.
///
/// # Safety
///
/// Only safe to call once at the end of non-threaded system initialization.
pub unsafe fn enter() -> ! {
    static AP_CAN_ENTER: SpinFence = SpinFence::new();

    let mut cpu = Cpu::local();

    if !cpu.is_bootstrap() {
        // Wait until BSP allows us to enter
        AP_CAN_ENTER.wait_one();
    } else {
        AP_CAN_ENTER.signal();
    }

    let queue = CpuQueue::for_cpu(cpu.queue_index());
    cpu.set_scheduler(queue);

    queue.enter()
}
