#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__
#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_PCL
	co_thread_init();
#endif//USE_PCL
#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_PCL
	co_thread_cleanup();
#endif//USE_PCL
#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))
#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);
	}
//}}アプリケーション特有の処理
}
/*--------------------------------------------------------------------------*/
void app_exit() {
//{{アプリケーション特有の処理
	{
		//※TODO:
	}
//}}アプリケーション特有の処理
}
/*--------------------------------------------------------------------------*/
#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








/****************************************************************************
 *	フィールド定数
 ****************************************************************************/
#define MAX_COLS	12		//最大列数
#define MAX_ROWS	9		//最大行数
/*--------------------------------------------------------------------------*/
#define Pai_Max		((9*3)+4+3)	//牌の種類	1〜Pai_Max
/****************************************************************************
 *	フィールド構造体
 ****************************************************************************/
//方向テーブル
typedef struct _ST_Dir {
	char		vCol, vRow;
} ST_Dir;
const ST_Dir TBL_Dir[4]={{1,0},{-1,0},{0,1},{0,-1}};	//0=右,1=左,2=下,3=上				PAD_*やTRG_*の並び順に合わせました。
/*--------------------------------------------------------------------------*/
//セル
typedef struct _ST_Cell {
	uint8_t		iPai;				//牌の種類					0=空き,1〜Pai_Max=牌
	uint8_t		iPutOrd;			//何回目に配置した牌か?				0=空き,1=一回目,2=二回目,3=三回目,...
	uint8_t		iSide;				//配置タイプ					0=空き,1=動かす(動かさない事も有る)端,2=内側,3=動かさない端
	uint8_t		fBlank2;			//空き数が2以上である方向のビットマスク		bit0=右,bit1=左,bit2=下,bit3=上
	uint8_t		TBL_Blank[4];			//各方向への空き数				0〜max(MAX_COLS,MAX_ROWS)
	//↑ここより上のフィールドは、Field_Gen()にて格納し、それ以降、ゲーム中には変更しない。
	//↓ここより下のフィールドは、ゲーム中に変更する。
	uint8_t		iGetOrd;			//何回目に消した牌か?				0=空き,又は,まだ消していない,1=一回目,2=二回目,3=三回目,...
} ST_Cell;
/*--------------------------------------------------------------------------*/
//フィールド
typedef struct _ST_Field {
	uint8_t		iColMin, iColMax;		//列番号の最小値と最大値
	uint8_t		iRowMin, iRowMax;		//行番号の最小値と最大値
	ST_Cell		TBL_Cell[MAX_ROWS][MAX_COLS];	//セル配列
} ST_Field;
/****************************************************************************
 *	フィールド初期化処理
 ****************************************************************************/
//セルへのポインタを取得する。
ST_Cell* Field_GetCell(ST_Field* pField, int iCol, int iRow) {
	if((iCol < pField->iColMin) || (iCol > pField->iColMax) ||
	   (iRow < pField->iRowMin) || (iRow > pField->iRowMax)) { DIE(); }	//バグ
	return &pField->TBL_Cell[iRow][iCol];
}
/*--------------------------------------------------------------------------*/
//全てのセルの、TBL_Blank[],及び,fBlank2フィールドを更新する。
void Field_UpdateBlank(ST_Field* pField) {
	int iDir;
	for(iDir = 0; iDir < 4; iDir++) {
		int iCol1 = pField->iColMin, iRow1 = pField->iRowMin;	//数えない方向の起点位置
		for(;;) {
			//空きが連続する方向の逆方向に数えて行く。
			int iCol2 = (TBL_Dir[iDir].vCol < 0) ? pField->iColMin : (TBL_Dir[iDir].vCol > 0) ? pField->iColMax : iCol1;
			int iRow2 = (TBL_Dir[iDir].vRow < 0) ? pField->iRowMin : (TBL_Dir[iDir].vRow > 0) ? pField->iRowMax : iRow1;
			int nBlank = 0;	//空き数の初期値を0としておく。
			for(;;) {
				ST_Cell* pCell = Field_GetCell(pField, iCol2, iRow2);
				nBlank = pCell->iPutOrd ? 0 : (nBlank + 1);	//空きでなければ空き数を0に戻す。空きならば空き数を増やす。	Field_Gen()の途中から呼び出された時点では、iPaiはまだ格納されていないので、iPaiではなくiPutOrdで判断する事に注意しろ。
				pCell->TBL_Blank[iDir] = nBlank;
				if(nBlank >= 2) {
					pCell->fBlank2 |=  (1<<iDir);	//空き数が2以上ならば、この方向のビットをセットする。
				} else {
					pCell->fBlank2 &= ~(1<<iDir);	//空き数が2未満ならば、この方向のビットをクリアする。
				}
				//数える方向の位置を次へ進めて、フィールド外になったらループを抜ける。
				iCol2 -= TBL_Dir[iDir].vCol;
				iRow2 -= TBL_Dir[iDir].vRow;
				if((iCol2 < pField->iColMin) || (iCol2 > pField->iColMax)) { break; }
				if((iRow2 < pField->iRowMin) || (iRow2 > pField->iRowMax)) { break; }
			}
			//数えない方向の位置を次へ進めて、フィールド外になったらループを抜ける。
			iCol1 += !TBL_Dir[iDir].vCol;
			iRow1 += !TBL_Dir[iDir].vRow;
			if((iCol1 < pField->iColMin) || (iCol1 > pField->iColMax)) { break; }
			if((iRow1 < pField->iRowMin) || (iRow1 > pField->iRowMax)) { break; }
		}
	}
}
/*--------------------------------------------------------------------------*/
//フィールドの初期配置を生成する。
void Field_Gen(ST_Field* pField) {
	int iPutOrd = 0;
	//二方向に二連以上の空きが有る位置への配置を試みるループ。
	for(;;) {
		//全てのセルの、TBL_Blank[],及び,fBlank2フィールドを更新する。
		Field_UpdateBlank(pField);
		{
			uint8_t TBL_DirOrd[][2/*0=動かす方向,1=挟む方向*/]={{0,1},{0,2},{0,3},{1,0},{1,2},{1,3},{2,0},{2,1},{2,3},{3,0},{3,1},{3,2}};
			int iDirOrd, iDir1, iDir2, fBlank2, iCol = -1/*警告抑制*/, iRow, nCount, nLen;
			ST_Cell* pCell;
			//方向の検索順序をシャッフルする。
			shuffle(TBL_DirOrd, ARRAY_SIZE(TBL_DirOrd), sizeof TBL_DirOrd[0], &seed);
			//条件に合う位置と方向を数える。
			nCount = 0;
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir1 = TBL_DirOrd[iDirOrd][0];	//動かす方向
				iDir2 = TBL_DirOrd[iDirOrd][1];	//挟む方向
				fBlank2 = (1 << iDir1) | (1 << iDir2);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if((pCell->fBlank2 & fBlank2) == fBlank2) { nCount++; }
					}
				}
			}
			if(!nCount) { break; }	//条件に合う位置と方向が無ければ、一方向に二連の空きが有る位置への配置を試みるループへ移行する。	条件に合う位置と方向が無買った場合、一方向に三連以上の空きが有る位置は無いはずです。もし一方向に三連以上の空きが有れば、中間の位置を起点として二方向に二連以上の空きが有る位置として検出されるはずだからです。
			//条件に合う位置と方向を抽選する。
			nCount = RND32_RANGE(seed, 0, nCount);
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir1 = TBL_DirOrd[iDirOrd][0];	//動かす方向
				iDir2 = TBL_DirOrd[iDirOrd][1];	//挟む方向
				fBlank2 = (1 << iDir1) | (1 << iDir2);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if((pCell->fBlank2 & fBlank2) == fBlank2) { nCount--; }
						if(nCount < 0) { break; }
					}
					if(nCount < 0) { break; }
				}
				if(nCount < 0) { break; }
			}
			if(nCount >= 0) { DIE(); }	//バグ
			//配置番号を進めておく。
			iPutOrd++;
			//動かす距離を抽選する。
			pCell = Field_GetCell(pField, iCol, iRow);
#if 0
			nLen = RND32_RANGE(seed, 0, pCell->TBL_Blank[iDir1]);	//0≦nLen≦(動かす方向への空き数-1)	動かさない事も有るバージョン
#else			//↑↓どちらでも構いません。とりあえず、せっかく動かせるのだから、必ず動かす事にしました。
			nLen = RND32_RANGE(seed, 1, pCell->TBL_Blank[iDir1]);	//1≦nLen≦(動かす方向への空き数-1)	必ず動かすバージョン
#endif
			//動かす(動かさない事も有る)牌を配置する。
			pCell = Field_GetCell(pField,
				iCol + (TBL_Dir[iDir1].vCol * nLen),
				iRow + (TBL_Dir[iDir1].vRow * nLen));
			if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
			pCell->iPutOrd = iPutOrd;
			pCell->iSide = 1/*動かす(動かさない事も有る)端*/;
			//挟む長さ(終端も含む)を抽選する。
			pCell = Field_GetCell(pField, iCol, iRow);
			nLen = RND32_RANGE(seed, 1, pCell->TBL_Blank[iDir2]);	//1≦nLen≦(挟む方向への空き数-1)
			//内側の牌(挟む長さが2ならば存在しない),及び,動かさない牌を配置する。
			do {
				iCol += TBL_Dir[iDir2].vCol;
				iRow += TBL_Dir[iDir2].vRow;
				pCell = Field_GetCell(pField, iCol, iRow);
				if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
				pCell->iPutOrd = iPutOrd;
				pCell->iSide = (nLen > 1) ? 2/*内側*/ : 3/*動かさない端*/;
			} while(--nLen);
		}
	}
	//一方向に二連の空きが有る位置への配置を試みるループ
	for(;;) {
		//全てのセルの、TBL_Blank[],及び,fBlank2フィールドを更新する。
		Field_UpdateBlank(pField);
		{
			uint8_t TBL_DirOrd[]={0,1,2,3};
			int iDirOrd, iDir, fBlank2, iCol = -1/*警告抑制*/, iRow, nCount, nLen;
			ST_Cell* pCell;
			//方向の検索順序をシャッフルする。
			shuffle(TBL_DirOrd, ARRAY_SIZE(TBL_DirOrd), sizeof TBL_DirOrd[0], &seed);
			//条件に合う位置と方向を数える。
			nCount = 0;
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir = TBL_DirOrd[iDirOrd];	//挟む方向
				fBlank2 = (1 << iDir);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if(pCell->fBlank2 & fBlank2) { nCount++; }
					}
				}
			}
			if(!nCount) { break; }	//条件に合う位置と方向が無ければ、配置を終了する。
			//条件に合う位置と方向を抽選する。
			nCount = RND32_RANGE(seed, 0, nCount);
			for(iDirOrd = 0; iDirOrd < ARRAY_SIZE(TBL_DirOrd); iDirOrd++) {
				iDir = TBL_DirOrd[iDirOrd];	//挟む方向
				fBlank2 = (1 << iDir);
				for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
					for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
						pCell = Field_GetCell(pField, iCol, iRow);
						if((pCell->fBlank2 & fBlank2) == fBlank2) { nCount--; }
						if(nCount < 0) { break; }
					}
					if(nCount < 0) { break; }
				}
				if(nCount < 0) { break; }
			}
			if(nCount >= 0) { DIE(); }	//バグ
			//配置番号を進めておく。
			iPutOrd++;
			//始点の牌を配置する。
			pCell = Field_GetCell(pField, iCol, iRow);
			if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
			pCell->iPutOrd = iPutOrd;
			pCell->iSide = 1/*動かす(動かさない事も有る)端*/;
			//挟む長さ(終端も含む)を抽選する。
			pCell = Field_GetCell(pField, iCol, iRow);
			nLen = RND32_RANGE(seed, 1, pCell->TBL_Blank[iDir]);	//1≦nLen≦(挟む方向への空き数-1)
			if(nLen != 1) { DIE(); }	//一応抽選しているものの、実際には空き数は2であるはずなので、挟む長さ(終端も含む)は常に1となるはずだ。
			//内側の牌(挟む長さが2ならば存在しない),及び,動かさない牌を配置する。
			do {
				iCol += TBL_Dir[iDir].vCol;
				iRow += TBL_Dir[iDir].vRow;
				pCell = Field_GetCell(pField, iCol, iRow);
				if(pCell->iPai || pCell->iPutOrd || pCell->iSide) { DIE(); }	//バグ
				pCell->iPutOrd = iPutOrd;
				pCell->iSide = (nLen > 1) ? 2/*内側*/ : 3/*動かさない端*/;
			} while(--nLen);
		}
	}
	//牌の種類を決定する処理。
	{
		uint8_t TBL_PaiOrd[Pai_Max * 4/*4枚づつ*/];
		int i, iCol, iRow;
		ST_Cell* pCell;
		//牌の種類の配列を初期化する。
		for(i = 0; i < ARRAY_SIZE(TBL_PaiOrd); i++) {
			TBL_PaiOrd[i] = (i / 4) + 1;	//{1,1,1,1,2,2,2,2,3,3,3,3,...,Pai_Max,Pai_Max,Pai_Max,Pai_Max}
		}
		//まず、二個組みでシャフルする。(両端の牌)
		shuffle(TBL_PaiOrd, ARRAY_SIZE(TBL_PaiOrd) / 2, (sizeof TBL_PaiOrd[0]) * 2, &seed);
		//配置した回数の分は二個組みを維持して、それ以降はばらばらにシャフルする。(内側の牌)
		shuffle(&TBL_PaiOrd[iPutOrd * 2], ARRAY_SIZE(TBL_PaiOrd) - (iPutOrd * 2), sizeof TBL_PaiOrd[0], &seed);
		//配置する。
		i = iPutOrd * 2;	//ばらばらにシャフルした先頭位置
		for(iRow = pField->iRowMin; iRow <= pField->iRowMax; iRow++) {
			for(iCol = pField->iColMin; iCol <= pField->iColMax; iCol++) {
				pCell = Field_GetCell(pField, iCol, iRow);
				if(pCell->iPutOrd) {
					if(pCell->iPai) { DIE(); }	//バグ
					switch(pCell->iSide) {
					default:DIE();
					case 1/*動かす(動かさない事も有る)端*/:
					case 3/*動かさない端*/:
						pCell->iPai = TBL_PaiOrd[(pCell->iPutOrd - 1) * 2];
						break;
					case 2/*内側*/:
						pCell->iPai = TBL_PaiOrd[i++];
						break;
					}
					if((pCell->iPai < 1) || (pCell->iPai > Pai_Max)) { DIE(); }	//バグ
				}
			}
		}
	}
}
/*--------------------------------------------------------------------------*/
//フィールドを初期化する。
void Field_Init(ST_Field* pField, int iColMin, int iColMax, int iRowMin, int iRowMax) {
	//構造体をクリアする。
	memset(pField, 0, sizeof(ST_Field));
	//列番号と行番号の最小値と最大値を格納する。
	if((((unsigned)iColMin >= MAX_COLS) || ((unsigned)iColMax >= MAX_COLS) || (iColMin > iColMax)) ||
	   (((unsigned)iRowMin >= MAX_ROWS) || ((unsigned)iRowMax >= MAX_ROWS) || (iRowMin > iRowMax))) { DIE(); }
	pField->iColMin = iColMin;
	pField->iColMax = iColMax;
	pField->iRowMin = iRowMin;
	pField->iRowMax = iRowMax;
	//フィールドの初期配置を生成する。
	Field_Gen(pField);
}








int app_main(int argc, char* argv[]) {
	ST_Field stField;
	Field_Init(&stField, 0, MAX_COLS/2 - 1, 0, MAX_ROWS/2 - 1);
	Field_UpdateBlank(&stField);

	{
		int col, row;
		for(row = stField.iRowMin; row <= stField.iRowMax; row++) {
			for(col = stField.iColMin; col <= stField.iColMax; col++) {
				ST_Cell* pCell = Field_GetCell(&stField, col, row);
				printf("%2d ", pCell->iPutOrd);
			}
			puts("");
		}
	}
		puts("");
	{
		int col, row;
		for(row = stField.iRowMin; row <= stField.iRowMax; row++) {
			for(col = stField.iColMin; col <= stField.iColMax; col++) {
				ST_Cell* pCell = Field_GetCell(&stField, col, row);
				printf("%2d ", pCell->iPai);
			}
			puts("");
		}
	}

	getch();

	return 0;
}
