#include "memory.h"
#include "process.h"
#include "irq.h"
#include "fileobj.h"

// AMS linkage
void lowlevel_yield();

unsigned short last_pid = 0;

struct context *cur_cx;

struct context kernel_cx;

struct process_item *cur_proc;
int num_yielding = 0;
int num_processes = 0;

void context_init(struct context *cx) {
	memset(cx, 0, sizeof(struct context));

	cx->pd = create_pd();
	cx->valloc_addr = (unsigned int)VIRTUAL_HEAP_START;
	cx->heap_top = (unsigned int)VIRTUAL_HEAP_START; // start at 1GB mark
}

void switch_context(struct context *cx) {
	cur_cx = cx;
	
	set_cr3(cur_cx->pd);
}

void switch_process(struct process_item *proc) {
	cur_proc = proc;
	to_process_context();
}

void to_kernel_context() {
	switch_context(&kernel_cx);
}

void to_process_context() {
	switch_context(&cur_proc->cx);
}

void next_process() {
	cur_proc->timeleft = cur_proc->timeslice;
//	do {
		switch_process(cur_proc->next);
//	} while (cur_proc->timeleft == 0);
//	printf(" * ");
}

// Cooperative multitasking inside kernel
// kernel_yield() should be called while waiting for IO etc
void kernel_yield() {
	num_yielding++;
//	printf("%d %d\n", num_yielding, num_processes);
	lowlevel_yield();
	num_yielding--;
}

// Timer interrupt
void interrupt_32() {
	if (--cur_proc->timeleft <= 0) {
		next_process();
	}
	ACK_MASTER_IRQ;
}

int food = 3;

void sys_next_process() {
	switch_process(cur_proc->next);
}

void sys_exit() {
	
}


/*%macro push_regs 0
	pusha  ; EAX, ECX, EDX, EBX, ESP, EBP, ESI and EDI
	push gs
	push fs
	push es
	push ds
%endmacro*/


void process_init(struct process_item *proc, unsigned int entry) {
	memset(proc, 0, sizeof(struct process_item));
	proc->timeleft = proc->timeslice = 1;
	proc->entry = entry;
	proc->pid = ++last_pid;
	proc->owner = 0;
}

void internal_init_process(struct process_item *proc) {
	proc->timeleft = proc->timeslice = 10;

	// Process stack starts at right below PD
	proc->cx.esp = 0xFFFFFFFF - (1024 * 4096);

	// Switch to the process PD for a while to create and init the stack

	switch_context(&proc->cx);
	
	// Some stack space
	void *stack = alloc_page();
	map_page(stack, (void *)(0xFFFFFFFF - (1024 * 4096) - 4096 + 1));
	
	unsigned int *esp = (unsigned int *)proc->cx.esp;

	// Init stack (these are for iret)
	*--esp = 0x0202; // flags
	*--esp = 0x08; // code segment
//	*--esp = 0x18; // user code segment
	*--esp = proc->entry; // eip (entry point)

	// (mimic push_regs)
	*--esp = 0x00; // eax
	*--esp = 0x00; // ecx
	*--esp = 0x00; // edx
	*--esp = 0x00; // ebx
	*--esp = proc->cx.esp; // esp
	*--esp = 0x00; // ebp
	*--esp = 0x00; // esi
	*--esp = 0x00; // edi

	*--esp = 0x10; // gs
	*--esp = 0x10; // fd
	*--esp = 0x10; // es
	*--esp = 0x10; // ds
	
	proc->cx.esp = (unsigned int)esp;

	// Allocate some space on the heap for the process struct
	
	proc->proc = (struct process *)kmalloc(sizeof(struct process));
	
	memset(&proc->proc->fd_table, 0, sizeof(struct file_object *) * 10);
	proc->proc->n_fds = 0;

	// Switch back to current PD
	
	switch_context(cur_cx);

}

/*void shoop(int da_woop) {
	int lazorz;
	printf("Imma firing my 0x%x\n", lazorz);
}*/

void sys_start_process(struct process_item *proc) {
	printf("Hajimemashite, SUTAATO PUROSESSU desu~!\n");
//	shoop(123);
	context_init(&proc->cx);

//	sti();
	internal_init_process(proc);
//	cli();

//	printf("Um...\n");

//	if (food++ > 0) { panic("Halt!"); }
	proc->next = cur_proc->next;
	proc->prev = cur_proc;
	
	cur_proc->next->prev = proc;
	cur_proc->next = proc;

	num_processes++;
}

void sys_kill_process(struct process_item *proc) {
	printf("This would kill a process but... no.");
}
