#pragma once
#include "memory/AddressSpace.h"
#include "memory/Paging.h"

enum Flags
{
    ReadWrite = 1 << 0,
    User = 1 << 1,
    Execute = 1 << 2
};
namespace VMM
{
    void init(); // Fetch page table from cr3

    void switch_to_user_address_space(AddressSpace& space);
    void switch_to_previous_user_address_space();
    void switch_back_to_kernel_address_space();

    void enter_syscall_context();
    void exit_syscall_context();

    void apply_address_space();

    bool is_using_kernel_address_space();

    void map(uint64_t vaddr, uint64_t paddr, int flags);
    void remap(uint64_t vaddr, int flags);
    void unmap(uint64_t vaddr);
    uint64_t get_physical(uint64_t vaddr);
    uint64_t get_flags(uint64_t vaddr);

    PageDirectoryEntry* find_pde(PageTable* root, uint64_t vaddr);
    PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr);

    void propagate_read_write(PageTable* root, uint64_t vaddr);
    void propagate_no_execute(PageTable* root, uint64_t vaddr);
    void propagate_user(PageTable* root, uint64_t vaddr);

    void flush_tlb(uint64_t addr);

    void decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
                         uint64_t& pdp_index);
    uint64_t recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index);

    void install_kernel_page_directory_into_address_space(AddressSpace& space);
};