#include "clib.h"
#include "i386.h"
#include "systab.h"
#include "sched.h"
#include "phmem.h"
#include "kmalloc.h"
#include "paging.h"

#define tasks    (tasks_list)

tss_t *tasks_list[NR_TASK];

tss_t* sel2tss(selector_t seltss)
{
	descriptor_t d;
        if (get_gdt_descriptor(seltss shr 3, &d) == 0)
        	return (tss_t*)get_descriptor_base(&d);
        else {
        	WARNING("return NULL");
                return NULL;
        }
}


tss_t* alloc_tss(tss_t* tss, const char* name)
{
	int i;

        for (i = 0; i < NR_TASK; i++) {
		if (tasks_list[i] == NULL) {
                	descriptor_t d;
                        int di;
                        int lim;
                        di = alloc_gdt_descriptor();
                        if (di == 0) goto _ret;

                        if (tss == NULL) {
                                tss = kmalloc(sizeof(tss_t));
                                tss->limit = sizeof(tss_t) - 1;
                        }
                        tasks_list[i] = tss;
                        lim = tss->limit;

                	memset(tss, 0, sizeof(tss_t)); // CANCELLA TSS
                        tss->index = i;
                        tss->tss_sel = di shl 3;
                        tss->limit = lim;
                        strncpy((char*)tss->name, name, MAX_TASK_NAME_LENGTH);

                        set_system_descriptor(&d, (dword)tss, tss->limit,
                        	SDT_A386TSS + SDT_PRESENT);

                        set_gdt_descriptor(di, &d);
			return tss;
                }
        }
_ret:
	WARNING("return NULL");
        return NULL;
}

void free_tss(tss_t* tss)
{
        ASSERT(tss != NULL);
        if (tss->index > 0 && tss->index < NR_TASK) {
        	tasks_list[tss->index] = NULL;

                /* free CR3 */
                free_task_pgtable(tss);

                /* free tls */
                if (tss->tls) {
                	kfree(tss->tls);
                }
                /* free LDT */
                if (tss->ldt_sel != 0) {
                	kfree(tss->ldt.table);
                        free_gdt_descriptor(tss->ldt_sel shr 3);
                }
                /* free TSS */
                free_gdt_descriptor(tss->tss_sel shr 3);
                kfree(tss);
        } else WARNING("invalid TSS");
}

void sched_setup(void)
{
        memset(tasks_list, 0, sizeof(tasks_list));
}

void task_dump(selector_t tss_sel)
{
        descriptor_t d;
        tss_t *t;

        if (get_gdt_descriptor(tss_sel shr 3, &d) != 0) goto _inval;

       	t = (tss_t*) get_descriptor_base(&d);

        if (t) {
                	kprintf("task `%s' dump (index %d dec):\n"
                               "tr %04x ldt %04x cr3 %08x\n"
                               "cs:eip %04x:%08x ss:esp %04x:%08x\n"
                               "ss0:esp0 %04x:%08x eflags %08x\n"
                               "ds %04x es %04x fs %04x gs %04x\n"
                               "eax %08x ebx %08x edx %08x ecx %08x\n"
                               "ebp %08x esi %08x edi %08x \n"
                               "tls %08x status %xh\n",
                               t->name, t->index,
                               t->tss_sel, t->ldt_sel, t->cr3,
                               t->cs, t->eip, t->ss, t->esp,
                               t->ss0, t->esp0, t->eflags,
                               t->ds, t->es, t->fs, t->gs,
                               t->eax, t->ebx, t->edx, t->ecx,
                               t->ebp, t->esi, t->edi,
                               t->tls, t->status);

			return;

        }
_inval:
;

        kprintf("invalid tss selector %xh", tss_sel);
}


bool task_signal(tss_t *task, int signal)
{
   	if (task->handler) {
		dword buf[2];
                buf[0] = (dword)task->eip;
                buf[1] = signal;
                task->eip = task->handler;
                task->esp -= sizeof(buf);
                write_to_task(task, (addr_t)task->esp, buf, sizeof(buf));
                return true;
        } else return false;
}