/****************************************************************************
 *	paint
 ****************************************************************************/

/* * Sat Jul 20 08:18:00 JST 2002 Naoyuki Sawa
 * - 上記タイムスタンプの時点でのrender_paint()をベースに、シンプルライブラリ用に移植しました。
 *   移植に際してコメントは修正していませんので、一部、API使用を想定したコメントが残っています。
 * - 塗りつぶされる色を保持する変数「c」の型に注意！
 *   API用render_paint()ではunsigned charでしたが、シンプルライブラリではintです。
 *   point()は透明値として-1を返すので、intで保持しておかないと透明部分の塗りつぶしができません。
 */

/* 全く高速化していません。遅いです。
 * ゲームでは、高速塗りつぶしが要求される場面は少ないと思うので、
 * 現状のままでも特に問題はないと思います。
 */
void
paint(int x0, int y0, int color)
{
/* 塗りつぶし点スタックサイズ：
 * 実験したところ、かなり複雑な塗りつぶしでも24程度でしたが、
 * 念のため、余裕を見込んで、大きめに取っておくことにします。
 * あまり大きすぎると、P/ECEではスタックがあふれるので、ご注意ください。
 */
#define RENDER_PAINT_STACK_SIZE	64

/* ビューポートサイズ：
 * 画面に見えている部分(DISP_X×DISP_Y)ではなく、
 * グラフィック面全体のサイズ(256×256)とします。
 */
#define VP_LEFT		  0
#define VP_RIGHT	256
#define VP_TOP		  0
#define VP_BOTTOM	256

	short stack[RENDER_PAINT_STACK_SIZE][2]; /* メモリ節約のためshortで */
	int i_stack, x, y, x1, x2;
	int c; /* APIではunsigned charですが、シンプルライブラリではintです！上のコメント参照 */

	/* 塗りつぶし開始点がビューポート外なら、何もしません。 */
	if(x0 < VP_LEFT || VP_RIGHT  - 1 < x0) return;
	if(y0 < VP_TOP  || VP_BOTTOM - 1 < y0) return;

	/* 塗りつぶされる色を取得します。 */
	c = point(x0, y0);

	/* 塗りつぶし開始点が既に塗りつぶす色なら、何もしません。 */
	/* if(c == color) return; */
	/* ->よく考えたら、このチェックは不要でした。
	 *   もしも塗りつぶし開始点が既に塗りつぶす色なら、
	 *   1. スタックに最初の(x0,y0)を積む。
	 *   2. while()ループの中で(x0,y0)を取り出す。
	 *   3. 既に塗られているので、continueで飛ばされる。
	 *   4. スタックは空なので、終了する。
	 *   となり、何もせずに終了します。
	 */

	/* 塗りつぶし開始点をスタックに積みます。 */
	stack[0][0] = x0;
	stack[0][1] = y0;
	i_stack = 1;

	/* 塗りつぶし点がなくなるまで... */
	while(i_stack != 0) {
		/* 塗りつぶし点を取り出します。 */
		i_stack--;
		x0 = stack[i_stack][0];
		y0 = stack[i_stack][1];

		/* 既に塗られていたら、飛ばします。 */
		if(point(x0, y0) == color) continue;

		/* 塗られる色の左端を探します。 */
		x1 = x0;
		for(;;) {
			x1--;
			if(x1 < VP_LEFT) break; /* ビューポート外 */
			if(point(x1, y0) != c) break; /* 別色 */
		}
		x1++; /* 一つ行き過ぎているので戻す */

		/* 塗られる色の右端を探します。 */
		x2 = x0;
		for(;;) {
			x2++;
			if(x2 > VP_RIGHT - 1) break; /* ビューポート外 */
			if(point(x2, y0) != c) break; /* 別色 */
		}
		x2--; /* 一つ行き過ぎているので戻す */

		/* 左右の間を塗りつぶします。 */
		for(x = x1; x <= x2; x++) {
			pset(x, y0, color);
			/* ここでpceLCDTrans()を呼ぶと、塗りつぶしの様子を1ピクセル単位で観察できます。 */
			/* pceLCDTrans() */
		}
		/* ここでpceLCDTrans()を呼ぶと、塗りつぶしの様子を1ライン単位で観察できます。 */
		/* pceLCDTrans() */

		/* 上のラインの調査。 */
		if(y0 != VP_TOP) {
			y = y0 - 1;
			x = x1;
			for(;;) {
				/* 塗られる色の始まりを探します。 */
				for(;;) {
					if(x > x2) break;
					if(point(x, y) == c) break;
					x++;
				}
				if(x > x2) break; /* もうない */

				/* 塗られる色の終わりを探します。 */
				for(;;) {
					if(x > x2) break;
					if(point(x, y) != c) break;
					x++;
				}

				/* 塗られる色の終わりの位置をスタックに積みます。 */
				if(i_stack < RENDER_PAINT_STACK_SIZE) {
					stack[i_stack][0] = x - 1; /* 一つ行き過ぎているので戻す */
					stack[i_stack][1] = y;
					i_stack++;
				}
			}
		}

		/* 下のラインの調査。 */
		if(y0 != VP_BOTTOM - 1) {
			y = y0 + 1;
			x = x1;
			for(;;) {
				/* 塗られる色の始まりを探します。 */
				for(;;) {
					if(x > x2) break;
					if(point(x, y) == c) break;
					x++;
				}
				if(x > x2) break; /* もうない */

				/* 塗られる色の終わりを探します。 */
				for(;;) {
					if(x > x2) break;
					if(point(x, y) != c) break;
					x++;
				}

				/* 塗られる色の終わりの位置をスタックに積みます。 */
				if(i_stack < RENDER_PAINT_STACK_SIZE) {
					stack[i_stack][0] = x - 1; /* 一つ行き過ぎているので戻す */
					stack[i_stack][1] = y;
					i_stack++;
				}
			}
		}
	}

#undef RENDER_PAINT_STACK_SIZE
#undef VP_LEFT
#undef VP_RIGHT
#undef VP_TOP
#undef VP_BOTTOM
}
