#pragma once

#include <BAN/Errors.h>
#include <kernel/Memory/Types.h>
#include <kernel/SpinLock.h>

namespace Kernel
{

	class PageTable
	{
	public:
		using flags_t = uint8_t;
		enum Flags : flags_t 
		{
			Present = 1,
			ReadWrite = 2,
			UserSupervisor = 4,
		};

	public:
		static void initialize();

		static PageTable& kernel();
		static PageTable& current();

		static BAN::ErrorOr<PageTable*> create_userspace();
		~PageTable();

		void identity_map_page(paddr_t, flags_t);
		void identity_map_range(paddr_t, size_t bytes, flags_t);

		void unmap_page(vaddr_t);
		void unmap_range(vaddr_t, size_t bytes);

		void map_range_at(paddr_t, vaddr_t, size_t, flags_t);
		void map_page_at(paddr_t, vaddr_t, flags_t);

		paddr_t physical_address_of(vaddr_t) const;
		flags_t get_page_flags(vaddr_t) const;

		bool is_page_free(vaddr_t) const;
		bool is_range_free(vaddr_t, size_t bytes) const;

		vaddr_t get_free_page() const;
		vaddr_t get_free_contiguous_pages(size_t page_count, vaddr_t first_address = PAGE_SIZE) const;

		void invalidate(vaddr_t);
		void load();

		void lock() const { m_lock.lock(); }
		void unlock() const { m_lock.unlock(); }

		void debug_dump();

	private:
		PageTable() = default;
		uint64_t get_page_data(vaddr_t) const;
		void initialize_kernel();

	private:
		paddr_t						m_highest_paging_struct { 0 };
		mutable RecursiveSpinLock	m_lock;
	};

}