
#define HSP3_MEMVAL

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

extern CHSP3_TASK __HspTaskFunc[];
extern struct SHEET *sht_back;
extern int *fat;
extern int onmouse;
struct TASK *hsptask;
struct TIMER *hsptimer;

int onclick_jmp = -1;

// LIFOスタック
char strtmp[256];
PVal hspst[STACKSIZE];
int hsplifo_c;
int hspredraw;
int hspcnt;
PVal hspstat;

struct HSPRGB color;
struct HSPPOS pos;

// 変数バッファ
PVal mem_var[STACKSIZE];

/*
	コメントについてる記号の読み方

	TBD			未定（to be determined）
	TODO		要実装（To do）
	ReqImp		要改良（Require improvement）

*/


// 文字列出力
void putfonts8_asc_sht(struct SHEET *sht, int x, int y, struct HSPRGB c, int b, char *s, int l)
{
	struct TASK *task = task_now();
	if (task->langmode != 0 && task->langbyte1 != 0) {
		putstringA(sht->buf, sht->bxsize, x, y, c.r, c.g, c.b, s);
		if (hspredraw == 1) {
			sheet_refresh(sht, x - 8, y, x + l * 8, y + 16);
		}
	} else {
		putstringA(sht->buf, sht->bxsize, x, y, c.r, c.g, c.b, s);
		if (hspredraw == 1) {
			sheet_refresh(sht, x, y, x + l * 8, y + 16);
		}
	}
	return;
}

#define MAXSTR    120
// テスト用printf
int hsp_printf(const char *format, ...)
{
	va_list arglist;
	char buf[MAXSTR];
	int ret;

	va_start(arglist, format);
	ret = vsprintf(buf, format, arglist);
	mes(buf);
	va_end(arglist);

	return ret;
}

// 画像読み込み(CD-R)
// QEMUだとかなり速度が遅い
int loadimg(char *filename, int x, int y)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	unsigned char *filebuf, r, g, b;
	struct DLL_STRPICENV env;
	struct RGB *picbuf;
	int info[8], fsize, xx, yy;
	FILE *fp;

	fp = fopen(filename, "r");

	if (fp != NULL) {
		fsize   = fp->size;
		filebuf = (unsigned char *) memman_alloc_4k(memman, fsize);
		if (!fread(filebuf, fsize, 1, fp)) {
			fclose(fp);
			return 1;
		}
		fclose(fp);
		if (info_BMP(&env, info, fsize, filebuf) == 0)
			if (info_JPEG(&env, info, fsize, filebuf) == 0)
				return 1;
		picbuf = (struct RGB *) memman_alloc_4k(memman, info[2] * info[3] * sizeof(struct RGB));
		if (info[0] == 1)
			decode0_BMP(&env, fsize, filebuf, 4, (unsigned char *) picbuf, 0);
		else
			decode0_JPEG(&env, fsize, filebuf, 4, (unsigned char *) picbuf, 0);
		for (yy = 0; yy < info[3]; yy++) {
			for (xx = 0; xx < info[2]; xx++) {
				r = picbuf[yy * info[2] + xx].r;
				g = picbuf[yy * info[2] + xx].g;
				b = picbuf[yy * info[2] + xx].b;
				sht_back->buf[(yy + y) * sht_back->bxsize + (xx + x)] = (unsigned short)(((r << 8) & 0xf800) | ((g << 3) & 0x07e0) | (b >> 3));
			}
		}
	} else {
		fclose(fp);
		return 1;
	}

	sheet_refresh(sht_back, 0, 0, sht_back->bxsize, sht_back->bysize);
	return 0;
}


// HSPタスク設定
void init_hsp_task(void)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	hsptask = task_alloc();
	int *hsptask_fifo = (int *) memman_alloc_4k(memman, 128 * 4);
	hsptask->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024;
	hsptask->tss.eip = (int) &hsp_task;
	hsptask->tss.es =  8;
	hsptask->tss.cs = 16;
	hsptask->tss.ss =  8;
	hsptask->tss.ds =  8;
	hsptask->tss.fs =  8;
	hsptask->tss.gs =  8;
	hsptask->langmode = 1;
	task_run(hsptask, 1, 2); /* level=1, priority=2 */
	fifo32_init(&hsptask->fifo, 128, hsptask_fifo, hsptask);
}

// HSPタスクのmain
void hsp_task(void)
{
	int i;
	hsptimer = timer_alloc();
	timer_init(hsptimer, &hsptask->fifo, 2);
	fifo32_put(&hsptask->fifo, 1);

	while (1) {
		io_cli();
		if (fifo32_status(&hsptask->fifo) == 0) {
			task_sleep(hsptask);
			io_sti();
		} else {
			i = fifo32_get(&hsptask->fifo);
			io_sti();
			if (i == 1) {
				InitHSP();
				__HspEntry();
				Prgcmd(17, 0);
			}
			if (i == 2) {
				if (onclick_jmp != -1) {
					TaskSwitch(onclick_jmp);
				}
			}
			if (i == 3) {
			}
			if (i == 0xff) {
				/* stop */
			}
		}
	}
	return;
}

// HSPタスクの初期化
void InitHSP(void)
{
	int i;
	for (i = 0; i == 20; i++) {
		hspst[i].string = (char *) 0;
		hspst[i].num = 0;
		hspst[i].label = 0;
		hspst[i].mode = 0;
	}
	color.r = color.g = color.b = 0;
	pos.x = pos.y = 1;
	hsplifo_c = 0;
	hspredraw = 1;
	hspcnt = 0;
	boxfillA(sht_back->buf, sht_back->bxsize, 255,255,255, 0, 0, sht_back->bxsize, sht_back->bysize);
	sheet_refresh(sht_back, 0, 0, sht_back->bxsize, sht_back->bysize);
	return;
}

void PushStr(char *str)
{
	hspst[hsplifo_c].string = str;
	hspst[hsplifo_c].num = 0;
	hspst[hsplifo_c].label = 0;
	hspst[hsplifo_c].mode = 1;
	hsplifo_c++;
	return;
}

void PushInt(int i)
{
	hspst[hsplifo_c].string = (char *) 0;
	hspst[hsplifo_c].num = i;
	hspst[hsplifo_c].label = 0;
	hspst[hsplifo_c].mode = 2;
	hsplifo_c++;
	return;
}

void PushLabel(int i)
{
	hspst[hsplifo_c].string = (char *) 0;
	hspst[hsplifo_c].num = 0;
	hspst[hsplifo_c].label = i;
	hspst[hsplifo_c].mode = 3;
	hsplifo_c++;
	return;
}

void PushVar(PVal *var, int i)
{
	hspst[hsplifo_c] = *var;
	hsplifo_c++;
	return;
}

void PushVAP(PVal *var, int i)
{
	hspst[hsplifo_c] = *var;
	hsplifo_c++;
	return;
}

void PushExtvar(int num, int i)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	extern int *mousex;
	extern int *mousey;
	if (num == 256) {	/* ginfo TODO TBD ReqImp */
		int b = hspst[hsplifo_c-1].num;
		if (b == 0) {	/* マウスX座標 */
			hspst[hsplifo_c-1].num = *mousex;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 1) {	/* マウスY座標 */
			hspst[hsplifo_c-1].num = *mousey;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 2) {	/* アクティブなウインドウID TBD */
			hspst[hsplifo_c-1].num = 0;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 3) {	/* 操作先ウィンドウID TBD */
			hspst[hsplifo_c-1].num = 0;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 4) {	/* ウィンドウの左上X座標 */
			hspst[hsplifo_c-1].num = 0;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 5) {	/* ウィンドウの左上Y座標 */
			hspst[hsplifo_c-1].num = 0;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 6) {	/* ウィンドウの右下X座標 */
			hspst[hsplifo_c-1].num = binfo->scrnx;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 7) {	/* ウィンドウの右下Y座標 */
			hspst[hsplifo_c-1].num = binfo->scrny;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 8) {	/* ウィンドウの描画基点X座標 */
			hspst[hsplifo_c-1].num = 0;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 9) {	/* ウィンドウの描画基点Y座標 */
			hspst[hsplifo_c-1].num = 0;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 10) {	/* ウィンドウ全体のXサイズ */
			hspst[hsplifo_c-1].num = binfo->scrnx;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 11) {	/* ウィンドウ全体のYサイズ */
			hspst[hsplifo_c-1].num = binfo->scrny;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 12) {	/* クライアント領域Xサイズ */
			hspst[hsplifo_c-1].num = binfo->scrnx;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 13) {	/* クライアント領域Yサイズ */
			hspst[hsplifo_c-1].num = binfo->scrny;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 14) {	/* メッセージの出力Xサイズ TODO */
		}
		if (b == 15) {	/* メッセージの出力Yサイズ TODO */
		}
		if (b == 16) {	/* 現在設定されているカラーコード(R) */
			hspst[hsplifo_c-1].num = color.r;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 17) {	/* 現在設定されているカラーコード(G) */
			hspst[hsplifo_c-1].num = color.g;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 18) {	/* 現在設定されているカラーコード(B) */
			hspst[hsplifo_c-1].num = color.b;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 19) {	/* デスクトップのカラーモード */
			hspst[hsplifo_c-1].num = 1;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 20) {	/* デスクトップ全体のXサイズ */
			hspst[hsplifo_c-1].num = binfo->scrnx;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 21) {	/* デスクトップ全体のYサイズ */
			hspst[hsplifo_c-1].num = binfo->scrny;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 22) {	/* カレントポジションのX座標 */
			hspst[hsplifo_c-1].num = pos.x;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 23) {	/* カレントポジションのY座標 */
			hspst[hsplifo_c-1].num = pos.y;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 24) {	/* メッセージ割り込み時のウィンドウID TODO */
		}
		if (b == 25) {	/* 未使用ウィンドウID TODO */
		}
		if (b == 26) {	/* 画面の初期化Xサイズ */
			hspst[hsplifo_c-1].num = binfo->scrnx;
			hspst[hsplifo_c-1].mode = 2;
		}
		if (b == 27) {	/* 画面の初期化Yサイズ */
			hspst[hsplifo_c-1].num = binfo->scrny;
			hspst[hsplifo_c-1].mode = 2;
		}
	}
	return;
}

void PushSysvar(int num, int i)
{
	if (num == 4) {		/* cnt */
		hspst[hsplifo_c].num = hspcnt;
		hspst[hsplifo_c].mode = 2;
	}
	hsplifo_c++;
	return;
}

void PushFuncEnd(void)	/* TBD */
{
	hsplifo_c = 0;
	return;
}

void VarSet(PVal *var, int i)
{
	*var = hspst[hsplifo_c-1];
	return;
}

void VarInc(PVal *var, int i)
{
	var->num++;
	return;
}

void CalcAddI(void)
{
	static char s[256];
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	if (a.mode == 1 && b.mode == 1) {
		strcpy(s, a.string);
		strcat(s, b.string);
		a.string = s;
	} else if (a.mode == 1 && b.mode == 2) {
		sprintf(s, "%s%d", a.string, b.num);
		a.string = s;
	}  else {
		a.num += b.num;
	}
	hspst[hsplifo_c-2] = a;
	hsplifo_c--;
	return;
}

void CalcSubI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	a.num -= b.num;
	hsplifo_c--;
	return;
}

void CalcMulI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	a.num *= b.num;
	hsplifo_c--;
	return;
}

void CalcEqI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	hspst[hsplifo_c-2].num = !(a.num == b.num);
	hsplifo_c--;
	return;
}

void CalcLtI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	hspst[hsplifo_c-2].num = !(a.num < b.num);
	hsplifo_c--;
	return;
}

void CalcGtI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	hspst[hsplifo_c-2].num = !(a.num > b.num);
	hsplifo_c--;
	return;
}

void CalcLtEqI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	hspst[hsplifo_c-2].num = !(a.num <= b.num);
	hsplifo_c--;
	return;
}

void CalcGtEqI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-2];
	b = hspst[hsplifo_c-1];
	hspst[hsplifo_c-2].num = !(a.num >= b.num);
	hsplifo_c--;
	return;
}

void CalcOrI(void)
{
	PVal a, b;
	a = hspst[hsplifo_c-1];
	b = hspst[hsplifo_c];
	hspst[hsplifo_c-1].num = !(a.num || b.num);
	hsplifo_c--;
	return;
}

/* TODO */
void VarCalc(PVal *var, int i, int j)
{
	if (i == 0) {
		hspst[hsplifo_c-1].num += var->num;
	}
	return;
}

int HspIf(void)
{
	int ret = hspst[hsplifo_c-1].num;
	return ret;
}

void mes(char *s)
{
	putfonts8_asc_sht(sht_back, pos.x, pos.y, color, 0, s, strlen(s));
	pos.y += 16;
}

void line(int x0, int y0, int x1, int y1)
{
	int i, x, y, len, dx, dy;

	dx = x1 - x0;
	dy = y1 - y0;
	x = x0 << 10;
	y = y0 << 10;
	if (dx < 0)	dx = - dx;
	if (dy < 0)	dy = - dy;
	if (dx >= dy) {
		len = dx + 1;
		if (x0 > x1) {
			dx = -1024;
		} else {
			dx = +1024;
		}
		if (y0 <= y1) {
			dy = ((y1 - y0 + 1) << 10) / len;
		} else {
			dy = ((y1 - y0 - 1) << 10) / len;
		}
	} else {
		len = dy + 1;
		if (y0 > y1) {
			dy = -1024;
		} else {
			dy = +1024;
		}
		if (x0 <= x1) {
			dx = ((x1 - x0 + 1) << 10) / len;
		} else {
			dx = ((x1 - x0 - 1) << 10) / len;
		}
	}
	for (i = 0; i < len; i++) {
		sht_back->buf[(y >> 10) * sht_back->bxsize + (x >> 10)] = RGB(color.r, color.g, color.b);
		x += dx;
		y += dy;
	}

	pos.x = x0;
	pos.y = y0;
	if (hspredraw == 1) {
		sheet_refresh(sht_back, x0, y0, x1, y1);
	}

	return;
}

void Intcmd(int num, int i)
{
	if (num == 3) {		/* onclick TODO */
		/* iのFILOに関数へのラベルが入ってる */
		onclick_jmp = hspst[hsplifo_c-1].num;
	}
	hsplifo_c -= i;
	return;
}

void Extcmd(int num, int i)
{
	int tmp;
	if (num == 0x0c) {	/* pset */
		if (i == 0) {
			sht_back->buf[pos.y * sht_back->bxsize + pos.x] = RGB(color.r, color.g, color.b);
			sheet_refresh(sht_back, pos.x, pos.y, pos.x+1, pos.y+1);
		}
		if (i == 2) {
			pos.x = hspst[hsplifo_c-1].num;
			pos.y = hspst[hsplifo_c-2].num;
			sht_back->buf[pos.y * sht_back->bxsize + pos.x] = RGB(color.r, color.g, color.b);
			sheet_refresh(sht_back, pos.x, pos.y, pos.x+1, pos.y+1);
		}
	}
	if (num == 0x0f) {	/* mes */
		if (hspst[hsplifo_c-1].string == (char *) 0) {
			char s[10];
			sprintf(s, "%d", hspst[hsplifo_c-1].num);
			mes(s);
		} else {
			mes(hspst[hsplifo_c-1].string);
		}
	}
	if (num == 0x10) {	/* title TBD */
	}
	if (num == 0x11) {	/* pos */
		if (i == 2) {
			pos.x = hspst[hsplifo_c-1].num;
			pos.y = hspst[hsplifo_c-2].num;
			hsplifo_c -= i;
		}
	}
	if (num == 0x13) {	/* cls ReqImp */
		pos.x = pos.y = 0;
		if (i == 0) {
			boxfillA(sht_back->buf, sht_back->bxsize, COLA_FFFFFF, 0, 0, sht_back->bxsize, sht_back->bysize);
			sheet_refresh(sht_back, 0, 0, sht_back->bxsize, sht_back->bysize);
		}
		if (i == 1) {
			color.r = color.g = color.b = pos.x = pos.y = 0;
			if (hspst[hsplifo_c-1].num == 0) {
				tmp = RGB(0xff,0xff,0xff);
			}
			if (hspst[hsplifo_c-1].num == 1) {
				tmp = RGB(0xcc,0xcc,0xcc);
			}
			if (hspst[hsplifo_c-1].num == 2) {
				tmp = RGB(0x99,0x99,0x99);
			}
			if (hspst[hsplifo_c-1].num == 3) {
				tmp = RGB(0x33,0x33,0x33);
			}
			if (hspst[hsplifo_c-1].num == 4) {
				tmp = RGB(0x00,0x00,0x00);
			}
			boxfillA(sht_back->buf, sht_back->bxsize,
					 getRGB16(tmp).r, getRGB16(tmp).g, getRGB16(tmp).b,
					 0, 0, sht_back->bxsize, sht_back->bysize);
			sheet_refresh(sht_back, 0, 0, sht_back->bxsize, sht_back->bysize);
		}
	}
	if (num == 0x17) {	/* picload */
		if (loadimg(hspst[hsplifo_c-1].string, 0, 0)) {
			mes("画像ファイルが見つかりません。");
			while(1);
		}
	}
	if (num == 0x18) {	/* color */
		if (i == 0) {
			color.r = color.g = color.b = 0;
		}
		if (i == 3) {
			color.r = hspst[hsplifo_c-1].num;
			color.g = hspst[hsplifo_c-2].num;
			color.b = hspst[hsplifo_c-3].num;
		}
	}
	if (num == 0x2f) {	/* line */
		if (i == 0) {
			line(0, 0, pos.x, pos.y);
		}
		if (i == 2) {
			line(pos.x, pos.y, hspst[hsplifo_c-1].num, hspst[hsplifo_c-2].num);
			pos.x = hspst[hsplifo_c-1].num;
			pos.y = hspst[hsplifo_c-2].num;
		}
		if (i == 4) {
			line(hspst[hsplifo_c-1].num, hspst[hsplifo_c-2].num, hspst[hsplifo_c-3].num, hspst[hsplifo_c-4].num);
		}
	}
	if (num == 0x31) {	/* boxf ReqImp */
		if (i == 0) {
			boxfillA(sht_back->buf, sht_back->bxsize, color.r, color.g, color.b, 0, 0, sht_back->bxsize, sht_back->bysize);
			if (hspredraw == 1) {
				sheet_refresh(sht_back, 0, 0, sht_back->bxsize, sht_back->bysize);
			}
		}
		if (i == 2) {
			int x0, y0;
			x0 = hspst[hsplifo_c-1].num;
			y0 = hspst[hsplifo_c-2].num;
			boxfillA(sht_back->buf, sht_back->bxsize, color.r, color.g, color.b, x0, y0, sht_back->bxsize, sht_back->bysize);
			if (hspredraw == 1) {
				sheet_refresh(sht_back, x0-1, y0-1, sht_back->bxsize, sht_back->bysize);
			}
		}
		if (i == 4) {
			int x0, y0, x1, y1;
			x0 = hspst[hsplifo_c-1].num;
			y0 = hspst[hsplifo_c-2].num;
			x1 = hspst[hsplifo_c-3].num;
			y1 = hspst[hsplifo_c-4].num;
			boxfillA(sht_back->buf, sht_back->bxsize, color.r, color.g, color.b, x0, y0, x1, y1);
			if (hspredraw == 1) {
				sheet_refresh(sht_back, x0-1, y0-1, x1+1, y1+1);
			}
		}
	}
	if (num == 44) {	/* mouse */
		int x, y;
		x = hspst[hsplifo_c-1].num;
		y = hspst[hsplifo_c-2].num;
		if (i == 0) {
			mouse(0, 0, 2);
		}
		if (i == 1) {
			if (x == -1) {
				mouse(0, 0, 1);
			} else {
				mouse(x, -1, 0);
			}
		}
		if (i == 2) {
			if (x == -1 || y == -1) {
				mouse(0, 0, 1);
			} else {
				mouse(x, y, 0);
			}
		}
	}
	hsplifo_c -= i;
	return;
}

int loopcount;
int loopmode;
int jcount;
int endjmp;
int repjmp;

void Prgcmd(int num, int i)
{
	if (num == 4) {		/* repeat */
		if (i == 3) {
			loopmode = 0;
			loopcount = 0;
			jcount = hspst[hsplifo_c-3].num;
			endjmp = hspst[hsplifo_c-2].label;
			repjmp = hspst[hsplifo_c-1].label;
			TaskSwitch(repjmp);
		}
		if (i == 2) {
			loopmode = -1;
			repjmp = hspst[hsplifo_c-1].label;
			TaskSwitch(repjmp);
		}
	}
	if (num == 5) {		/* loop */
		if (loopmode == 0) {
			loopcount++;
			if (jcount == loopcount) {
				TaskSwitch(endjmp);
				goto end;
			}
			TaskSwitch(repjmp);
		} else {
			TaskSwitch(repjmp);
		}
	}
	if (num == 7) {		/* wait ReqImp */
		if (hspst[hsplifo_c-1].num != 0) {
			timer_settime(hsptimer, hspst[hsplifo_c-1].num);
			task_sleep(hsptask);
			hsptimer->fifo->free = 128;
		}
	}
	if (num == 8) {		/* await TODO */
	}
	if (num == 16) {	/* end */
		int tmp = initAcpi();
		if (tmp == 0) {
			acpiPowerOff();
		} else {
			io_cli();
			fifo32_put(&hsptask->fifo, 0xff);
		}
		while(1) io_hlt();
	}
	if (num == 17) {	/* stop ReqImp */
		fifo32_put(&hsptask->fifo, 0xff);
	}
end:
	hsplifo_c -= i;
	return;
}

inline void TaskSwitch(int a)
{
	char (*p)(void);
	hsplifo_c = 0;
	p = __HspTaskFunc[a];
	(*p)();
	return;
}

/* end of hsp.c */
