/* シート関係 */

#include "bootpack.h"

struct sheets *shts;	/* 管理用 */

void sheets_init(unsigned char *vram, int xsize, int ysize)
{
	int i;

	shts = (struct sheets *) memory_alloc(mem, sizeof(struct sheets));
	if(shts == 0) {
		return;
	}
	shts->map = (unsigned char *) memory_alloc(mem, xsize * ysize);
	if(shts->map == 0) {
		memory_free(mem, (int) shts, sizeof(struct sheets));
		return;
	}
	shts->vram = vram;
	shts->xsize = xsize;
	shts->ysize = ysize;
	shts->top = -1;	/* シートはない */
	for(i = 0; i < SHEET_MAX; i++) {
		shts->sheet[i].flag = SHEET_NONE;
	}
	return;
}

struct sheet *sheet_alloc(void)
{
	struct sheet *sht;
	int i;

	for(i = 0; i < SHEET_MAX; i++) {
		if(shts->sheet[i].flag == SHEET_NONE) {
			sht = &shts->sheet[i];
			sht->flag = SHEET_USE;
			sht->height = -1;	/* 非表示中 */
			sht->task = 0;
			return sht;
		}
	}
	return 0;
}

void sheet_set(struct sheet *sht, unsigned char *buf, int xsize, int ysize, int cinv)
{
	sht->buf = buf;
	sht->sx = xsize;
	sht->sy = ysize;
	sht->cinv = cinv;
	return;
}

void sheet_updown(struct sheet *sht, int height)
{
	int h, old = sht->height;	/* 設定前の高さをメモ */

	/* 指定が不正なら直す */
	if(height > shts->top + 1) {
		height = shts->top + 1;
	}
	if(height < -1) {
		height = -1;
	}
	sht->height = height;	/* 高さを設定 */

	/* shts->s[]を高さ順に並べる */
	if(old > height) {	/* 以前より低いなら */
		if(height >= 0) {
			/* 間のものを上げる */
			for(h = old; h > height; h--) {
				shts->s[h] = shts->s[h - 1];
				shts->s[h]->height = h;
			}
			shts->s[height] = sht;
			sheet_refreshmap(sht->x, sht->y, sht->x + sht->sx, sht->y + sht->sy, height + 1);
			sheet_refreshsub(sht->x, sht->y, sht->x + sht->sx, sht->y + sht->sy, height + 1, old);
		} else {	/* 非表示化 */
			if(shts->top > old) {
				/* 上になっているものをおろす */
				for(h = old; h < shts->top; h++) {
					shts->s[h] = shts->s[h+1];
					shts->s[h]->height = h;
				}
			}
			shts->top--;	/* 表示中のシートが減る */
			sheet_refreshmap(sht->x, sht->y, sht->x + sht->sx, sht->y + sht->sy, 0);
			sheet_refreshsub(sht->x, sht->y, sht->x + sht->sx, sht->y + sht->sy, 0, old - 1);
		}
	} else if(old < height) {	/* 以前より高いなら */
		if(old >= 0) {
			/* 間のものを押し下げる */
			for(h = old; h < height; h++) {
				shts->s[h] = shts->s[h + 1];
				shts->s[h]->height = h;
			}
			shts->s[height] = sht;
		} else {	/* 表示化 */
			/* 上になるものを持ち上げる */
			for(h = shts->top; h >= height; h--) {
				shts->s[h + 1] = shts->s[h];
				shts->s[h + 1]->height = h + 1;
			}
			shts->s[height] = sht;
			shts->top++;	/* 表示中のシートが増える */
		}
		sheet_refreshmap(sht->x, sht->y, sht->x + sht->sx, sht->y + sht->sy, height);
		sheet_refreshsub(sht->x, sht->y, sht->x + sht->sx, sht->y + sht->sy, height, height);
	}
	return;
}

void sheet_refresh(struct sheet *sht, int x0, int y0, int x1, int y1)
{
	if(sht->height >= 0) {/* 表示中なら、新しい情報で表示 */
		sheet_refreshmap(sht->x + x0, sht->y + y0, sht->x + x1, sht->y + y1, sht->height - 1);
		sheet_refreshsub(sht->x + x0, sht->y + y0, sht->x + x1, sht->y + y1, sht->height, sht->height);
	}
	return;
}

void sheet_refreshsub(int x0, int y0, int x1, int y1, int h0, int h1)
{
	struct sheet *sht;
	int h, x, y, rx, ry, sx0, sy0, sx1, sy1, sx2, sid4, i, i1, *p, *q, *r;
	unsigned char *buf, *vram = shts->vram, *map = shts->map, sid;

	/* 画面外をサポート */
	if(x0 < 0) { x0 = 0; }
	if(y0 < 0) { y0 = 0; }
	if(x1 > shts->xsize) { x1 = shts->xsize; }
	if(y1 > shts->ysize) { y1 = shts->ysize; }

	/* 指定された高さの間を処理 */
	for(h = h0; h <= h1; h++) {
		sht = shts->s[h];
		buf = sht->buf;
		sid = sht - shts->sheet;

		/* x0〜y1を使ってsx0〜sy1を逆算 */
		sx0 = x0 - sht->x;
		sy0 = y0 - sht->y;
		sx1 = x1 - sht->x;
		sy1 = y1 - sht->y;
		if(sx0 < 0) { sx0 = 0; }
		if(sy0 < 0) { sy0 = 0; }
		if(sx1 > sht->sx) { sx1 = sht->sx; }
		if(sy1 > sht->sy) { sy1 = sht->sy; }

		if((sht->x & 3) == 0) {	/* 4バイト型 */
			i = (sx0 + 3) / 4;
			i1 = sx1 / 4;
			i1 = i1 - i;
			sid4 = sid | sid << 8 | sid << 16 | sid << 24;
			for(y = sy0; y < sy1; y++) {
				ry = sht->y + y;
				for(x = sx0; x < sx1 && (x & 3) != 0; x++) {	/* 前の端数を1バイトずつ */
					rx = sht->x + x;
					if(map[ry * shts->xsize + rx] == sid) {
						vram[ry * shts->xsize + rx] = buf[y * sht->sx + x];
					}
				}
				rx = sht->x + x;
				p = (int *) &map[ry * shts->xsize + rx];
				q = (int *) &vram[ry * shts->xsize + rx];
				r = (int *) &buf[y * sht->sx + x];
				for(i = 0; i < i1; i++) {	/* 4の倍数の部分 */
					if(p[i] == sid4) {
						q[i] = r[i];
					} else {
						sx2 = x + i * 4;
						rx = sht->x + sx2;
						if(map[ry * shts->xsize + rx] == sid) {
							vram[ry * shts->xsize + rx] = buf[y * sht->sx + sx2];
						}
						if(map[ry * shts->xsize + rx + 1] == sid) {
							vram[ry * shts->xsize + rx + 1] = buf[y * sht->sx + sx2 + 1];
						}
						if(map[ry * shts->xsize + rx + 2] == sid) {
							vram[ry * shts->xsize + rx + 2] = buf[y * sht->sx + sx2 + 2];
						}
						if(map[ry * shts->xsize + rx + 3] == sid) {
							vram[ry * shts->xsize + rx + 3] = buf[y * sht->sx + sx2 + 3];
						}
					}
				}
				for(x += i1 * 4; x < sx1; x++) {	/* 後ろの端数を1バイトずつ */
					rx = sht->x + x;
					if(map[ry * shts->xsize + rx] == sid) {
						vram[ry * shts->xsize + rx] = buf[y * sht->sx + x];
					}
				}
			}
		} else {
			/* 1バイト型 */
			for(y = sy0; y < sy1; y++) {
				ry = sht->y + y;
				for(x = sx0; x < sx1; x++) {
					rx = sht->x + x;
					if(map[ry * shts->xsize + rx] == sid) {
						vram[ry * shts->xsize + rx] = buf[y * sht->sx + x];
					}
				}
			}
		}
	}
	return;
}

void sheet_slide(struct sheet *sht, int x0, int y0)
{
	int oldx = sht->x, oldy = sht->y;

	sht->x = x0;
	sht->y = y0;
	if(sht->height >= 0) {	/* 表示中なら */
		sheet_refreshmap(oldx, oldy, sht->sx + oldx, sht->sy + oldy, 0);
		sheet_refreshmap(x0, y0, sht->sx + x0, sht->sy + y0, sht->height);
		sheet_refreshsub(oldx, oldy, sht->sx + oldx, sht->sy + oldy, 0, sht->height - 1);
		sheet_refreshsub(x0, y0, sht->sx + x0, sht->sy + y0, sht->height, sht->height);
	}
	return;
}

void sheet_free(struct sheet *sht)
{
	if(sht->height >= 0) /* 非表示にする */
		sheet_updown(sht, -1);
	sht->flag = SHEET_NONE;
	return;
}

void sheet_refreshmap(int x0, int y0, int x1, int y1, int h0)
{
	struct sheet *sht;
	int h, x, y, rx, ry, sx0, sy0, sx1, sy1, sid4, *p;
	unsigned char *buf, *map = shts->map, sid;

	/* 画面外をサポート */
	if(x0 < 0) { x0 = 0; }
	if(y0 < 0) { y0 = 0; }
	if(x1 > shts->xsize) { x1 = shts->xsize; }
	if(y1 > shts->ysize) { y1 = shts->ysize; }

	/* 指定された高さから上を処理 */
	for(h = h0; h <= shts->top; h++) {
		sht = shts->s[h];
		buf = sht->buf;
		sid = sht - shts->sheet;

		/* x0〜y1を使ってsx0〜sy1を逆算 */
		sx0 = x0 - sht->x;
		sy0 = y0 - sht->y;
		sx1 = x1 - sht->x;
		sy1 = y1 - sht->y;
		if(sx0 < 0) { sx0 = 0; }
		if(sy0 < 0) { sy0 = 0; }
		if(sx1 > sht->sx) { sx1 = sht->sx; }
		if(sy1 > sht->sy) { sy1 = sht->sy; }

		if(sht->cinv == -1) {
			if((sht->x & 3) == 0 && (sx0 & 3) == 0 && (sx1 & 3) == 0) {
				/* 透明色なし専用高速版（4バイト型） */
				sx1 = (sx1 - sx0) / 4;	/* 回数 */
				sid4 = sid | sid << 8 | sid << 16 | sid << 24;
				for(y = sy0; y < sy1; y++) {
					ry = sht->y + y;
					rx = sht->x + sx0;
					p = (int *) &map[ry * shts->xsize + rx];
					for(x = 0; x < sx1; x++) {
						p[x] = sid4;
					}
				}
			} else {
				/* 透明色なし専用一般版（1バイト型） */
				for(y = sy0; y < sy1; y++) {
					ry = sht->y + y;
					for(x = sx0; x < sx1; x++) {
						rx = sht->x + x;
						map[ry * shts->xsize + rx] = sid;
					}
				}
			}
		} else {
			/* 透明色あり専用一般版（1バイト型） */
			for(y = sy0; y < sy1 ;y++) {
				ry = sht->y + y;
				for(x = sx0; x < sx1; x++) {
					rx = sht->x + x;
					if(buf[y * sht->sx + x] != sht->cinv) {
						map[ry * shts->xsize + rx] = sid;
					}
				}
			}
		}
	}
	return;
}
