/*	
 *	main.c
 *
 *	CLiP Windows アプリケーションの雛型
 *	Copyright (C) 2001-2002 Naoyuki Sawa
 *
 *	* Wed Jun  5 11:45:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 */
#include <clip.h>

/* ※TODO: 以下の設定は、プロジェクト毎に変更してください。
 *	プロジェクト→設定→Ｃ／Ｃ＋＋→プリプロセッサ→インクルードファイルのパス
 *	プロジェクト→設定→リンク→インプット→追加ライブラリのパス
 */

/****************************************************************************
 *	定数・構造体
 ****************************************************************************/

#define APPNAME		"MyApp"

#define BIN_FNAME	"spline.bin"
#define TXT_FNAME	"spline.txt"

#define DISP_X		128
#define DISP_Y		 88

#define MAX_LINES	 16
#define MAX_POINTS	 64
typedef struct _LINE {
	int n;
	POINT p[MAX_POINTS];
} LINE;

/****************************************************************************
 *	グローバル変数
 ****************************************************************************/

HINSTANCE hinst;
HWND main_wnd;

unsigned char vbuff[DISP_X * DISP_Y];
unsigned char zbuff[DISP_X * DISP_Y];
SURFACE surface = { DISP_X, DISP_Y, vbuff, zbuff };
RENDER render;

LINE lines[MAX_LINES];
int i_line;

/****************************************************************************
 *	関数宣言
 ****************************************************************************/

LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
void on_draw();

/****************************************************************************
 *	main
 ****************************************************************************/

int
main()
{
	int retval, i, j, n, x, y, w, h;
	LINE* line;
	POINT* p;
	WNDCLASS wc;
	RECT wr, cr;
	MSG msg;
	FILE* fp;

	fp = fopen(BIN_FNAME, "rb");
	if(fp != NULL) {
		retval = fread(lines, 1, sizeof lines, fp);
		fclose(fp);
		if(retval != sizeof lines) {
			memset(lines, 0, sizeof lines);
		}
	}

	/* インスタンスハンドルを取得します。 */
	hinst = GetModuleHandle(NULL);

	/* 仮想画面を設定します。 */
	pceLCDSetBuffer((PIECE_VRAM*)&surface);
	render_init(&render, &surface, 0, 0, 0, 0, 0, 0, 0);

	/* ウインドウクラスを登録します。 */
	memset(&wc, 0, sizeof wc);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = wnd_proc;
	wc.hInstance = hinst;
	wc.hCursor = LoadCursor(NULL, IDC_CROSS);
	wc.lpszClassName = APPNAME;
	RegisterClass(&wc);

	/* メインウインドウを作成します。 */
	main_wnd = CreateWindow(APPNAME, APPNAME,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hinst, NULL);
	GetWindowRect(main_wnd, &wr);
	GetClientRect(main_wnd, &cr);
	w = (wr.right - wr.left) - (cr.right - cr.left) + DISP_X;
	h = (wr.bottom - wr.top) - (cr.bottom - cr.top) + DISP_Y;
	x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
	y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
	MoveWindow(main_wnd, x, y, w, h, FALSE);
	ShowWindow(main_wnd, SW_SHOW);
	UpdateWindow(main_wnd);

	/* メッセージループ。 */
	while(GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	fp = fopen(BIN_FNAME, "wb");
	if(fp != NULL) {
		retval = fwrite(lines, 1, sizeof lines, fp);
		fclose(fp);
		if(retval != sizeof lines) {
			remove(BIN_FNAME);
		}
	}
	fp = fopen(TXT_FNAME, "w");
	if(fp != NULL) {
		for(i = 0, line = lines; i < MAX_LINES; i++, line++) {
			n = line->n;
			if(n == 0) continue;
			fprintf(fp, "{");
			for(j = 0, p = line->p; j < n; j++, p++) {
				fprintf(fp, "{%d,%d}", p->x, p->y);
				if(j != n - 1) fprintf(fp, ",");
			}
			fprintf(fp, "}\n");
		}
		fclose(fp);
	}

	return msg.wParam;
}

/****************************************************************************
 *	wnd_proc
 ****************************************************************************/

LRESULT CALLBACK
wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rc;
	int i, j, n;

	switch(msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		on_draw();
		hdc = BeginPaint(hwnd, &ps);
		SetStretchBltMode(hdc, COLORONCOLOR);
		GetClientRect(hwnd, &rc);
		pceLCDTrans(hdc, 0, 0, rc.right, rc.bottom, 0, 0, 0, 0, NULL);
		EndPaint(hwnd, &ps);
		return 0;
	case WM_KEYDOWN:
		switch(wparam) {
		case VK_ESCAPE:
			SendMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;
		case VK_SPACE:
			/* 現在のライン以外でノードのあるラインを選択。 */
			for(i = 1; i < MAX_LINES; i++) {
				j = (i_line + i) % MAX_LINES;
				if(lines[j].n != 0) {
					i_line = j;
					break;
				}
			}
			InvalidateRect(main_wnd, NULL, FALSE);
			return 0;
		case VK_RETURN:
			/* 現在のラインを含めてノードのないラインを選択。 */
			for(i = 0; i < MAX_LINES; i++) {
				j = (i_line + i) % MAX_LINES;
				if(lines[j].n == 0) {
					i_line = j;
					break;
				}
			}
			InvalidateRect(main_wnd, NULL, FALSE);
			return 0;
		case VK_DELETE:
			/* 現在のラインを削除し、前にノードのあるラインへ。 */
			lines[i_line].n = 0;
			for(i = MAX_LINES - 1; i > 0; i--) {
				j = (i_line + i) % MAX_LINES;
				if(lines[j].n != 0) {
					i_line = j;
					break;
				}
			}
			if(i == 0) i_line = 0;
			InvalidateRect(main_wnd, NULL, FALSE);
			return 0;
		}
		break;
	case WM_LBUTTONDOWN:
		/* 頂点数が限界でなければ、頂点を追加。 */
		if((n = lines[i_line].n) < MAX_POINTS) {
			SetCapture(hwnd);
			GetClientRect(hwnd, &rc);
			lines[i_line].p[n].x = (short)lparam         * DISP_X / rc.right ;
			lines[i_line].p[n].y = (short)(lparam >> 16) * DISP_Y / rc.bottom;
			lines[i_line].n = n + 1;
		}
		InvalidateRect(main_wnd, NULL, FALSE);
	case WM_MOUSEMOVE:
		if(GetCapture() == hwnd) {
			/* 頂点があれば、最終頂点を移動。 */
			if((n = lines[i_line].n) > 0) {
				GetClientRect(hwnd, &rc);
				lines[i_line].p[n - 1].x = (short)lparam         * DISP_X / rc.right ;
				lines[i_line].p[n - 1].y = (short)(lparam >> 16) * DISP_Y / rc.bottom;
			}
			InvalidateRect(main_wnd, NULL, FALSE);
		}
		return 0;
	case WM_LBUTTONUP:
		if(GetCapture() == hwnd) {
			ReleaseCapture();
			InvalidateRect(main_wnd, NULL, FALSE);
		}
		return 0;
	case WM_RBUTTONDOWN:
		if(lines[i_line].n > 0) lines[i_line].n--;
		InvalidateRect(main_wnd, NULL, FALSE);
		return 0;
	}
	return DefWindowProc(hwnd, msg, wparam, lparam);
}

/****************************************************************************/

#define DIV	10

void
spline_seg(short t, short div,
	   short x0, short y0,
	   short x1, short y1,
	   short x2, short y2,
	   short x3, short y3,
	   int color)
{
	float t1, t2;
	short vx1, vy1, vx2, vy2;
	short dx1, dy1, dx2, dy2;

	t1 = (float) t      / div;
	t2 = (float)(t + 1) / div;

	vx1 = (x2 - x0) / 2;
	vy1 = (y2 - y0) / 2;
	vx2 = (x3 - x1) / 2;
	vy2 = (y3 - y1) / 2;

	dx1 = hermite1(t1, x1, x2, vx1, vx2);
	dy1 = hermite1(t1, y1, y2, vy1, vy2);
	dx2 = hermite1(t2, x1, x2, vx1, vx2);
	dy2 = hermite1(t2, y1, y2, vy1, vy2);

	render_line(&render, dx1, dy1, dx2, dy2, color);
}



void
on_draw()
{
	LINE* line;
	POINT p0, p1, p2, p3;
	int i, j, k, n;

	memset(vbuff, 0, sizeof vbuff);

	for(i = 0, line = lines; i < MAX_LINES; i++, line++) {
		n = line->n;

		if(n >= 2) {
			for(j = 0; j < n - 1; j++) {
				p1 = line->p[j    ];
				p2 = line->p[j + 1];
				if(j > 0) {
					p0 = line->p[j - 1];
				} else {
					p0.x = p1.x + (p1.x - p2.x) * 2;
					p0.y = p1.y + (p1.y - p2.y) * 2;
				}
				if(j < n - 2) {
					p3 = line->p[j + 2];
				} else {
					p3.x = p2.x + (p2.x - p1.x) * 2;
					p3.y = p2.y + (p2.y - p1.y) * 2;
				}
				for(k = 0; k < DIV; k++) {
					spline_seg((short)k, (short)DIV,
						   (short)p0.x, (short)p0.y,
						   (short)p1.x, (short)p1.y,
						   (short)p2.x, (short)p2.y,
						   (short)p3.x, (short)p3.y,
						   i == i_line ? 2 : 1);
/*
					x1 = hermite1((float)k       / DIV, p1.x, p2.x, v1.x, v2.x);
					y1 = hermite1((float)k       / DIV, p1.y, p2.y, v1.y, v2.y);
					x2 = hermite1((float)(k + 1) / DIV, p1.x, p2.x, v1.x, v2.x);
					y2 = hermite1((float)(k + 1) / DIV, p1.y, p2.y, v1.y, v2.y);
					render_line(&render, x1, y1, x2, y2, i == i_line ? 2 : 1);
*/
				}
			}
		}

		for(j = 0; j < n; j++) {
			render_point(&render, line->p[j].x, line->p[j].y,
				i == i_line ? 3 : 2);
		}
	}
}
