#include "../bootpack.h"

using namespace TaskDefinitions;

// タスクスイッチ用タイマー
Timer* system::g_task_timer;


Task::Task()
	: selector_(0), flags_(FLAG_FREE), level_(0), priority_(0),
	fifo_(256)
{
}

void Task::init() {
	tss_.eflags = 0x00000202; /* IF = 1; */
	tss_.eax = 0; /* とりあえず0にしておくことにする */
	tss_.ecx = 0;
	tss_.edx = 0;
	tss_.ebx = 0;
	tss_.ebp = 0;
	tss_.esi = 0;
	tss_.edi = 0;
	tss_.es = 0;
	tss_.ds = 0;
	tss_.fs = 0;
	tss_.gs = 0;
	tss_.t = 0;
	tss_.iobase = 0x4000;
	tss_.ss0 = 0;
}

void Task::set_segmtss(int sel) {
	GDT::SetSegmDesc(GDT::gdt + sel, 103, (dword)&tss_, AR_TSS32);
}

void Task::set_segmldt(int sel) {
	GDT::SetSegmDesc(GDT::gdt + sel, 15, (dword)ldt_, AR_LDT);
}

Task* TaskLevel::current_task_ = NULL;

TaskLevel::TaskLevel()
	: running_(0), current_(0)
{
}

void TaskLevel::add(Task* task) {
	tasks_[running_] = task;
	running_++;
	task->run();
	if (current_task_ == NULL) {
		current_task_ = task;
	}
}

void TaskLevel::remove(Task* task) {
	int i;
	
	// タスクの位置を探す
	for (i = 0; i < running_; i++) {
		if (task == tasks_[i]) {
			break;
		}
	}
	
	running_--;
	
	if (i < current_) {
		current_--; // ずれを修正する
	}
	if (current_ >= running_) {
		current_ = 0; // おかしな値は修正する
	}
	
	task->stop(); // スリープ
	
	for (; i < running_; i++) {
		// ずらす
		tasks_[i] = tasks_[i + 1];
	}
	
	reset_current_task();
}

void TaskLevel::nexttask() {
	current_++;
	if (current_ == running_) {
		current_ = 0;
	}
}

void TaskLevel::task_switch() {
	Task* new_task = tasks_[current_];
	timer_settime(g_task_timer, new_task->get_priority());
	if (current_task_ != new_task) {
		current_task_ = new_task;
		driver::farjmp(0, new_task->get_selector());
	}
}

void TaskLevel::task_switch2() {
	Task* new_task = tasks_[current_];
	if (current_task_ != new_task) {
		current_task_ = new_task;
		driver::farjmp(0, new_task->get_selector());
	}
}

TaskController::TaskController()
	: tasks0_(new Task[MAX_TASKS]),
	level_(new TaskLevel[MAX_LEVELS]),
	level_changed_(false), current_level_(0)
{}

Task* TaskController::init()
{
	int i;
	for (i = 0; i < MAX_TASKS; i++) {
		tasks0_[i].release();
		tasks0_[i].set_selector((TASK_GDT0 + i) * 8);
		tasks0_[i].set_tss_ldtr((TASK_GDT0 + MAX_TASKS + i) * 8);
		tasks0_[i].set_segmtss(TASK_GDT0 + i);
		tasks0_[i].set_segmldt(TASK_GDT0 + MAX_TASKS + i);
	}
	
	// id = 0
	// level 0, priority 2 (=0.02sec)
	Task* task = alloc(1, 2);
	task->run();
	add(task);
	task_switchsub();
	load_tr(task->get_selector());
	
	// id = 1
	Task* idle = alloc(MAX_LEVELS - 1, 1);
	idle->set_tss_esp((dword)malloc(64 * 1024) + 64 * 1024);
	idle->set_tss_eip( (dword)&system::task_idle );
	run(idle);
	
	// id = 2
	Task* keyboard = alloc(0, 1);
	keyboard->set_tss_esp((dword)malloc(64 * 1024) + 64 * 1024);
	keyboard->set_tss_eip( (dword)&system::task_keyboard );
	run(keyboard);
	
	// id = 3
	Task* mouse = alloc(0, 2);
	mouse->set_tss_esp((dword)malloc(64 * 1024) + 64 * 1024);
	mouse->set_tss_eip( (dword)&system::task_mouse );
	run(mouse);
	
	// id = 4
	Task* taskbar = alloc(1, 1);
	taskbar->set_tss_esp((dword)malloc(64 * 1024) + 64 * 1024);
	taskbar->set_tss_eip( (dword)&system::task_taskbar );
	run(taskbar);
	
	g_task_timer = g_timerctl->alloc();
	timer_settime(g_task_timer, task->get_priority());
	
	return task;
}

TaskController::~TaskController() {
	delete[] tasks0_;
	delete[] level_;
}

Task* TaskController::alloc(int level, int priority) {
	Task* task = NULL;
	int i;
	for (i = 0; i < MAX_TASKS; i++) {
		if (tasks0_[i].used() == false) {
			// 空きが見つかった
			task = tasks0_ + i;
			task->use();
			task->init();
			task->set_level(level);
			task->set_priority(priority);
			task->set_tss_es(1 * 8);
			task->set_tss_cs(2 * 8);
			task->set_tss_ss(1 * 8);
			task->set_tss_ds(1 * 8);
			task->set_tss_fs(1 * 8);
			task->set_tss_gs(1 * 8);
			break;
		}
	}
	return task;
}

void TaskController::run(Task* task, int level, int priority) {
	if (level < 0) {
		// レベルを変更しない
		level = task->get_level();
	}
	if (priority > 0) {
		// 優先度を変更する
		task->set_priority(priority);
	}
	
	if (task->running() == true && level != task->get_level()) {
		// タスクが動作中で、レベルも変更する
		remove(task); // これを実行すると、taskはstopする
	}
	if (task->running() == false) {
		task->set_level(level);
		add(task);
	}
	
	// 次回タスクスイッチ時、レベルを見直す
	level_changed_ = true;
}

void TaskController::sleep(Task* task) {
	Task* current;
	if (task->running() == true) {
		// タスクが起きていたら
		current = get_current_task();
		remove(current);
		if (task == current) {
			task_switchsub();
			current = get_current_task();
			farjmp(0, current->get_selector());
		}
	}
}

void TaskController::task_switchsub() {
	int i;
	
	// 一番上のレベルを探す
	for (i = 0; i < MAX_LEVELS; i++) {
		if (level_[i].get_running() > 0) {
			break;
		}
	}
	current_level_ = i;
	
	// レベル見直しが完了したのでfalse
	level_changed_ = false;
}

void TaskController::task_switch() {
	level_[current_level_].nexttask();
	if (level_changed_ == true) {
		task_switchsub();
	}
	level_[current_level_].task_switch();
}

void TaskController::send_message(int to, Message& msg) {
	if (0 <= to && to < MAX_TASKS) {
		tasks0_[to].send_message(msg);
		if (tasks0_[to].running() == false) {
			run(tasks0_ + to);
		}
	}
}

void TaskController::send_message(Task* task, Message& msg) {
	int to = task - tasks0_;
	send_message(to, msg);
}

