#include <mem_manage.h>
#include <boot_info.h>
#include <video.h>
//bootstrap memory management
void fill_info_base();

struct mem_info * info_base; 
size_t info_base_len = 18;

struct mem_info * free_mem_head = 0;

struct mem_info * used_mem_head = 0;

extern char end;
extern u_int64_t page_size;

//Initialize the free memeory stacks
void init_mm()
{
	free_mem_head = (struct mem_info *) &end;
	used_mem_head = ((struct mem_info *) &end) + 1;
	info_base = ((struct mem_info *)&end) + 2;
	info_base_len = 18;
	/*the kernel will be moved in the future, when that happens, these change*/
	free_mem_head->free = 1;
	free_mem_head->start = &end + sizeof(struct mem_info) * 20;
	free_mem_head->length = (40 * 1024 * 1024) - (sizeof(struct mem_info) * 20) - ((u_int64_t)&end);
	free_mem_head->next = 0;
	free_mem_head->prev = used_mem_head;
	free_mem_head->stk_next = 0;

	used_mem_head->free = 0;
	used_mem_head->start = &end;
	used_mem_head->length = sizeof(struct mem_info) * 20;
	used_mem_head->next = free_mem_head;
	used_mem_head->prev = 0;
	used_mem_head->stk_next = 0;
}

//allocate memory
void * alloc(size_t len)
{
	if(info_base_len = 0)
	{
		fill_info_base();
	}
}

//find more memory for the stacks if we run out
void fill_info_base()
{
	struct mem_info * new_info;
	if(info_base_len != 0)
		return;
	new_info = find_free_mem_info;
}

//find a free piece of memory with at least min_len
struct mem_info* find_free_mem_info(size_t min_len)
{

	struct mem_info * curr;
	struct mem_info * prev;
	for(curr = free_mem_head; curr != 0 && curr->length < min_len; prev = curr, curr = curr->stk_next);
	if(curr == 0)
	{
		alloc_page(min_len / page_size + ((min_len % page_size > 0) ? 1 : 0));
		prev = 0;
		curr = free_mem_head;
	}
	/*by the time we reach here, we have allocated a new page or we have a structure that we can carve the info out of*/

	return curr;
}

//move a memory structure onto the free stack
void push_free_mem(struct mem_info * info)
{
	info->stk_next = free_mem_head;
	free_mem_head = info;
}

//move a memory structure onto the used stack
void push_used_mem(struct mem_info * info)
{
	info->stk_next = used_mem_head;
	used_mem_head = info;
}

//called when a page needs to be allocated
void * alloc_page(size_t length)
{
	//not implemented
	panic("kernel panic, memory page management not implemented");
}

extern struct page * kernel_page;

/*
int chk_page_addr_r(u_int64_t addr, struct page * tbl,int depth)
{
	if((depth == 1 && tbl->pat) || (depth == 0))
	{
		return tbl->present + depth;
	}
	
	int i = (addr & (0x1FF << ((depth - 1) * 9 + 12))) >> ((depth - 1) * 9 + 12);
	if (tbl[i].present)
	{
		return chk_page_addr_r(addr,(struct page * )(((int64_t)tbl[i].ptr) << 12), depth - 1);
	}
	return depth;
}
int chk_page_addr(u_int64_t addr)
{
	int lvl1_i = (addr >> 12) & 0x1FF;
	int lvl2_i = (addr >> 21) & 0x1FF;
	int lvl3_i = (addr >> 30) & 0x1FF;
	int lvl4_i = (addr >> 39) & 0x1FF;
	u_int64_t *curr = kernel_page;

	cons_writes("page map size: ");
	write_numd(sizeof(struct page));
	cons_writes("\n");

	cons_writes("checking ");
	write_numh(addr);
	cons_writes(" level indexes:\n");
	write_numd(lvl4_i);
	cons_writes("\n");
	write_numd(lvl3_i);
	cons_writes("\n");
	write_numd(lvl2_i);
	cons_writes("\n");
	write_numd(lvl1_i);
	cons_writes("\n");

	if(curr[lvl4_i] & 1 == 0)
		return 4;
	curr = (u_int64_t)curr[lvl4_i] & 0x000FFFFFFFFFF000;

	if(curr[lvl3_i] & 1 == 0)
		return 3;
	curr = curr[lvl3_i] & 0x000FFFFFFFFFF000;

	if(curr[lvl2_i] & 1 == 0 || (curr[lvl2_i] & 0x80) )
		return 2;
	curr = curr[lvl2_i] & 0x000FFFFFFFFFF000;

	return curr[lvl1_i] & 1;
}
*/
void print_mmap_info()
{
	if(bootloader_info->flags & 0x40)
	{
		int i;
		if(bootloader_info->mmap_length > 0)
		{
			cons_writes("\nmemory maps: ");
			write_numd(bootloader_info->mmap_length);
			struct memory_map * mmap = (struct memory_map *)((u_int64_t)bootloader_info->mmap_addr);
			for(i = 0; i < bootloader_info->mmap_length;mmap = mmap + *(((int*)mmap)-1))
			{
				cons_writes("\nbase address: ");
				write_numh(mmap->base);
				cons_writes("\nlength: ");
				write_numd(mmap->length);
				cons_writes("\ntype: ");
				write_numd(mmap->type);
			}
			cons_writes("\ndone\n");
		}
		else
		{
			cons_writes("\nmemory map has zero length\n");
			/*
			write_numh(bootloader_info->mmap_length);
			cons_writes("\n");
			write_numh(bootloader_info->mmap_addr);
			*/
		}
	}
	else
	{
		cons_writes("No memory map info");
	}
}
