#include "Framebuffer.h"
#include "Init.h"
#include "MemoryManager.h"
#include "arch/CPU.h"
#include "arch/MMU.h"
#include "arch/Serial.h"

extern u8 fb[1];

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

    Serial::println("Hello, world!");

    Framebuffer::rect(0, 0, 200, 200, 0xFF00FF00);

    auto cpu_name_or_error = CPU::identify();

    Serial::println(cpu_name_or_error.has_error() ? "Unable to determine CPU name" : cpu_name_or_error.release_value());

    Serial::println(MMU::get_physical((u64)fb).has_error() ? "fb is not mapped" : "fb is mapped!!");

    const u64 address = 0xfffffffff8000000;

    u64 physical = MemoryManager::alloc_physical_page().release_value();

    auto rc = MMU::map(address, physical, MMU::ReadWrite);
    bool success = !rc.has_error();

    int flags;
    volatile u8* ptr;

    if (success) Serial::println("Mapped page :)");
    else
    {
        Serial::println("Failed to map page");
        CPU::efficient_halt();
    }

    if (MMU::get_physical(address).release_value() == physical) Serial::println("Mapping is active ;)");
    else
    {
        Serial::println("Mapping is not active");
        CPU::efficient_halt();
    }

    flags = MMU::get_flags(address).release_value();

    if (flags & MMU::ReadWrite) Serial::println("Mapping is writable");
    if (flags & MMU::User) Serial::println("Mapping is user accessible");

    auto rrc = MMU::remap(address, MMU::ReadWrite | MMU::User);
    if (rrc.has_error())
    {
        Serial::println("Failed to change flags of mapping");
        CPU::efficient_halt();
    }

    flags = MMU::get_flags(address).release_value();

    if (flags & MMU::ReadWrite) Serial::println("Mapping is now writable");
    if (flags & MMU::User) Serial::println("Mapping is now user accessible");

    ptr = (volatile u8*)address;

    *ptr = 8;

    Serial::println("Can write to pointer");

    auto urc = MMU::unmap(address);
    if (urc.has_error())
    {
        Serial::println("Failed to unmap page");
        CPU::efficient_halt();
    }

    if (urc.release_value() != physical)
    {
        Serial::println("unmap returned a different address than the one we mapped");
        CPU::efficient_halt();
    }

    Serial::println("Successfully unmapped address");

    *ptr = 16;

    Serial::println("ERROR: Still here after page fault");

    CPU::efficient_halt();
}