#include "./memory_smgr_init.h"

#ifdef __cplusplus
extern "C" {
#endif

void mem_smgr_init_constructor(memory_smgr_init_t *mem_smgr)
{
	mem_smgr->total_kb = 1024; //We assume nobody is using less than 16MB RAM past the year 2010. So I can
	//afford to take up the first MB withut any problems.
	mem_smgr->total_pages = (mem_smgr->total_kb * 1024) / 4096;	//How many pages is that?
	for (int i=0; i<(mem_smgr->total_pages/8); i++)		//Set the bitmap to all blank.
	{
		mem_smgr->page_map[i] = 0x0;
	};
	
	mem_smgr->destructor = &mem_smgr_init_destructor;
	mem_smgr->rm_malloc = &rm_malloc_f;
	mem_smgr->find_free_pages = &find_free_pages_f;
	mem_smgr->set_page_used = &set_page_used_f;
	mem_smgr->set_page_unused = &set_page_unused_f;
	mem_smgr->bytes_to_pages = &bytes_to_pages_f;
	mem_smgr->addr_to_page = &addr_to_page_f;
	
	/* Note that I'm NOT mapping the bootloader mem (0x7C00-0x7DFF) as used. We're overwriting it.
	 * Next segment mapped as used will be 0x9FC00-0xFFFFF 
	 * That encompasses the BDA & the VidMem Framebuffer, and anything else I didn't care to
	 * remember.
	**/
	mem_smgr->set_page_used(0, mem_smgr);		//Map IVT & BDA used.
	for (int i=mem_smgr->addr_to_page(0x9FC00); i<(mem_smgr->addr_to_page(0xFFFFF)); i++)
	{
		mem_smgr->set_page_used(i, mem_smgr);
	};
};

void mem_smgr_init_destructor(void)
{	/* Nothing to do really. If I feel like it, I could clear the bitmap to all zeros, I guess. */
};

void *rm_malloc_f(size_t size, memory_smgr_init_t *mem_smgr)
{
	/* Very rudimentary: simply hands over the next free page and exits. */
	uarch_t free_page = mem_smgr->find_free_pages(size, mem_smgr);
	if (free_page)
	{
		if (mem_smgr->bytes_to_pages(size) == 1)
		{	mem_smgr->set_page_used(free_page, mem_smgr);
		};
		for(int i=free_page; i<(free_page + mem_smgr->bytes_to_pages(size)); i++)
		{	mem_smgr->set_page_used(i, mem_smgr);
		};
	}
	else
	{
		//We have a BIG problem.
	};
	return (void *) (free_page * 4096);
};

uarch_t find_free_pages_f(size_t size, memory_smgr_init_t *mem_smgr)
{
	/*Pseudocode:
	* 1. find out how many free pages must be found.
	* 2. search for a set of contiguous free pages that fit it.
	* 3. Return the address of the starting page.
	*/
	uarch_t n_pgs = mem_smgr->bytes_to_pages(size);
	int j=0;
	for (int i=0; i<(mem_smgr->total_pages); i++)
	{
		if(!mem_smgr->page_is_used(i, mem_smgr))
		{
			for(j=i; j<(n_pgs+i); j++)
			{
				if(mem_smgr->page_is_used(j, mem_smgr)) break;
				if((j == ((n_pgs+i) - 1)) && (!mem_smgr->page_is_used(j, mem_smgr)))
				{	return i;
				};
			};
			/*If we're still here, it means that the area of free pages we found wasn't
			* large enough. Increment i by the number of pages we searched contiguously,
			* so the search can continue where the contiguous search left off.
			*/
			i = j;
		};
	};
	return 0; //Note well that we always map the first page (pg 0) as used, so this is safe.
};

void set_page_used_f(uarch_t which, memory_smgr_init_t *mem_smgr)
{
	mem_smgr->page_map[_I] |= (1<<(which % 8));
};

void set_page_unused_f(uarch_t which, memory_smgr_init_t *mem_smgr)
{
	mem_smgr->page_map[_I] &= ~(1<<(which % 8));
};

uarch_t page_is_used_f(uarch_t which, memory_smgr_init_t *mem_smgr)
{
	return (mem_smgr->page_map[_I] & (1<<(which % 8)));
};

uarch_t bytes_to_pages_f(uarch_t size)
{
	if (size <= 0) return 0;
	if (size < 4096) return 1;
	return ((size / 4096) + (((size % 4096)>0)? 1:0));
};

uarch_t addr_to_page_f(uarch_t addr)
{
	return addr / 4096;
};

#ifdef __cplusplus
}
#endif

