#include "Log.h"
#include "arch/CPU.h"
#include "arch/Timer.h"
#include "boot/Init.h"
#include "config.h"
#include "fs/InitRD.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/tmpfs/FileSystem.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
#include <luna/Units.h>

#ifdef ARCH_X86_64
#include "arch/x86_64/disk/ATA.h"
#endif

extern void set_host_name(StringView);

void reap_thread()
{
    while (true)
    {
        CPU::disable_interrupts();
        auto dying_threads = Scheduler::check_for_dying_threads();
        CPU::enable_interrupts();

        dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });

        kernel_wait_for_event();
    }
}

[[noreturn]] void init()
{
    kinfoln("Starting Moon %s, built on %s at %s", MOON_VERSION, __DATE__, __TIME__);

    // Default hostname if nobody from userspace changes it
    set_host_name("moon"_sv);

    kinfoln("Current platform: %s", CPU::platform_string().chars());
    kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"_sv).chars());

    auto root = mark_critical(TmpFS::FileSystem::create(), "Failed to create initial ramfs");
    mark_critical(VFS::mount_root(root), "Failed to mount the initial ramfs as the root filesystem");
    mark_critical(InitRD::populate_vfs(), "Failed to load files from the initial ramdisk");
    mark_critical(DeviceRegistry::init(), "Failed to register initial devices");

    auto init = mark_critical(VFS::resolve_path("/bin/init", Credentials {}), "Can't find init in the initial ramfs!");
    auto init_thread =
        mark_critical(Scheduler::new_userspace_thread(init, "/bin/init"), "Failed to create PID 1 process for init");

    auto reap = mark_critical(Scheduler::new_kernel_thread(reap_thread, "[reap]"),
                              "Failed to create the process reaper kernel thread");
    Scheduler::set_reap_thread(reap);

#ifdef ARCH_X86_64
    ATA::Controller::scan();
#endif

    // Disable console logging before transferring control to userspace.
    setup_log(log_debug_enabled(), log_serial_enabled(), false);

    init_thread->wake_up();

    kernel_exit();
}

extern "C" [[noreturn]] void _start()
{
    Init::check_magic();
    Init::early_init();

    Timer::init();

    Thread::init();
    Scheduler::init();

    Scheduler::new_kernel_thread(init, "[kinit]");

    CPU::platform_finish_init();

    CPU::enable_interrupts();

    CPU::idle_loop();
}