#pragma once
#include "fs/VFS.h"
#include "thread/Thread.h"

namespace Scheduler
{
    void init();

    Thread* current();
    Thread* idle();
    Thread* init_thread();

    void set_reap_thread(Thread*);
    void signal_reap_thread();

    void set_oom_thread(Thread*);
    void signal_oom_thread();

    Result<Thread*> new_kernel_thread(u64 address, const char* name);
    Result<Thread*> new_kernel_thread(void (*func)(void), const char* name);
    Result<Thread*> new_kernel_thread(void (*func)(void*), void* arg, const char* name);

    Result<Thread*> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name);

    void add_thread(Thread* thread);

    Thread* pick_task();

    void reap_thread(Thread* thread);

    void switch_task(Registers* regs);

    void invoke(Registers* regs);

    LinkedList<Thread> check_for_dying_threads();

    Option<Thread*> find_by_pid(pid_t pid);

    template <typename Callback> void for_each_child(Thread* thread, Callback callback)
    {
        for (Thread* current = thread; current; current = g_threads.next(current).value_or(nullptr))
        {
            if (current->parent == thread)
            {
                bool should_continue = callback(current);
                if (!should_continue) return;
            }
        }
    }

    template <typename Callback> void for_each_in_process_group(pid_t group, Callback callback)
    {
        for (Thread* current = g_threads.first().value_or(nullptr); current;
             current = g_threads.next(current).value_or(nullptr))
        {
            if (current->pgid == group)
            {
                bool should_continue = callback(current);
                if (!should_continue) return;
            }
        }
    }

    void dump_state();

    bool has_children(Thread* thread);

    Option<Thread*> find_exited_child(Thread* thread);
}

extern "C" void kernel_yield();
void kernel_wait(pid_t pid);
void kernel_sleep(u64 ms);
[[noreturn]] void kernel_exit();

// Freezes the current thread until someone else calls wake_up() on this thread.
void kernel_wait_for_event();