#include "app.h"
/****************************************************************************
 *	main
 ****************************************************************************/
#ifdef  USE_LUA
static int atpanic(lua_State* L) {
	die("atpanic: %s", luaL_checkstring(L, -1));
}
#endif//USE_LUA
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int main(int argc, char* argv[]) {
	int exitCode = EXIT_SUCCESS;
#ifdef  _DEBUG
	remove("syslog.out");			//毎回ログを削除する事にした。
    #ifndef USE_JOB_DEBUG
	openlog(NULL, LOG_PERROR, LOG_USER);	//デバッグビルドでは、システムログを標準エラーにも出力する。ただし、デバッグジョブを使用する場合は、デバッグジョブのcurses表示と衝突しないよう、標準エラーには出力しない。
    #endif//USE_JOB_DEBUG
#else //_DEBUG
	setlogmask(LOG_UPTO(LOG_INFO));		//リリースビルドでは、LOG_DEBUGを抑制する。
#endif//_DEBUG
#if     (defined(GC_H) && !defined(USE_BISON_FLEX))
	putenv("GC_LOG_FILE=CON");//ログ出力先を指定する。
	GC_set_find_leak(1);//メモリリーク検出を開始する。
#endif//(defined(GC_H) && !defined(USE_BISON_FLEX))
#ifdef  __CLIP_SEH_H__
	SEH_init();
#endif//__CLIP_SEH_H__
//{{DATA
#ifdef  __CLIP_REG_H__
	if(!(TBL_RegTbl = (const unsigned char*)LockResource(LoadResource(NULL, FindResource(NULL, "RegTbl", "RegTbl"))))) { DIE(); }
#endif//__CLIP_REG_H__
#ifdef  __CLIP_ANMS_H__
	if(!(TBL_AnmSeqNo = (const int*)LockResource(LoadResource(NULL, FindResource(NULL, "AnmSeq", "AnmSeq"))))) { DIE(); }
#endif//__CLIP_ANMS_H__
#ifdef  __CLIP_NCSS_H__
	if(!(TBL_NcsSeqNo = (const int*)LockResource(LoadResource(NULL, FindResource(NULL, "NcsSeq", "NcsSeq"))))) { DIE(); }
#endif//__CLIP_NCSS_H__
#ifdef  __CLIP_DIRS_H__
	if(!(TBL_DirTblNo = (const int*)LockResource(LoadResource(NULL, FindResource(NULL, "DirSeq", "DirSeq"))))) { DIE(); }
#endif//__CLIP_DIRS_H__
#ifdef  __CLIP_LEDS_H__
	if(!(TBL_LedSeqNo = (const int*)LockResource(LoadResource(NULL, FindResource(NULL, "LedDef", "LedDef"))))) { DIE(); }
#endif//__CLIP_LEDS_H__
#ifdef  __CLIP_TAPS_H__
	if(!(TBL_TapSeqNo = (const int*)LockResource(LoadResource(NULL, FindResource(NULL, "TapDef", "TapDef"))))) { DIE(); }
#endif//__CLIP_TAPS_H__
#ifdef  __CLIP_AFXB_H__
	if(!(TBL_AfxSeqNo = (const int*)LockResource(LoadResource(NULL, FindResource(NULL, "AfxBin", "AfxBin"))))) { DIE(); }
#endif//__CLIP_AFXB_H__
#ifdef  __CLIP_LOT_H__
	if(!(TBL_LotTblNo = (const int*)LockResource(LoadResource(NULL, FindResource(NULL, "LotTbl", "LotTbl"))))) { DIE(); }
//	LotRom_Init(TBL_LotTblNo, (int (*)())rnd16, &seed);		//┐
//	LotRom_Init(TBL_LotTblNo, (int (*)())mrand48, NULL/*ダミー*/);	//├どれか一つを選択して下さい。
	LotRom_Init(TBL_LotTblNo, (int (*)())WELL_next, &well);		//┘
#endif//__CLIP_LOT_H__
#ifdef  __CLIP_ENM_H__
	if(!(pEnmKey = REG_open_key(TBL_RegTbl, RegKey_EnmDef))) { DIE(); }
#endif//__CLIP_ENM_H__
#ifdef  __CLIP_PRP_H__
	if(!(pPrpKey = REG_open_key(TBL_RegTbl, RegKey_PrpTbl))) { DIE(); }
	PrpHdr_init(phApp, pPrpKey, PrpTbl_App);
#endif//__CLIP_PRP_H__
#ifdef  __CLIP_FSM_H__
	if(!(pFsmKey = REG_open_key(TBL_RegTbl, RegKey_FsmMap))) { DIE(); }
	FsmObj_Init(&foApp, pFsmKey, FsmMap_App);
#endif//__CLIP_FSM_H__
#ifdef  __CLIP_ACT_H__
	aoApp = ActObj_New(NULL/*pParent*/, NULL/*pFsmKey*/, 0/*iFsmMap*/, NULL/*pPrpKey*/, 0/*iPrpTbl*/, NULL/*fnAct*/, 0/*nDirSeqCh*/, 0/*nDataBytes*/);
#endif//__CLIP_ACT_H__
//}}DATA
#ifdef  __CLIP_WKST_H__
	pWS = Worksheet_new();
#endif//__CLIP_WKST_H__
#ifdef  USE_SQLITE
	if(!(db = sqlite_open(":memory:", 0, NULL))) { DIE(); }
#endif//USE_SQLITE
#ifdef  USE_PDCURSES
	initscr();
	refresh();//initscr()の直後にrefresh()を呼び出しておくのはPDCursesの定石です。
#endif//USE_PDCURSES
#ifdef  USE_GDIPLUS
	Gdiplus_Init();
#endif//USE_GDIPLUS
#ifdef  USE_LUA
	if(!(L = luaL_newstate())) { DIE(); }
	lua_atpanic(L, atpanic);
	luaL_openlibs(L);
#endif//USE_LUA
#ifdef  USE_ACTIVETCL
	if(!(interp = Tcl_CreateInterp())) { DIE(); }
	Tcl_FindExecutable(argv[0]);
	if(Tcl_Init(interp) || Tk_Init(interp)) { DIE(); }
	if(Tcl_VarEval(interp, "wm title . {", argv[0], "}", NULL)) { DIE(); }
#endif//USE_ACTIVETCL
	app_init(argc, argv);
	exitCode = app_main(argc, argv);
	app_exit();
#ifdef  USE_ACTIVETCL
	Tcl_DeleteInterp(interp);
#endif//USE_ACTIVETCL
#ifdef  USE_LUA
	lua_close(L);
#endif//USE_LUA
#ifdef  USE_GDIPLUS
	Gdiplus_Exit();
#endif//USE_GDIPLUS
#ifdef  USE_PDCURSES
	endwin();
#endif//USE_PDCURSES
#ifdef  USE_SQLITE
	sqlite_close(db);
#endif//USE_SQLITE
#if     (defined(GC_H) && !defined(USE_BISON_FLEX))
	CHECK_LEAKS();//メモリリークを検出する。
#endif//(defined(GC_H) && !defined(USE_BISON_FLEX))
//{{TEST
	if(isatty(fileno(stdout))) {
		cputs("ok");
		getch();//"curses.h"をインクルードするとgetch()がwgetch(stdscr)に置き換えられるので注意せよ。
	}
//}}TEST
#ifdef  USE_ACTIVETCL
	Tcl_Exit(exitCode);
	/* NOTREACHED */
#endif//USE_ACTIVETCL
	return exitCode;
}
/****************************************************************************
 *	usage
 ****************************************************************************/
__declspec(noreturn) static void usage() {
	//※TODO:
	exit(EXIT_FAILURE);
}
/****************************************************************************
 *	
 ****************************************************************************/
void app_init(int argc, char* argv[]) {
//{{アプリケーション特有の処理
	{
		seed = GetTickCount();
		srand(seed);
		srand48(seed);
		WELL_init(&well, seed);
	}
//}}アプリケーション特有の処理
#ifdef  USE_DXLIB
	cpp_init(argc, argv);
#endif//USE_DXLIB
}
/*--------------------------------------------------------------------------*/
void app_exit() {
//{{アプリケーション特有の処理
	{
		//※TODO:
	}
//}}アプリケーション特有の処理
#ifdef  USE_DXLIB
	cpp_exit();
#endif//USE_DXLIB
}
/*--------------------------------------------------------------------------*/
//{{TEST
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void test1() {
#ifdef  __CLIP_PRP_H__
	{
		ST_PrpHdr* pPrpHdr = PrpHdr_new(pPrpKey, PrpTbl_Dots);
		{
			int x, y, c = 0;
			for(x = 0; x < 3; x++) {
				for(y = 0; y < 2; y++) {
					PrpHdr_set_l(pPrpHdr, x, PrpVar_Dot,x,y, PrpVar_x, -1);
					PrpHdr_set_l(pPrpHdr, y, PrpVar_Dot,x,y, PrpVar_y, -1);
					PrpHdr_set_l(pPrpHdr, c, PrpVar_Dot,x,y, PrpVar_c, -1);
					if(++c > 4) { c = 0; }
				}
			}
		}
		PrpHdr_dump(stderr, pPrpHdr, fnVarFmt, 0);
		{
			PrpHdr_init_l(pPrpHdr, PrpVar_Dot, -1);
		}
		PrpHdr_dump(stderr, pPrpHdr, fnVarFmt, 0);
		PrpHdr_free(pPrpHdr);
		pPrpHdr = NULL;
	}
#ifdef  USE_PDCURSES
	sleep(1);
	clear();
	{
		ST_PrpEdt* pPrpEdt;
		pPrpEdt = PrpEdt_new(phApp->pPrpKey, phApp->iTbl, phApp+1, 0, fnVarFmt, stdscr, PRPEDT_PATH_W, PRPEDT_VAL_W);
		for(;;) {
			refresh();
			if(PrpEdt_exec(pPrpEdt) == KEY_F(12)) { break; }
			PrpEdt_draw(pPrpEdt);
		}
		PrpEdt_free(pPrpEdt);
	}
	clear();
	refresh();
#endif//USE_PDCURSES
#endif//__CLIP_PRP_H__
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void test2() {
#ifdef  USE_LUA
	if(luaL_dostring(L,
		"for i = 1, 10 do\n"
		" print 'Lua ok.'\n"
		"end\n")) { DIE(); }
#endif//USE_LUA
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void test3() {
#ifdef  USE_SQLITE
	int rc, iCol;
	rc = sqlite_exec_printf(db, "CREATE TABLE tbl(id INTEGER PRIMARY KEY, val)", NULL, NULL, NULL);
	if(rc) { DIE(); }
	rc = sqlite_exec_printf(db, "BEGIN", NULL, NULL, NULL);
	if(rc) { DIE(); }
	{
		ST_Recordset_w* rs_w;
		rs_w = Recordset_New(db, "tbl");
			Recordset_SetNN(rs_w, 1234			,"id");
			Recordset_SetNT(rs_w, "One-Two-Three-Four"	,"val");
		Recordset_Insert(rs_w);
		rs_w = Recordset_New(db, "tbl");
			Recordset_SetNN(rs_w, 56			,"id");
			Recordset_SetNT(rs_w, "Five-Six"		,"val");
		Recordset_Insert(rs_w);
		rs_w = Recordset_New(db, "tbl");
			Recordset_SetNN(rs_w, 789			,"id");
			Recordset_SetNT(rs_w, "Seven-Eight-Nine"	,"val");
		Recordset_Insert(rs_w);
		rs_w = Recordset_New(db, "tbl");
			Recordset_SetNT(rs_w, "FIVE-SIX"		,"val");
		Recordset_Update(rs_w, "id = 56");
	}
	rc = sqlite_exec_printf(db, "COMMIT", NULL, NULL, NULL);
	if(rc) { DIE(); }
	{
		ST_Recordset_r* rs_r;
		for(rs_r = Recordset_Open(db, "SELECT * FROM %s ORDER BY id", "tbl");
		    rs_r;
		    rs_r = Recordset_Next(rs_r)) {
			for(iCol = 0; iCol < Recordset_Count(rs_r); iCol++) {
				if(iCol) { putchar(','); }
				printf("%s=%s",
					Recordset_ColIN(rs_r, iCol),
					Recordset_GetIT(rs_r, iCol));
			}
			putchar('\n');
		}
	}
#endif//USE_SQLITE
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void test4() {
#ifdef  USE_BISON_FLEX
	FILE* fp = fopen("prog1.txt", "r");
#if     (YYDEBUG!=0)
//	yydebug = 1;	//nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	yylex_init(fp);
	if((yyparse() != 0) || (yynerrs != 0)) { DIE(); }
	fclose(fp);
#endif//USE_BISON_FLEX
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void test5() {
#ifdef  USE_PDCURSES
	//□参考資料:
	//│「cursesによる端末制御 - List 0. 簡単なcursesプログラム hello.c」
	//│(http://www.kushiro-ct.ac.jp/yanagawa/ex/2-game/01.html)
    //	initscr();	//端末制御の開始
	start_color();	//カラーの設定
	init_pair(1, COLOR_RED, COLOR_BLUE);	//色番号1を赤文字/青地とする
	bkgd(COLOR_PAIR(1));			//色1をデフォルト色とする
	erase();	//画面表示
	move(10, 20);
	addstr("Hello World");
	refresh();
	timeout(-1);
	getch();	//キー入力
    //	endwin();	//端末制御の終了
#endif//USE_PDCURSES
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifdef  __CLIP_RRS_H__
static void job1(ST_RRS* pRRS) { for(;;) { putchar('1'); RRS_yield(pRRS); } }
static void job2(ST_RRS* pRRS) { for(;;) { putchar('2'); RRS_yield(pRRS); } }
static void job3(ST_RRS* pRRS) { for(;;) { putchar('3'); RRS_yield(pRRS); } }
#endif//__CLIP_RRS_H__
static void test6() {
#ifdef  __CLIP_RRS_H__
	int i;
	RRS_new_l(&pRRS, 0, job1, job2, job3, NULL);
	for(i = 0; i < 10; i++) {
		puts("0");
		RRS_yield(pRRS);
	}
	puts("");
	sleep(1);
#endif//__CLIP_RRS_H__
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//}}TEST
#ifdef  __cplusplus
#ifdef  USE_GDIPLUS
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	switch(uMsg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hDC = BeginPaint(hWnd, &ps);
			Graphics* graphics = new Graphics(hDC);
			if(graphics->GetLastStatus()) { DIE(); }
			if(graphics->Clear(Color.Red)) { DIE(); }
			wchar_t* fname = strdup_ShiftJisToUnicode("C:/usr/PIECE/HTML/IMAGE/MENULOGO.GIF");
			Image* image = new Image(fname);
			if(image->GetLastStatus()) { DIE(); }
			free(fname);
			if(graphics->DrawImage(image, 0, 0)) { DIE(); }
			delete image;
			delete graphics;
			EndPaint(hWnd, &ps);
		}
		return 0;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
#endif//USE_GDIPLUS
#endif//__cplusplus
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int app_main(int argc, char* argv[]) {
	int exitCode = EXIT_SUCCESS;
//{{TEST
	test1();
	test2();
	test3();
	test4();
	test5();
	test6();
//}}TEST
#if 0
	{
		int opt;
		opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
		while((opt = getopt(argc, argv, "eh")) != -1) {	//コマンドラインオプションを処理する。
			switch(opt) {
		    #ifdef  USE_BISON_FLEX
			case 'e':
				{
					//エラーログファイル名を作成する。
					// - カレントフォルダに'実行ファイル名.err'という名前で生成する事にした。この動作は、'lk33.exe'の仕様を参考にした。
					char fname[MAX_FNAME];
					splitpath(argv[0], NULL, NULL, fname, NULL);
					makepath(err_path, NULL, NULL, fname, ".err");
					//'-e'オプションが指定されたら、最初に確実に、既存のエラーログファイルを削除しておく。
					//前回に生成したエラーログが残っていて、今回生成したエラーログと混同する危険を避けるためである。
					//尚、'-e'オプションが指定されなかった場合は、既存のエラーログファイルを削除する処理は行わない。
					remove(err_path);
				}
				break;
		    #endif//USE_BISON_FLEX
			case 'h':
			default:
				usage();
			}
		}
	}
	{
		char* glbuf = NULL;	//┬getline()用バッファ
		int   glcap = 0;	//┘
		FILE* fp = stdin;	//入力ファイルが指定されていなければ、標準入力を一回処理する。
		do {
			if(optind < argc) {	//入力ファイルが指定されていたら…
				fp = fopen(argv[optind], "r");		//入力ファイルを開く。
				if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", argv[optind]); }
			}
			while(getline(&glbuf, &glcap, fp) != EOF) {	//一行読み込む。ファイル終端でなければ…
				//※TODO:
			}
			if(optind < argc) {	//入力ファイルが指定されていたら…
				fclose(fp);	//入力ファイルを閉じる。
				optind++;	//次の入力ファイルへ進める。
			}
		} while(optind < argc);
		free(glbuf);	//getline()用バッファを開放する。
	}
#endif
#ifdef  __cplusplus
#ifdef  USE_GDIPLUS
	{
		WNDCLASS wc;
		HWND hWnd;
		MSG msg;
		memset(&wc, 0, sizeof wc);
		wc.style = (CS_HREDRAW|CS_VREDRAW);
		wc.lpfnWndProc = WndProc;
		wc.hInstance = GetModuleHandle(NULL);
		wc.hCursor = LoadCursor(NULL, IDC_ARROW);
		wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
		wc.lpszClassName = program_invocation_short_name;
		if(!RegisterClass(&wc)) { DIE(); }
		hWnd = CreateWindow(
			program_invocation_short_name,
			program_invocation_short_name,
			WS_OVERLAPPEDWINDOW,
			CW_USEDEFAULT, CW_USEDEFAULT,
			CW_USEDEFAULT, CW_USEDEFAULT,
			NULL, NULL, wc.hInstance, NULL);
		if(!hWnd) { DIE(); }
		ShowWindow(hWnd, SW_SHOW);
		UpdateWindow(hWnd);
		while(GetMessage(&msg, NULL, 0, 0)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
#endif//USE_GDIPLUS
#endif//__cplusplus
#if     (defined(USE_ACTIVETCL) && !defined(USE_DXLIB))
	while(Tk_GetNumMainWindows()) {
		while(Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT)) { /** no job **/ }
		//※TODO:
	}
#endif//(defined(USE_ACTIVETCL) && !defined(USE_DXLIB))
#ifdef  __CLIP_RRS_H__
	//汎用の同期ルートを初期化する。
	syncRoot.req = 2/*変更可*/;
	//Round Robin Scheduler (RRS)を初期化する。
	RRS_new_l(&pRRS, 0/*stacksize*/,
	    #ifdef  USE_JOB_DEBUG
		job_debug,	//デバッグジョブ
	    #endif//USE_JOB_DEBUG
		job_main,	//計算ジョブ
		//{{TODO:ここにジョブ関数を追加して下さい。}}
		NULL/*終端*/);
#endif//__CLIP_RRS_H__
#ifdef  __CLIP_MSGH_H__
	//メッセージハンドシェイクを初期化する。
	MsgHsk_Init(&stMsgHsk, pRRS);
#endif//__CLIP_MSGH_H__
#ifdef  USE_DXLIB
	exitCode = cpp_main(argc, argv);	//UIジョブ
#else //USE_DXLIB
    #ifdef  __CLIP_RRS_H__
	for(;;) { RRS_yield(pRRS); }	//UIジョブを実行せず,かつ,RRSを使用する場合は、ここで無限ループする事にしました。そうしないとすぐにプロセスが終了してしまい、計算ジョブを実行する前にプロセスが終了してしまうからです。
    #endif//__CLIP_RRS_H__
#endif//USE_DXLIB
	return exitCode;
}
/****************************************************************************
 *	デバッグジョブ
 ****************************************************************************/
#ifdef  USE_JOB_DEBUG
void job_debug(ST_RRS* pRRS) {
	ST_PrpEdt* pPrpEdt = PrpEdt_new(phApp->pPrpKey, phApp->iTbl, phApp + 1, 0, fnVarFmt, stdscr, PRPEDT_PATH_W, PRPEDT_VAL_W);
	nodelay(stdscr, 1);
	curs_set(0);
	for(;;) {
		wnoutrefresh(stdscr);
		doupdate();
		RRS_yield(pRRS);
		if(PrpEdt_exec(pPrpEdt) == '\x1B') { break; }
		PrpEdt_draw(pPrpEdt);
	}
	PrpEdt_free(pPrpEdt);
	exit(EXIT_SUCCESS);
}
#endif//USE_JOB_DEBUG
/****************************************************************************
 *	計算ジョブ
 ****************************************************************************/
#ifdef  __CLIP_RRS_H__
void job_main(ST_RRS* pRRS) {
//{{TEST
	int i = 0;
//}}TEST
	for(;;) {
	    #ifdef  USE_DXLIB
		schedule();	//計算ジョブからもschedule()を呼び出せるようにしました。計算ジョブ特有の周期処理が不要な場合はRRS_yield(pRRS)を呼び出しても構いません。
	    #else //USE_DXLIB
		RRS_yield(pRRS);
	    #endif//USE_DXLIB
//{{TEST
#ifdef  _DEBUG
		syslog(LOG_DEBUG, "job#%d/%d: %d", RRS_i_job(pRRS)+1, RRS_n_job(pRRS));
#endif//_DEBUG
		printf("<%d>\n", i);
		i += 10;
//}}TEST
	}
	exit(EXIT_SUCCESS);
}
#endif//__CLIP_RRS_H__
//{{DATA
/****************************************************************************
 *	変数フォーマッタ
 ****************************************************************************/
#ifdef  __CLIP_PRP_H__
void fnVarFmt(char* buf/*[cap]*/, int cap, int iFmt, int iVal) {
	if(iVal >= 0) {
#ifdef  __CLIP_ENM_H__
		int iRegKey;
		switch(iFmt) {
		default:DIE();
		case PrpFmt_Color:	iRegKey = RegKey_Color;		break;
		//{{TODO:ここにPrpFmt_〜の処理を追加して下さい。}}
		}
		snprintf(buf, cap, "%d(%s)", iVal, EnmDef_ToStr(pEnmKey, iRegKey, iVal));
#else //__CLIP_ENM_H__
		switch(iFmt) {
		default:DIE();
		case PrpFmt_Color:
			{
				//'MkNamDef.bat Define.h'で生成,編集しました。
				static const char* const TBL_NamDef_Color[5]={
				"白",     //0 Color_White
				"灰(淡)", //1 Color_GlayW
				"灰(濃)", //2 Color_GlayB
				"黒",     //3 Color_Black
				"マスク", //4 Color_Mask 
				};
				if((unsigned)iVal >= ARRAY_SIZE(TBL_NamDef_Color)) { DIE(); }
				snprintf(buf, cap, "%d(%s)", iVal, TBL_NamDef_Color[iVal]);
			}
			break;
		//{{TODO:ここにPrpFmt_〜の処理を追加して下さい。}}
		}
#endif//__CLIP_ENM_H__
	} else {
		snprintf(buf, cap, "%d", iVal);
	}
}
#endif//__CLIP_PRP_H__
//}}DATA
