//////////////////////////////////////////
// MazeProc.h
// Copyright (c) 06/21/2020 by BCCSkelton
//////////////////////////////////////////

/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//ウィンドウメッセージ関数
/////////////////////////////////
bool CMyWnd::OnCreate(WPARAM wParam, LPARAM lParam) {

	//コモンコントロールの初期化
	InitCommonControls();

	//ステータスバー登録−Init(hWnd, hIinstance, ID, (以下省略可）Style)
	SBar.Init(m_hWnd, m_hInstance, STATUSBAR);
	//ステータスバー区画設定
	int sec[2] = {200, 400};
	SBar.SetSection(2, sec);
	//ステータスバー文字列設定
	SBar.SetText(0, "Maze Ver1.0");
	SBar.SetText(1, "Maze size");

	//仮想ウィンドウの初期化
	cvs.SetCanvas(m_hWnd);
	cvs.BrSelection(1);		//塗潰し

	//外部ビットマップを読みこむ
	Brick.LoadBMP(m_hWnd, "IDI_BRICK");
	Ball.LoadBMP(m_hWnd, "IDI_BALL");
	Back.LoadBMP(m_hWnd, "IDI_BACK");
	Exit.LoadBMP(m_hWnd, "IDI_EXIT");

	//ウィンドウサイズの初期化
	RECT w, c, s;
	GetWindowRect(m_hWnd, &w);	//ウィンドウの外寸を求める
	GetClientRect(m_hWnd, &c);	//ウインドウの内寸を求める
	GetWindowRect(SBar.GetHandle(), &s);	//ステータスバーの高さ
	SetWindowPos(m_hWnd, HWND_TOP, 0, 0,
	 	MSIZE *UNIT + (w. right - w.left - c.right + c.left),
		MSIZE *UNIT + (w. bottom - w.top - c.bottom + c.top + s.bottom - s.top),
		SWP_NOMOVE);	//必要な迷路サイズに内外寸差とステータスバーの高さを加算

	//ウィンドウキャプションの初期化
	FileName = "無名 - Maze";
	SendMsg(WM_SETTEXT, 0, (LPARAM)FileName.ToChar());

	//メニューハンドルの取得
	g_hPopup = GetSubMenu(GetMenu(m_hWnd), 1);

	//迷路が作られるまではメニュー「保存」と「表示」を不能にする
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 0), IDM_SAVE, MF_GRAYED | MF_BYCOMMAND);
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), IDM_TWOD, MF_GRAYED | MF_BYCOMMAND);
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), IDM_THREED, MF_GRAYED | MF_BYCOMMAND);

	//さまよう人のゴールを設定
	man.SetGoal(MSIZE - 2, MSIZE - 2);

	return TRUE;
}

bool CMyWnd::OnTimer(WPARAM wParam, LPARAM lParam) {

	if(Mazedata) {
		if(ThreeD)
			Display3D();
		else
			Display2D();
	}
	else {
		KillTimer(m_hWnd, ID_TIMER);
		Show = FALSE;
		MessageBox(m_hWnd, "まだ迷路が作成されていません", "Error", MB_OK | MB_ICONERROR);
	}
	return TRUE;
}

bool CMyWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam)  {

	//タイマーの有効化処理
	if(!Show) {
		if(!SetTimer(m_hWnd, ID_TIMER, 50, NULL)) {
				MessageBox(m_hWnd, "タイマー設定に失敗しました", "Error", MB_OK | MB_ICONERROR);
				return FALSE;
		}
	}
	//タイマーの無効化処理
	else {
		KillTimer(m_hWnd, ID_TIMER);
	}
	Show = !Show;

	return TRUE;
}

bool CMyWnd::OnRButtonDown(WPARAM wParam, LPARAM lParam) {

	//ウインドウの位置情報を取得
	RECT rec;
	GetWindowRect(m_hWnd, &rec);
	//ポップアップメニューの表示場所を設定
	POINT pt;
	GetCursorPos(&pt);
	//ポップアップメニューの表示
	TrackPopupMenu(g_hPopup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, m_hWnd, &rec);
	return TRUE;
}

bool CMyWnd::OnSize(WPARAM wParam, LPARAM lParam) {

	SBar.AutoSize();
	return TRUE;
}

bool CMyWnd::OnPaint(WPARAM wParam, LPARAM lParam) {

	PAINTSTRUCT paint;
	cvs.OnPaint(BeginPaint(m_hWnd, &paint));
	EndPaint(m_hWnd, &paint);
	return TRUE;
}

bool CMyWnd::OnClose(WPARAM wParam, LPARAM lParam) {

	if(MessageBox(m_hWnd, "終了しますか", "終了確認",
					MB_YESNO | MB_ICONINFORMATION) == IDYES) {
		//処理をするとDestroyWindow、PostQuitMessageが呼ばれる
		if(Show) KillTimer(m_hWnd, ID_TIMER);	//タイマー終了処理
		return TRUE;
	}
	else
		//そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
		return FALSE;
}

/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//メニュー項目、コントロール関数
/////////////////////////////////
bool CMyWnd::OnNew() {

	if(Mazedata)
		if(MessageBox(m_hWnd, "現在の迷路は破壊されますがよろしいでしょうか？",
			"警告", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
			return FALSE;
	Mazedata = mz.CreateMaze();	//迷路データのポインター
	CSTR str = MSIZE;
	str = str + " x ";
	str = str + MSIZE;
	SBar.SetText(1, str.ToChar());
	//メニュー「保存」と「表示」を可能にする
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 0), IDM_SAVE, MF_ENABLED | MF_BYCOMMAND);
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), IDM_TWOD, MF_ENABLED | MF_BYCOMMAND);
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), IDM_THREED, MF_ENABLED | MF_BYCOMMAND);
	//平面表示
	OnTwod();
	//探索開始指示表示
	MessageBox(m_hWnd, "迷路探索を開始するにはウインドウ上でマウスを左クリックしてください", "迷路探索開始", MB_OK |MB_ICONINFORMATION);
	return TRUE;
}

bool CMyWnd::OnOpen() {

	//ファイル名取得
	char* fn = cmndlg.GetFileName(m_hWnd, "迷路データファイル(*.maz)\0*.maz\0\0", TRUE, ".maz", ".", "迷路データを開く");
	if(!fn) {
		MessageBox(m_hWnd, "読み込みはキャンセルされました", "エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	FileName = fn;
	//データ読み込み
	int w, h, *data;
	CSTR str1, str2;
	str1.FromFile(FileName.ToChar());
	//迷路データの1行目は幅, 高さ
	str1.Next(str2);
	w = atoi(str2.ToChar());	//幅読み込み
	str1.Next(str2);
	h = atoi(str2.ToChar());	//高さ読み込み
	if(w != MSIZE || h != MSIZE) {
		MessageBox(m_hWnd, "迷路サイズが異なるので読み込めません", "エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	//2行目以降はカンマ区切りの迷路データ（通路0と壁1）
	data = mz.GetMaze();
	for(int y = 0; y < h; y++) {
		for(int x = 0; x < w; x++) {
			str1.Next(str2);
			data[x + w * y] = atoi(str2.ToChar());
		}
	}
	mz.Set(w, h, data);			//読み込んだデータをセット
	Mazedata = mz.GetMaze();	//迷路データのポインター
	//ウィンドウキャプション変更
	str1 = FileName;
	str1 = str1 + " - Maze";
	SendMsg(WM_SETTEXT, 0, (LPARAM)str1.ToChar());
	//ステータスバー変更
	str1 = w;
	str1 = str1 + " x ";
	str1 = str1 + h;
	SBar.SetText(1, str1.ToChar());
	//メニュー「保存」と「表示」を可能にする
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 0), IDM_SAVE, MF_ENABLED | MF_BYCOMMAND);
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), IDM_TWOD, MF_ENABLED | MF_BYCOMMAND);
	EnableMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), IDM_THREED, MF_ENABLED | MF_BYCOMMAND);
	//平面表示
	OnTwod();
	//探索開始指示表示
	MessageBox(m_hWnd, "迷路探索を開始するにはウインドウ上でマウスを左クリックしてください", "迷路探索開始", MB_OK |MB_ICONINFORMATION);
	return TRUE;
}

bool CMyWnd::OnSave() {

    char* fn = cmndlg.GetFileName(m_hWnd, "迷路データファイル(*.maz)\0*.maz\0\0", FALSE, ".maz", ".", "迷路データの保存");
	if(!fn) {
		MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	FileName = fn;
	//迷路データの保存
	CSTR str;
	//1行目　幅, 高さ
	int Width = mz.GetW();
	int Height = mz.GetH();
    str = Width;
	str = str + ",";
	str = str + Height;
	str = str + "\n";
	//2行目以降はカンマ区切りの迷路データ（通路0と壁1）
	for(int y = 0; y < Height; y++) {		// 高さ
		for(int x = 0; x < Width; x++) {	// 幅
			str = str + Mazedata[x + Width * y];
			if(x == Width - 1)
				str = str + "\n";
			else
				str = str + ",";
		}
	}
	str.ToFile(FileName.ToChar());
	//ウィンドウキャプションの初期化
	str = FileName;
	str = str + " - Maze";
	SendMsg(WM_SETTEXT, 0, (LPARAM)str.ToChar());
	return TRUE;
}

bool CMyWnd::OnExit() {

	SendMsg(WM_CLOSE, 0, 0);
	return TRUE;
}

bool CMyWnd::OnTwod() {

	//3D表示をオフにする
	ThreeD = FALSE;
	//迷路の平面表示処理
	int Width = mz.GetW();
	int Height = mz.GetH();
	cvs.Box(0, 0, Width * UNIT, Height * UNIT, 1);	//最後の1は塗り潰しブラシ
	for(int y = 0; y < Height; y++) {		// 高さ
		for(int x = 0; x < Width; x++) {	// 幅
			if(Mazedata[x + Width * y])
				cvs.PutBMP(Brick.Handle(), x * UNIT, y * UNIT, 0);	// 0 = PSET
		}
	}
	cvs.PutBMP(Ball.Handle(), man.GetX() * UNIT, man.GetY() * UNIT, 0);	// 0 = PSET
	cvs.PutBMP(Exit.Handle(), man.GetGoalX() * UNIT, man.GetGoalY() * UNIT, 0);	// 0 = PSET
	return TRUE;
}

bool CMyWnd::OnThreed() {

	//3D表示をオンにする
	ThreeD = TRUE;
	return TRUE;
}

bool CMyWnd::OnVersion() {

	MessageBox(m_hWnd, "Maze Version 1.0\nCpoyright by YSAAM", "バージョン情報", MB_OK | MB_ICONEXCLAMATION);
	return TRUE;
}

///////////////////////////////
//ユーザ関数定義
///////////////////////////////
void CMyWnd::Display2D() {	//Wandererの平面表示

	//Wanderer（赤丸）を背景（■）で消す
	cvs.PutBMP(Back.Handle(), man.GetX() * UNIT, man.GetY() * UNIT, 0);	// 0 = PSET
	//Wandererの次の行動を起こさせる
	man.GoOn(mz.GetW(), mz.GetH(), mz.GetMaze());
	//Wanderer（赤丸）の表示
	cvs.PutBMP(Ball.Handle(), man.GetX() * UNIT, man.GetY() * UNIT, 0);	// 0 = PSET
}

void CMyWnd::WhenGoal() {
	//ゴール到達時の処理
	if(man.IsGoal()) {
		KillTimer(m_hWnd, ID_TIMER);
		Show = FALSE;
		MessageBox(m_hWnd, "You Made it!!!", "Congratulations", MB_OK | MB_ICONEXCLAMATION);
		int x = man.GetGoalX();
		int y = man.GetGoalY();
		x = (mz.GetW() - 1) - x;
		y = (mz.GetH() - 1) - y;
		man.SetGoal(x, y);
		cvs.PutBMP(Exit.Handle(), x * UNIT, y * UNIT, 0);	// 平面表示用
		cvs.Color(2);										// 立体表示用（赤色）
	}
}

void CMyWnd::Display3D() {	//Wandererの立体表示

	//表示画面サイズ
	RECT rec = {0, 0, mz.GetW() * UNIT,  mz.GetH() * UNIT};
	//迷路立体表示関数を再帰的に利用する
	DisplayMaze(man.GetX(), man.GetY(), man.GetDir(), rec, 6);
	//Wandererの次の行動を起こさせる
	man.GoOn(mz.GetW(), mz.GetH(), mz.GetMaze());
	//黒色に戻す
	cvs.Color(0);
}

bool CMyWnd::DisplayMaze(int x, int y, int dir, RECT pos, int dev) {

	//外周壁に着いたら戻る
	int Width = mz.GetW();
	int Height = mz.GetH();
	if(x == 0 || x == Width - 1)	return FALSE;
	if(y == 0 || y == Height - 1)	return FALSE;
	//進行方向先の光景を縮小率（1/3）で小さくするx、yの差（減少）分
	int diff = (pos.right - pos.left) / dev / 2;	//x, y共に同じ
	//一つ先のセルのサイズ（x、y共に差分ｘ2づつ減少する）
	RECT next;
	next.left = pos.left + diff;
	next.right = pos.right - diff;
	next.top = pos.top + diff;
	next.bottom = pos.bottom - diff;
	//画面クリアー
	cvs.Color(0);	//黒色
	cvs.Clear();
	//白色で描画
	cvs.Color(15);
	bool FWall, LWall, RWall;	//正面、左、右が壁か否かのフラグ
	switch(dir) {				// x, yは1からWidth-2, Height-2に留まる
		case 0:	//Up−上へ進む
				if(Mazedata[x + Width * (y - 1)])
					FWall = TRUE;
				else
					FWall = FALSE;
				if(Mazedata[x - 1 + Width * y])
					LWall = TRUE;
				else
					LWall = FALSE;
				if(Mazedata[x + 1 + Width * y])
					RWall = TRUE;
				else
					RWall = FALSE;
				y--;
				break;
		case 1:	//Right−右へ進む
				if(Mazedata[x + 1 + Width * y])
					FWall = TRUE;
				else
					FWall = FALSE;
				if(Mazedata[x + Width * (y - 1)])
					LWall = TRUE;
				else
					LWall = FALSE;
				if(Mazedata[x + Width * (y + 1)])
					RWall = TRUE;
				else
					RWall = FALSE;
				x++;
				break;
		case 2:	//Down−下へ進む
				if(Mazedata[x + Width * (y + 1)])
					FWall = TRUE;
				else
					FWall = FALSE;
				if(Mazedata[x + 1 + Width * y])
					LWall = TRUE;
				else
					LWall = FALSE;
				if(Mazedata[x - 1 + Width * y])
					RWall = TRUE;
				else
					RWall = FALSE;
				y++;
				break;
		case 3:	//Left−左へ進む
				if(Mazedata[x - 1 + Width * y])
					FWall = TRUE;
				else
					FWall = FALSE;
				if(Mazedata[x + Width * (y + 1)])
					LWall = TRUE;
				else
					LWall = FALSE;
				if(Mazedata[x + Width * (y - 1)])
					RWall = TRUE;
				else
					RWall = FALSE;
				x--;
				break;
	}
	dev = 3;	//自分がいるところは半分の1/6だが、その先は1/3づつ減少
	if(!FWall)
		DisplayMaze(x, y, dir, next, dev);	//先に奥を描画させるため
	//ゴール到達判定（到達すると次の描画が赤色いなる）
	WhenGoal();
	//壁、通路の描画
	if(FWall)	//正面が壁なら四角を描く
		cvs.Box(next.left, next.top, next.right, next.bottom);
	if(LWall) {	//左側の描画処理
		cvs.Line(pos.left, pos.top, next.left, next.top);
		cvs.Line(pos.left, pos.bottom, next.left, next.bottom);
	}
	else {
		cvs.Line(pos.left, next.top, next.left, next.top);
		cvs.Line(pos.left, pos.top, pos.left, pos.bottom);
		cvs.Line(pos.left, next.bottom, next.left, next.bottom);
		cvs.Line(next.left, next.top, next.left, next.bottom);
	}
	if(RWall) {	//右側の描画処理
		cvs.Line(pos.right, pos.top, next.right, next.top);
		cvs.Line(pos.right, pos.bottom, next.right, next.bottom);
	}
	else {
		cvs.Line(pos.right, next.top, next.right, next.top);
		cvs.Line(pos.right, pos.top, pos.right, pos.bottom);
		cvs.Line(pos.right, next.bottom, next.right, next.bottom);
		cvs.Line(next.right, next.top, next.right, next.bottom);
	}
	Sleep(100);	//描画状態に応じて調整する
	return TRUE;
}
