#pragma once

#include <stdint.h>

//in the lower half, memory is only ever mapped below 0x0080.0000.0000. this is
//one p3's worth. the p4 and p3 in app_memory are always real, but p3 may have
//zero entries indicating p2's that aren't needed. similarly, the p2's may have
//zero entries indicating p1's that aren't needed.

namespace hilbert::kernel {

  class app_memory {

    typedef uint64_t *v_page_table;

    v_page_table p4;
    v_page_table p3;
    v_page_table p2s[512];
    v_page_table *p1s[512];

    bool **pram_pages_to_free_on_exit[512];

  public:
    uint64_t p4_paddr;

    app_memory();
    ~app_memory();

    //vaddr and paddr must be page aligned.
    //vaddr must be < 0x0080.0000.0000.
    void map_page(
      uint64_t vaddr, uint64_t paddr, bool write,
      bool execute, bool free_pram_on_exit);

    //also frees the pram if marked in pram_pages_to_free_on_exit
    void unmap_page(uint64_t vaddr);

    bool valid_to_read(
      uint64_t vaddr_start, uint64_t vaddr_end, bool and_write) const;

    inline bool valid_to_read(
      const void *vaddr_start, const void *vaddr_end, bool and_write) const {
      return valid_to_read(
        (uint64_t)vaddr_start, (uint64_t)vaddr_end, and_write);
    }

    //pages are together; returns start of first page.
    //only looks in range [0x0000.0000.1000, 0x0040.0000.0000)
    uint64_t get_free_vaddr_pages(uint64_t count);

    //returns top of stack
    uint64_t map_new_stack();
    //also frees pram pages
    void unmap_stack(uint64_t top);

    //in lower half
    uint64_t count_mapped_vram_pages() const;

  };

}
