/* コンソール */

#include "bootpack.h"
#include <stdio.h>
#include <string.h>

void cons_task(void)
{
	struct task *task = task_now();
	struct console *cons = task->cons;
	int i;
	char line[40];

	task->line = line;
	cons_font(cons, '>', 1);

	for(;;) {
		cli();
		if(fifo_stat(&task->fifo) == 0) {
			task_sleep(task);
			sti();
		} else {
			i = fifo_get(&task->fifo);
			sti();

			if(i <= 1) {	/* カーソルタイマ */
				if(cons->cc != -1) {
					if(i != 0) {
						timer_init(cons->cur, &task->fifo, 0);
						cons->cc = COLOR_FFFFFF;
					} else {	
						timer_init(cons->cur, &task->fifo, 1);
						cons->cc = COLOR_000000;
					}
				}
				timer_set(cons->cur, 50);
			} else if(i == 2) {	/* カーソルON */
				cons->cc = COLOR_FFFFFF;
			} else if(i == 3) {	/* カーソルOFF */
				cons_font(cons, ' ', 0);
				cons->cc = -1;
			} else if(i == 4) {	/* コンソールの終了 */
				timer_cancel(cons->cur);
				cli();
				fifo_put(&fifo, cons->sht - shts->sheet + 1024);
				cons->sht = 0;
				sti();
			} else if(256 <= i && i < 512) {	/* キーボード */
				if(i == 256 + 8) {	/* バックスペース */
					if(cons->cx > 16) {
						cons_font(cons, ' ', 0);
						cons->cx -= 8;
					}
				} else if(i == 256 + 10) {	/* Enter */
					cons_font(cons, ' ', 0);
					line[cons->cx / 8 - 2] = 0;
					cons_nline(cons);

					/* コマンド実行 */
					cons_cmd_run(cons, line);
					if(cons->sht == 0) {
						fifo_put(&fifo, cons->sht - shts->sheet + 1280);
					}

					/* 入力ウィンドウの切り替え */
					fifo_put(&fifo, 1);

					/* プロンプト表示 */
					cons_font(cons, '>', 1);
				} else {	/* 一般文字 */
					cons_font(cons, ' ', 0);
					if(cons->cx < CONSOLE_X * 8) {
						line[cons->cx / 8 - 2] = i - 256;
						cons_font(cons, i - 256, 1);
					}
				}
			}
			if(cons->cc >= 0 && cons->sht != 0) {	/* カーソル表示 */
				graphic_box(cons->sht, cons->cc, cons->cx, cons->cy, cons->cx + 7, cons->cy + 15);
			}
		}
	}
}

struct sheet *cons_open(void)
{
	struct sheet *sht = sheet_alloc();
	unsigned char *buf = (unsigned char *) memory_alloc(mem, (CONSOLE_X * 8 + 16) * (CONSOLE_Y * 16 + 36));

	/* シートの準備 */
	sheet_set(sht, buf, CONSOLE_X * 8 + 16, CONSOLE_Y * 16 + 36, -1);
	window_open(sht, "コンソール", 0);
	window_tbox(sht, COLOR_000000, 8, 28, CONSOLE_X * 8, CONSOLE_Y * 16);

	/* タスクの準備 */
	sht->task = cons_topen(sht);
	sht->flag |= SHEET_CONSOLE;

	/* タイマの初期化 */
	sht->task->cons->cur = timer_alloc();
	timer_init(sht->task->cons->cur, &sht->task->fifo, 1);
	timer_set(sht->task->cons->cur, 50);
	return sht;
}

void cons_close(struct sheet *sht)
{
	struct task *task = sht->task;

	/* シートの片付け */
	memory_free(mem, (int) sht->buf, (CONSOLE_X * 8 + 16) * (CONSOLE_Y * 16 + 36));
	sheet_free(sht);

	/* タスクの片付け */
	cons_tclose(task);
	return;
}

struct task *cons_topen(struct sheet *sht)
{
	struct task *task = task_alloc();
	struct console *cons = (struct console *) memory_alloc(mem, sizeof(struct console));
	struct fhandle *fhandle = (struct fhandle *) memory_alloc(mem, sizeof(struct fhandle) * 8);
	int *fbuf = (int *) memory_alloc(mem, 32 * 4), i;

	/* タスクの初期化 */
	task->stack = memory_alloc(mem, 65536);
	task->tss.eip = (int) &cons_task;
	task->tss.esp = task->stack + 65536;
	task->tss.es = 1 * 8;
	task->tss.cs = 2 * 8;
	task->tss.ss = 1 * 8;
	task->tss.ds = 1 * 8;
	task->tss.fs = 1 * 8;
	task->tss.gs = 1 * 8;

	/* コンソール固有データの初期化 */
	cons->sht = sht;
	cons->cur = 0;
	cons->cx = 8;
	cons->cy = 28;
	cons->cc = -1;
	task->cons = cons;

	/* ファイルハンドルの初期化 */
	for(i = 0; i < 8; i++) {
		fhandle[i].buf = 0;
	}
	task->fh = fhandle;

	/* FIFOの初期化 */
	fifo_init(&task->fifo, task, fbuf, 32);
	task->fifo.task = task;

	task_run(task, 1, 1);
	return task;
}

void cons_tclose(struct task *task)
{
	/* タスクスリープの間すべてを開放する */
	task_sleep(task);
	memory_free(mem, task->stack, 65536);
	memory_free(mem, (int) task->cons, sizeof(struct console));
	memory_free(mem, (int) task->fifo.buf, 32 * 4);
	memory_free(mem, (int) task->fh, sizeof(struct fhandle) * 8);
	task->flag = TASK_NONE;
	cli();
	if(tasks->tfpu == task) {
		tasks->tfpu = 0;
	}
	sti();
	return;
}

void cons_fonts(struct console *cons, char *s)
{
	for(; *s != 0; s++) {
		cons_font(cons, *s, 1);
	}
	return;
}

void cons_font(struct console *cons, char c, char m)
{
	char s[2];

	if(c == 0x09) {	/* タブ */
		for(;;) {
			if(cons->sht != 0) {
				graphic_fonts(cons->sht, COLOR_000000, COLOR_FFFFFF, cons->cx, cons->cy, " ");
			}
			cons->cx += 8;
			if(cons->cx == 8 + CONSOLE_X * 8) {
				cons_nline(cons);
			}
			if(((cons->cx - 8) & 0x3f) == 0) {	/* 64ドット（8文字単位） */
				break;
			}
		}
	} else if(c == 0x0a) {	/* 改行 */
		cons_nline(cons);
	} else if(c == 0x0d) {	/* 復帰 */
	} else {
		s[0] = c;
		s[1] = 0;
		if(cons->sht != 0) {
			graphic_fonts(cons->sht, COLOR_000000, COLOR_FFFFFF, cons->cx, cons->cy, s);
		}
		if(m != 0) {
			cons->cx += 8;
			if(cons->cx == 8 + CONSOLE_X * 8) {
				cons_nline(cons);
			}
		}
	}
	return;
}

void cons_nline(struct console *cons)
{
	int x, y;

	if(cons->cy < CONSOLE_Y * 16) {
		cons->cy += 16;
	} else {	/* スクロール */
		if(cons->sht != 0) {
			for(y = 28; y < (CONSOLE_Y - 1) * 16 + 28; y++) {
				for(x = 8; x < CONSOLE_X * 8 + 8; x++) {
					cons->sht->buf[y * cons->sht->sx + x] = cons->sht->buf[(y + 16) * cons->sht->sx + x];
				}
			}
			for(y = (CONSOLE_Y - 1) * 16 + 28; y < CONSOLE_Y * 16 + 28; y++) {
				for(x = 8; x < CONSOLE_X * 8 + 8;x++) {
					cons->sht->buf[y * cons->sht->sx + x] = COLOR_000000;
				}
			}
			sheet_refresh(cons->sht, 8, 28, CONSOLE_X * 8 + 8, CONSOLE_Y * 16 + 28);
		}
	}
	cons->cx = 8;
	if(cons->sht->task->lbyte != 0) {
		cons->cx += 8;
	}
	return;
}

void cons_cmd_run(struct console *cons, char *line)
{
	if(strcmp(line, "mem") == 0 && cons->sht) {
		cons_cmd_mem(cons);
	} else if(strcmp(line, "cls") == 0 && cons->sht) {
		cons_cmd_cls(cons);
	} else if(strcmp(line, "dir") == 0 && cons->sht) {
		cons_cmd_dir(cons);
	} else if(strncmp(line, "del ", 4) == 0 && cons->sht) {
		cons_cmd_del(cons, line + 4);
	} else if(line[0] != 0) {
		if(cons_runapp(cons, line) == 0) {
			cons_fonts(cons, "このコマンドまたは、外部コマンドは、認識されていません。");
		}
	}
	cons_nline(cons);
	return;
}

void cons_cmd_mem(struct console *cons)
{
	char s[30];

	sprintf(s, "合計　%4dMB\n空き　%4dMB", memtotal/1048576, memory_fsize(mem)/1048576);
	cons_fonts(cons, s);
	return;
}

void cons_cmd_cls(struct console *cons)
{
	int x, y;

	for(y = 28; y < CONSOLE_Y * 16 + 28; y++) {
		for(x = 8; x < CONSOLE_X * 8 + 8; x++) {
				cons->sht->buf[y * cons->sht->sx + x] = COLOR_000000;
		}
	}
	sheet_refresh(cons->sht, 8, 28, CONSOLE_X * 8 + 8, CONSOLE_Y * 16 + 28);
	cons->cy = 12;
	return;
}

void cons_cmd_dir(struct console *cons)
{
	struct file *file = (struct file *)FILE_ENTRY_ADDR;
	int i, j, k, m;
	char s[30];

	for(i = j = 0;i < 224;i++) {
		if((file[i].name[0] != 0x00) && (file[i].name[0] != 0xe5) && ((file[i].type & 0x18) == 0)) {
			sprintf(s, "filename ext　　%7dバイト", file[i].size);
			j += file[i].size;
			for(k = 0; k < 8; k++) {
				s[k] = file[i].name[k];
			}
			k++;
			for(m = 0; k < 12; k++) {
				s[k] = file[i].ext[m];
				m++;
			}
			if(file->ext[0] != ' ') {
				s[8] = '.';
			}
			cons_fonts(cons, s);
			cons_nline(cons);
		} else 
			break;
	}
	sprintf(s, "%3d個のファイル %7dバイト", i, j);
	cons_fonts(cons, s);
	return;
}

void cons_cmd_del(struct console *cons, char *line)
{
	struct file *file;
	int i;
	char name[14];

	for(i = 0; i < 13; i++) {
		if(line[i] <= ' ') {
			break;
		}
		name[i] = line[i];
	}
	name[i] = 0;

	file = file_search(name);
	if(file != 0) {
		i = file_delete(file);
		if(i != 0) {
			cons_fonts(cons, "ファイルを削除できません。");
		}
	} else {
		cons_fonts(cons, "ファイルが存在しません。");
	}
	return;
}

int cons_runapp(struct console *cons, char *line)
{
	struct task *task = task_now();
	struct file *file;
	char name[14], *p, *q;
	int ssize, dsize, esp, dhrb, asize;
	int i;

	for(i = 0; i < 13; i++) {
		if(line[i] <= ' ') {
			break;
		}
		name[i] = line[i];
	}
	name[i] = 0;

	file = file_search(name);
	if(file == 0 && name[i - 1] != '.' && i < 9) {
		/* 拡張子をつけて再検索 */
		name[i] = '.';
		name[i + 1] = 'h';
		name[i + 2] = 'r';
		name[i + 3] = 'b';
		name[i + 4] = 0;
		file = file_search(name);
	}

	if(file != 0) {
		asize = file->size;
		p = (char *) file_read(file->cno, &asize);
		if(p == 0) {
			memory_free(mem, (int) p, asize);
			cons_fonts(cons, "ファイルを読み込めません。");
			return 1;
		}
		if(asize >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
			/* サイズの取得 */
			ssize = *((int *) (p + HRB_SEG_SIZE));
			dhrb = *((int *) (p + HRB_DATA_ADDR));
			dsize = *((int *) (p + HRB_DATA_SIZE));
			esp = *((int *) (p + HRB_ESP_INIT));

			/* メモリの初期化とデータのメモ */
			q = (char *) memory_alloc(mem, ssize);
			task->ds = (int) q;

			/* セグメント設定と実行 */
			seg_set(task->ldt, (int) p, asize - 1, AR_CODE_ER + AR_APP);
			seg_set(task->ldt + 1, (int) q, ssize - 1, AR_DATA_RW + AR_APP);
			for(i = 0; i < dsize; i++) {
				q[esp + i] = p[dhrb + i];
			}
			app_start(0x1b, 0 * 8 + 4, 1 * 8 + 4, esp, &(task->tss.esp0));

			/* ウィンドウ処理とタイマの処理の片付け */
			for(i = 0; i < SHEET_MAX; i++) {
				if((shts->sheet[i].flag & SHEET_FROMAPP) != 0 && shts->sheet[i].task == task) {
					sheet_free(&(shts->sheet[i]));
				}
			}
			timer_cancel_auto(&task->fifo);

			/* ファイル処理とメモリ処理の片付け */
			for(i = 0; i < 8; i++) {
				if(task->fh[i].buf != 0) {
					if(task->fh[i].flag != FHANDLE_NONE) {
						file_write(&task->fh[i], fat, task->fh[i].bpos);
					}
					memory_free(mem, (int)task->fh[i].buf, task->fh[i].size);
					task->fh[i].buf = 0;
				}
			}
			if(task->flag&TASK_MEMMAN) {
				memory_free(mem, (int)q, ssize);
				task->flag &= ~TASK_MEMMAN;
			}

			/* BEEPの消音 */
			beep(0);

			/* 日本語処理の正常化 */
			task->lbyte = 0;
		} else {
			cons_fonts(cons, "有効な外部コマンドではありません。");
		}
		memory_free(mem, (int) p, asize);
		return 1;
	}
	return 0;
}
