#pragma once
#include "arch/MMU.h"
#include <luna/Result.h>
#include <luna/String.h>
#include <luna/Types.h>

namespace MemoryManager
{
    constexpr int DEFAULT_ACCESS = 0;

    void init();

    Result<void> protect_kernel_sections();

    Result<u64> alloc_frame();
    Result<u64> alloc_zeroed_frame();
    Result<void> free_frame(u64 frame);
    Result<void> free_frames(u64 address, usize count);

    void lock_frame(u64 frame);
    void lock_frames(u64 frames, usize count);

    Result<void> remap(u64 address, usize count, int flags);
    Result<void> remap_unaligned(u64 address, usize count, int flags);

    bool validate_page_access(u64 address, int flags);
    bool validate_page_default_access(u64 address);

    Result<String> strdup_from_user(u64 address);

    bool validate_access(const void* mem, usize size, int flags);

    inline bool validate_user_write(void* user, usize size)
    {
        return validate_access(user, size, MMU::ReadWrite | MMU::User);
    }

    inline bool validate_user_read(const void* user, usize size)
    {
        return validate_access(user, size, MMU::User);
    }

    template <typename T> bool validate_user_write_typed(T* user)
    {
        return validate_user_write(user, sizeof(T));
    }

    template <typename T> bool validate_user_read_typed(const T* user)
    {
        return validate_user_read(user, sizeof(T));
    }

    bool copy_to_user(void* user, const void* kernel, usize size);

    template <typename T> bool copy_to_user_typed(T* user, const T* kernel)
    {
        return copy_to_user(user, kernel, sizeof(T));
    }

    bool copy_from_user(const void* user, void* kernel, usize size);

    template <typename T> bool copy_from_user_typed(const T* user, T* kernel)
    {
        return copy_from_user(user, kernel, sizeof(T));
    }

    Result<void> map_frames_at(u64 virt, u64 phys, usize count, int flags);
    Result<void> map_huge_frames_at(u64 virt, u64 phys, usize count, int flags);

    Result<u64> alloc_at(u64 virt, usize count, int flags);
    Result<u64> alloc_at_zeroed(u64, usize count, int flags);
    Result<u64> alloc_for_kernel(usize count, int flags);

    Result<u64> get_kernel_mapping_for_frames(u64 phys, usize count, int flags);

    Result<void> unmap_owned(u64 virt, usize count);
    Result<void> unmap_owned_and_free_vm(u64 virt, usize count);
    Result<void> unmap_weak(u64 virt, usize count);
    Result<void> unmap_weak_and_free_vm(u64 virt, usize count);

    Result<void> unmap_weak_huge(u64 virt, usize count);

    usize free();
    usize used();
    usize reserved();
    usize total();
}