#include <mm/vmm.h>
#include <sys/io.h>
#include <ubixos/kpanic.h>
#include <lib/kprintf.h>
#include <lib/kmalloc.h>
#include <ubixos/vitals.h>
#include <ubixos/spinlock.h>
#include <assert.h>

int		numPages = 0;
mMap		*mmUsedPages = 0xFD000000;

uInt32 freePages = 0, usedPages = 0;
uInt32 z  = 0, memStart = 0;
uInt32 countMemory();

kTask_t *SYSTEM = 0xFC000000;

void 
mmFreeVirtualPage(kTask_t *task, uInt32 memAddr)
{
	mMap *tmp, *tmp2;

	for(tmp = task->FirstPage ; tmp != NULL ; tmp = tmp->Next)
	{
		if(tmp->pageAddr == memAddr)
		{
			freePages++;
			/* remove the page from the task's used list */
			if(tmp == task->FirstPage)
			{
				task->FirstPage = task->FirstPage->Next;
			}
			else
			{
				tmp->Previous->Next = tmp->Next;
				tmp->Next->Previous = tmp->Previous;
				tmp->Next = NULL;
				tmp->Previous = NULL;
			}
			
			/* add the page to the end free pages list */
			mmFreePages->Last->Next = tmp;
			tmp->Previous = mmFreePages->Last;
			mmFreePages->Last = tmp;

			/* free contiguous blocks */
			if(tmp->Link != NULL)
			{
				kprintf("Freeing contiguous memory\n");
				for(tmp2 = tmp->Link ; tmp2 != NULL ; tmp2 = tmp2->Link)
				{
					mmFreePages->Last->Next = tmp2;
					tmp2->Previous = mmFreePages->Last;
					mmFreePages->Last = tmp2;
				}
			}

			kprintf("mmFreeVirtualPage: %d has been freed\n", memAddr);
			return;
		}
	}
	kpanic("mmFreeVirtualPage: attempted to free non-existant page\n");
	return;
}

void *
mmGetFreeVirtualPage(kTask_t *task)
{
	mMap *tmp, *tmpMap;
	freePages--;
//	kprintf("Z = %d + sizeof %d + 1 = %d\n", z, sizeof(mMap), z + (sizeof(mMap)+1));

        if(mmUsedPages == NULL)
        {
		mmUsedPages = (z + sizeof(mMap));
                mmUsedPages->First = NULL;
                mmUsedPages->Last = NULL;
		z+=(sizeof(mMap)+1);
	}

        tmpMap = z;
        tmpMap->pageAddr = 10;
        tmpMap->physicalAddr = 10;
	tmpMap->Next = NULL;
	tmpMap->Previous = mmUsedPages->Last;
	mmUsedPages->Last = tmpMap;
	if(mmUsedPages->First == NULL)
		mmUsedPages->First = tmpMap;
	z+=(sizeof(mMap)+1);

	return tmpMap;
}

int
vmmMemMapInit()
{
	uInt32 memStart;
	mMap *tmpMap;

	_current = SYSTEM;
	/* Count System Memory */
	numPages = countMemory();
	freePages = numPages;

//	memStart = sizeof(mMap) * numPages;

	/* initialize free pages */
	//kprintf("Initializing memory memStart = [%x]\n", memStart);
	/* initialize used pages (kernel space) */

	z = 30000;
	/* Print Out Memory Information */
	kprintf("Total Memory:      %iMB\n", ((numPages  * 4096) / 1024) / 1024 );
	//kprintf("Available Memory: %iMB\n", ((freePages * 4096) / 1024) / 1024 );
	//kprintf("Used Memory:      %iMB\n", ((usedPages * 4096) / 1024) / 1024 );

	/* Return */
	return (0);
}

uInt32 countMemory()
{
	register uInt32	*mem = 0x0;
	unsigned long	memCount = -1, tempMemory = 0x0;
	unsigned short	memKb = 0;
	uInt8		irq1State, irq2State;
	unsigned long	cr0 = 0x0;

	/*
	* Save The States Of Both IRQ 1 And 2 So We Can Turn Them Off And Restore
	* Them Later
	*/
	irq1State = inportByte(0x21);
	irq2State = inportByte(0xA1);

	/* Turn Off IRQ 1 And 2 To Prevent Chances Of Faults While Examining Memory */
	outportByte(0x21, 0xFF);
	outportByte(0xA1, 0xFF);

	/* Save The State Of Register CR0 */
	asm volatile (
		"movl %%cr0, %%ebx\n"
		:               "=a" (cr0)
		:
		:               "ebx"
	);

	asm volatile ("wbinvd");
	asm volatile (
		"movl %%ebx, %%cr0\n"
		:
		:               "a" (cr0 | 0x00000001 | 0x40000000 | 0x20000000)
		:               "ebx"
	);

	while (memKb < 4096 && memCount != 0)
	{
		memKb++;
		if (memCount == -1)
			memCount = 0;

		memCount += 1024 * 1024;
		mem = (uInt32 *)memCount;
		tempMemory = *mem;
		*mem = 0x55AA55AA;
		asm("": : :"memory");

		if (*mem != 0x55AA55AA)
			memCount = 0;
		else
		{
			*mem = 0xAA55AA55;
			asm("": : :"memory");
			if (*mem != 0xAA55AA55)
				memCount = 0;
		}
		asm("": : :"memory");
		*mem = tempMemory;
	}

	asm volatile (
		"movl %%ebx, %%cr0\n"
		:
		:               "a" (cr0)
		:               "ebx"
	);

	/* Restore States For Both IRQ 1 And 2 */
	outportByte(0x21, irq1State);
	outportByte(0xA1, irq2State);

	/* Return Amount Of Memory In Pages */
	return ((memKb * 1024 * 1024) / 4096);
}
