// MfcKatsunariDoc.cpp : CMfcKatsunariDoc クラスの動作の定義を行います。
//

#include "stdafx.h"

#include "MfcKatsunari.h"
#include "MfcKatsunariDoc.h"

#include "SetupDlg.h"

#include "process.h"
#include "mmsystem.h"


static int TimeForKatsunari[3];
static HWND hWnd;

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMfcKatsunariDoc

IMPLEMENT_DYNCREATE(CMfcKatsunariDoc, CDocument)

BEGIN_MESSAGE_MAP(CMfcKatsunariDoc, CDocument)
	//{{AFX_MSG_MAP(CMfcKatsunariDoc)
	ON_COMMAND(ID_NEWGAME, OnNewgame)
	ON_UPDATE_COMMAND_UI(ID_PASS, OnUpdatePass)
	ON_COMMAND(ID_PASS, OnPass)
	ON_UPDATE_COMMAND_UI(ID_UNDO, OnUpdateUndo)
	ON_COMMAND(ID_UNDO, OnUndo)
	ON_COMMAND(ID_EVALUATE, OnEvaluate)
	ON_UPDATE_COMMAND_UI(ID_REDO, OnUpdateRedo)
	ON_COMMAND(ID_REDO, OnRedo)
	ON_UPDATE_COMMAND_UI(ID_FILE_OPEN, OnUpdateFileOpen)
	ON_UPDATE_COMMAND_UI(ID_GO_KATSUNARI, OnUpdateGoKatsunari)
	ON_UPDATE_COMMAND_UI(ID_NEWGAME, OnUpdateNewgame)
	ON_COMMAND(ID_LASTVALUE, OnLastvalue)
	ON_UPDATE_COMMAND_UI(ID_LASTVALUE, OnUpdateLastvalue)
	ON_UPDATE_COMMAND_UI(ID_EVALUATE, OnUpdateEvaluate)
	ON_COMMAND(IDM_SET_COLOR, OnSetColor)
	ON_UPDATE_COMMAND_UI(IDM_SET_COLOR, OnUpdateSetColor)
	ON_UPDATE_COMMAND_UI(ID_CONTINUE, OnUpdateContinue)
	ON_COMMAND(ID_CONTINUE, OnContinue)
	ON_UPDATE_COMMAND_UI(ID_STOP, OnUpdateStop)
	ON_COMMAND(ID_STOP, OnStop)
	ON_COMMAND(ID_MATTA, OnMatta)
	ON_UPDATE_COMMAND_UI(ID_MATTA, OnUpdateMatta)
	ON_COMMAND(ID_GO_KATSUNARI, OnGoKatsunari)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMfcKatsunariDoc クラスの構築/消滅

CMfcKatsunariDoc::CMfcKatsunariDoc()
{
	// TODO: この位置に１回だけ構築用のコードを追加してください。
	Initialized=FALSE;
	DispLastValue=0;
	Message[0]='\0';
	isCommPortOpen=FALSE;
	R=G=B=0;
	playout=500;
	komi=7;
}

CMfcKatsunariDoc::~CMfcKatsunariDoc()
{
}

BOOL CMfcKatsunariDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;

	FILE *fp;
	char line[3];
	boardSize = BOARD_SIZE;
	pureBoardSize = PURE_BOARD_SIZE;
	if( (fopen_s(&fp, "mctsgo.ini", "r")) == 0 ){
		if( fgets( line, 3, fp ) != NULL){
			if(strcmp(line, "9") == 0 || strcmp(line, "9\n") == 0){
				boardSize = 11;
				pureBoardSize = 9;
			}else if(strcmp(line, "13") == 0 || strcmp(line, "13\n") == 0){
				boardSize = 15;
				pureBoardSize = 13;
			}
		}
	}
	fclose(fp);

	int x,y;
	for(y=0;y<boardSize/*BOARD_SIZE*/;y++) {
		for(x=0;x<boardSize/*BOARD_SIZE*/;x++) {
			if (x==0||y==0||x==boardSize/*BOARD_SIZE*/-1||y==boardSize/*BOARD_SIZE*/-1) {
				Ban[y][x]=OB;
			} else {
				Ban[y][x]=SPACE;
			}
		}
	}
	for(y=0;y<boardSize/*BOARD_SIZE*/;y++) {
		for(x=0;x<boardSize/*BOARD_SIZE*/;x++) {
			Value[y][x]=-1000;
		}
	}
	Teban=NO_PLAY;
	color=BLACK;
	move=1;
	hama[BLACK]=hama[WHITE]=0;
	BWSide[BLACK]=BWSide[WHITE]=-1;
	Time[BLACK]=Time[WHITE]=0;
	TimeForKatsunari[BLACK]=TimeForKatsunari[WHITE]=0;
	LastMove[BLACK]=LastMove[WHITE]=0;
	LastMoveX[BLACK]=LastMoveX[WHITE]=0;
	LastMoveY[BLACK]=LastMoveY[WHITE]=0;
	hThread=NULL;
	LastX=LastY=20;

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CMfcKatsunariDoc シリアライゼーション

void CMfcKatsunariDoc::Serialize(CArchive& ar)
{
	int i;
	CString FileName=ar.GetFile()->GetFilePath();
	if (ar.IsStoring())
	{
		saveScore(FileName);
	}
	else
	{
		loadScore(FileName);
		move=1;
		UndoDepth=scoreCount;

		ClearGameBoard(boardSize);
		SetHandicapStoneGameBoard(getHandicap());
		color=getHandicap()<=1 ? BLACK : WHITE;

		hama[BLACK]=hama[WHITE]=0;
		PlayLog[0].hama[BLACK]=hama[BLACK];
		PlayLog[0].hama[WHITE]=hama[WHITE];

		PlayLog[0].x=PlayLog[0].y=0;

		for(i=1;i<scoreCount;i++) {
			int c;
			getScore(i,PlayLog[i].x,PlayLog[i].y,c);
			hama[c] += SetStoneOnGameBoard(PlayLog[i].x, PlayLog[i].y, c, i);
			PlayLog[i].hama[BLACK]=hama[BLACK];
			PlayLog[i].hama[WHITE]=hama[WHITE];
		}
		//最後はパス
		PlayLog[i].x=PlayLog[i].y=0;
		PlayLog[i].hama[BLACK]=hama[BLACK];
		PlayLog[i].hama[WHITE]=hama[WHITE];

		BWSide[BLACK]=getBlackSide();
		BWSide[WHITE]=getWhiteSide();

		Teban=REPLAY;
		UpdateAllViews(NULL);
	}
}

/////////////////////////////////////////////////////////////////////////////
// CMfcKatsunariDoc クラスの診断

#ifdef _DEBUG
void CMfcKatsunariDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CMfcKatsunariDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMfcKatsunariDoc コマンド
//void saveLogData(char *fn )
//{
/*
int i, x, y;
  FILE *fp;

  if((fp = fopen(fn, "w+")) == NULL) fp = stdout;

  for( i=1; i<500; i++ ){
    fprintf( fp, "[%3d] side=%d (%2d,%2d) time=(%3d,%3d)(%4d,%4d) hama=(%2d,%2d) pcount=%d\n",
	    i, logData[i].side, logData[i].x, logData[i].y,
	    ( logData[i-1].BWtime[0] - logData[i].BWtime[0] ),
	    ( logData[i-1].BWtime[1] - logData[i].BWtime[1] ),
	    logData[i].BWtime[0], logData[i].BWtime[1],
	    logData[i].BWhama[0], logData[i].BWhama[1],
	    logData[i].pcount );
  }
  fclose( fp );
*/
//}

void CMfcKatsunariDoc::GameOver(int win)
{
	CUpdateKatsunari UpdateData;
	UpdateData.OldX=0;
	UpdateData.OldY=0;
	memcpy(UpdateData.OldBan,Ban,sizeof(Ban));

	int keisei = copyStatus(Ban, pureBoardSize);
	// 形勢表示
	memcpy(UpdateData.NewBan,Ban,sizeof(Ban));
	UpdateAllViews(NULL,DispLastValue,&UpdateData);
	
//	saveLogData( "debugLog" );  /* 98.1.7 */
	if(isCommPortOpen){
		isCommPortOpen=FALSE;
		closecom();
	}
	keisei = keisei-komi/*+hama[BLACK]-hama[WHITE]*/;

	char buf[80];
	if(win == 1)
		sprintf_s(buf,sizeof(buf),"黒の勝ち。");
	else if(win == 2)
		sprintf_s(buf,sizeof(buf),"白の勝ち。");
	else if(keisei > 0)
		sprintf_s(buf,sizeof(buf),"盤面 黒 %d 目半勝ち。",keisei-1);
	else if(keisei <= 0)
		sprintf_s(buf, sizeof(buf), "盤面 白 %d 目半勝ち。", (-keisei));
	else
		sprintf_s(buf, sizeof(buf), "盤面 牛角。");

	((CFrameWnd *)(AfxGetApp()->m_pMainWnd))->GetActiveView()->MessageBox(buf);
	Teban=REPLAY;
//	OnFileSaveAs();
}

void Run(void *p)
{
	int x,y,e;
	int color,move,kind,ko_x,ko_y,ko_move;
	int po,km;
	color=((CMfcKatsunariDoc *)p)->color;
	move=((CMfcKatsunariDoc *)p)->move;
	ko_x=((CMfcKatsunariDoc *)p)->ko_x;
	ko_y=((CMfcKatsunariDoc *)p)->ko_y;
	ko_move=((CMfcKatsunariDoc *)p)->ko_num;
	if (color==BLACK) {
		kind=((CMfcKatsunariDoc *)p)->KindBlack;
	} else {
		kind=((CMfcKatsunariDoc *)p)->KindWhite;
	}
	if (((CMfcKatsunariDoc *)p)->EndFlag) {
		return;
	}
	po=((CMfcKatsunariDoc *)p)->playout;
	km=((CMfcKatsunariDoc *)p)->komi;

	char *msg=selectNextMove( &x, &y, &e, color, move/*, kind*/, ko_x, ko_y, ko_move , ((CMfcKatsunariDoc *)p)->pureBoardSize, po, km);		/* select.c */
	copyEvaluation(((CMfcKatsunariDoc *)p)->Value, ((CMfcKatsunariDoc *)p)->boardSize);
	::PostMessage(hWnd,WM_NEXT_MOVE,x,y);
	_endthread();		// Profileをとるときコメントアウト
}

void Comm(void *p)
{
	int x,y;
	CMfcKatsunariDoc *pDoc=(CMfcKatsunariDoc *)p;

	while(!receivexy(&x,&y) && !pDoc->EndFlag) {
		Sleep(200);
	}
	if (!pDoc->EndFlag) {
		::PostMessage(hWnd,WM_NEXT_MOVE,x,y);
	}
	_endthread();
}

void CMfcKatsunariDoc::GoKatsunari()
{
	hWnd=((CFrameWnd *)(AfxGetApp()->m_pMainWnd))->GetActiveView()->m_hWnd;
	hThread=(HANDLE)_beginthread(Run,0,(void *)this);	// Profileをとるときコメントアウト
//	Run((void *)this);	// Profileをとるときのため
}

void CMfcKatsunariDoc::GoComm()
{
	hWnd=((CFrameWnd *)(AfxGetApp()->m_pMainWnd))->GetActiveView()->m_hWnd;
	hThread=(HANDLE)_beginthread(Comm,0,(void *)this);
}

void CMfcKatsunariDoc::NextMove(int x,int y)
{
	int win = 0;
	if (EndFlag) {
		return;
	}
	if (Teban==REPLAY) {
		return;
	}
	hWnd=((CFrameWnd *)(AfxGetApp()->m_pMainWnd))->GetActiveView()->m_hWnd;
	// 手を打ったらリドゥは無効
	UndoDepth=0;

	hThread=NULL;
	EndFlag=0;
	// 記録をとる。
	if (x==0) {
		win = y;
		y = 0;
		recordScore(0,0,color,move);
	} else {
		if (checkSetStoneGameBoard(x,y,color,move)!=TRUE) {
			char buf[256];
			sprintf_s(buf, sizeof(buf), "相手の手が変です。(%d,%d).",x,y);
			MessageBox(AfxGetMainWnd()->m_hWnd,buf,"通信エラー",MB_OK);
		}
		recordScore(x,y,color,move);
	}
	PlayLog[move].x=x;
	PlayLog[move].y=y;

	// ファイルにログを出しておく for DEBUG
	saveScore("PlayLog.sgf");

	// 相手に送りつける
	if ((color==WHITE && BWSide[BLACK]==COMM) || (color==BLACK && BWSide[WHITE]==COMM) ) {
		if (!sendxy(x,y)) {
			MessageBox(AfxGetMainWnd()->m_hWnd,"SendXYに失敗しました。","通信エラー",MB_OK);
		}
	}
	// 計時
	unsigned int Now=(unsigned int)GetTickCount64();
	Time[color]+=Now-LastTime;
	TimeForKatsunari[color]=Time[color];
	LastTime=Now;

	// 最後の手の書き換え
	LastMove[color]=move;
	LastMoveX[color]=x;
	LastMoveY[color]=y;
	// 盤面を書き換える。
	CUpdateKatsunari UpdateData;
	UpdateData.OldX=LastX;
	UpdateData.OldY=LastY;
	memcpy(UpdateData.OldBan,Ban,sizeof(Ban));
	int PreX=LastX;
	LastX=x;
	LastY=y;
if(x!=0&&y!=0){
	hama[color] +=SetStoneOnGameBoard(x,y,color,move);

	PlayLog[move].hama[BLACK]=hama[BLACK];
	PlayLog[move].hama[WHITE]=hama[WHITE];
	memcpy(UpdateData.NewBan,Ban,sizeof(Ban));
}
	UpdateAllViews(NULL,DispLastValue,&UpdateData);
	// x==0 は パス。
	if (win != 0 || x==0 && PreX==0 || move>500) {	// パス２連続 or 500手オーバー
		GameOver(win);
	} else {
		int preTeban=BWSide[color];
		// 次の手。
		if (color==WHITE) {
			color=BLACK;
		} else {
			color=WHITE;
		}

		move++;
		Teban=BWSide[color];
		if (Teban==HUMAN && LastX==0 && preTeban==COMPUTER) {
			MessageBox(AfxGetMainWnd()->m_hWnd,"パスしました","コンピュータ"/*"名無し"*/,MB_OK);
		}
		if (LastX!=0) {
			sndPlaySound((const char *)lpSound,SND_ASYNC|SND_MEMORY); 
		}
		Play();
	}
}


void CMfcKatsunariDoc::Play()
{
	switch(Teban) {
		case HUMAN:
			// 単純に入力を待てば良い
			break;
		case COMM:
			// スレッドを起こして、通信を待つ
			GoComm();
			break;
		case COMPUTER:
			// スレッドを起こして、名無しに考えさせる。
			GoKatsunari();
			break;
	}
}


void LoadInitialFileThread(void *p)
{
	CMfcKatsunariDoc *pDoc=(CMfcKatsunariDoc *)p;

	::PostMessage(hWnd,WM_INITIAL_LOADED,8,0);
	initAll();
	::PostMessage(hWnd,WM_INITIAL_LOADED,0,0);
	pDoc->Initialized=TRUE;
	_endthread();
}

void CMfcKatsunariDoc::LoadInitialFiles()
{
	hWnd=((CFrameWnd *)(AfxGetApp()->m_pMainWnd))->GetActiveView()->m_hWnd;
	_beginthread(LoadInitialFileThread,0,(void *)this);
}

void CMfcKatsunariDoc::OnNewgame() 
{
	// CommPortが開いていたら一旦閉じる
	if(isCommPortOpen){
		isCommPortOpen=FALSE;
		closecom();
	}
	if (hThread!=NULL) {
		EndFlag=1;
		AfxGetMainWnd()->EnableWindow(FALSE);
		while(WaitForSingleObject(hThread,100)==WAIT_TIMEOUT) {
			AfxGetThread()->PumpMessage();
		}
		AfxGetMainWnd()->EnableWindow(TRUE);
	}
	CSetupDlg SetupDlg;
	if (SetupDlg.DoModal()==IDCANCEL) {
		return;
	}

	playout = SetupDlg.m_playout;
	komi = SetupDlg.m_komi;

	port=1;
	if (SetupDlg.m_TebanBlack==COMM+1) {
		port=2;
		SetupDlg.m_TebanBlack=COMM;
	}
	if (SetupDlg.m_TebanWhite==COMM+1) {
		port=2;
		SetupDlg.m_TebanWhite=COMM;
	}
    if(SetupDlg.m_TebanWhite==COMM && SetupDlg.m_TebanBlack==COMM) {
		MessageBox(AfxGetMainWnd()->m_hWnd,"両方通信にすることはできません。","警告",MB_OK);
		return;
	}

    if( (SetupDlg.m_TebanWhite==COMM || SetupDlg.m_TebanBlack==COMM ) && SetupDlg.m_Handycap>0) {
		MessageBox(AfxGetMainWnd()->m_hWnd,"通信対戦では、手合いは互い先のみとなります。","警告",MB_OK);
		return;
	}

	if(SetupDlg.m_TebanBlack == COMM || SetupDlg.m_TebanWhite == COMM){
		if (!initcom(port)) {
			MessageBox(AfxGetMainWnd()->m_hWnd,"InitComに失敗しました。","通信エラー",MB_OK);
			return;
		}
		isCommPortOpen=TRUE;
	}
	BWSide[BLACK]=SetupDlg.m_TebanBlack;
	BWSide[WHITE]=SetupDlg.m_TebanWhite;

	ClearGameBoard(boardSize);

	for(int y=0;y<boardSize;y++) {
		for(int x=0;x<boardSize;x++) {
			if (x==0||y==0||x==boardSize-1||y==boardSize-1) {
				Ban[y][x]=OB;
			} else {
				Ban[y][x]=SPACE;
			}
		}
	}

    SetHandicapStoneGameBoard( SetupDlg.m_Handycap  );
	ko_x=ko_y=ko_num=0;

	color=SetupDlg.m_Handycap<=1 ? BLACK : WHITE;
	Teban=BWSide[color];

	if(SetupDlg.m_TebanBlack == COMM){
		if (!newgame(WHITE)) {
			MessageBox(AfxGetMainWnd()->m_hWnd,"NewGameに失敗しました。","通信エラー",MB_OK);
			return;
		}
	} else if(SetupDlg.m_TebanWhite == COMM){
		if (!newgame(BLACK)) {
			MessageBox(AfxGetMainWnd()->m_hWnd,"NewGameに失敗しました。","通信エラー",MB_OK);
			return;
		}
	}

	EndFlag=0;
	if (SetupDlg.m_KindBlack.IsEmpty()) {
		KindBlack=0;
	} else {
		KindBlack=SetupDlg.m_KindBlack[0]-'0';		// 名無しの気分(^^;
	}
	if (SetupDlg.m_KindWhite.IsEmpty()) {
		KindWhite=0;
	} else {
		KindWhite=SetupDlg.m_KindWhite[0]-'0';		// 名無しの気分(^^;
	}
	move=1;
	UndoDepth=0;
	hama[BLACK]=hama[WHITE]=0;
	Time[BLACK]=Time[WHITE]=0;
	TimeForKatsunari[BLACK]=TimeForKatsunari[WHITE]=0;
	LastMove[BLACK]=LastMove[WHITE]=0;
	LastMoveX[BLACK]=LastMoveX[WHITE]=0;
	LastMoveY[BLACK]=LastMoveY[WHITE]=0;
	LastTime=(unsigned int)GetTickCount64();
	hThread=NULL;
	UpdateAllViews(NULL);

	Play();
}

void CMfcKatsunariDoc::OnUpdatePass(CCmdUI* pCmdUI) 
{
    pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban==HUMAN);
}

void CMfcKatsunariDoc::OnPass() 
{
	hWnd=((CFrameWnd *)(AfxGetApp()->m_pMainWnd))->GetActiveView()->m_hWnd;
	::PostMessage(hWnd,WM_NEXT_MOVE,0,0);
}

void CMfcKatsunariDoc::OnUpdateRedo(CCmdUI* pCmdUI) 
{
    pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban==REPLAY && UndoDepth>0);
}

void CMfcKatsunariDoc::OnRedo() 
{
	move+=1;
	replayFromScoreGameBoard(move-1);

	// 相手の手番になる
	if (color==WHITE) color=BLACK; else color=WHITE;

	// 最後の手の書き換え
	SetLastMoveFromReplay();

	Teban=REPLAY;
	UndoDepth-=1;
	UpdateAllViews(NULL);
}

void CMfcKatsunariDoc::OnUpdateUndo(CCmdUI* pCmdUI) 
{
    pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban==REPLAY && move>1);
}

void CMfcKatsunariDoc::OnUndo() 
{
	move-=1;
	replayFromScoreGameBoard(move-1);

	// 相手の手番になる
	if (color==WHITE) color=BLACK; else color=WHITE;

	// 最後の手の書き換え
	SetLastMoveFromReplay();

	Teban=REPLAY;
	UndoDepth+=1;
	UpdateAllViews(NULL);
}

void CMfcKatsunariDoc::OnUpdateEvaluate(CCmdUI* pCmdUI) 
{
    pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban==HUMAN || Teban==REPLAY);
}

void CMfcKatsunariDoc::OnEvaluate() 
{
	int tmpBoard[BOARD_SIZE][BOARD_SIZE];
	memcpy(tmpBoard,Ban,sizeof(Ban));
	CUpdateKatsunari UpdateData;
	UpdateData.OldX=0;
	UpdateData.OldY=0;
	memcpy(UpdateData.OldBan,Ban,sizeof(Ban));

	int keisei = copyStatus(Ban, pureBoardSize);
	// 形勢表示
	memcpy(UpdateData.NewBan,Ban,sizeof(Ban));
	UpdateAllViews(NULL,DispLastValue,&UpdateData);

	keisei = keisei-komi/*+hama[BLACK]-hama[WHITE]*/;
	char buf[80];
	if(keisei > 0){
		sprintf_s(buf, sizeof(buf),"盤面 黒 %d 目半勝ち。",keisei-1);
	} else if(keisei <= 0){
		sprintf_s(buf, sizeof(buf),"盤面 白 %d 目半勝ち。", (-keisei));
	} else {
		sprintf_s(buf, sizeof(buf),"盤面 牛角。");
	}
	MessageBox(AfxGetMainWnd()->m_hWnd,buf,"形勢表示中",MB_OK);
	memcpy(Ban,tmpBoard,sizeof(Ban));
	UpdateAllViews(NULL);
}

void CMfcKatsunariDoc::OnUpdateFileOpen(CCmdUI* pCmdUI) 
{
	pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban!=COMPUTER && Teban!=COMM && Initialized);
}

int CMfcKatsunariDoc::_getKeyValue(FILE *fp,char *key,char *val)
{
  int c;
  char *kp, *vp;

  /* key[val] */
  kp = key;
  vp = val;

  do {
    c = fgetc(fp);
    if(c == ')') return 1;
    if(c == EOF) return -1;
  } while(c < 'A' || 'Z' < c);
  /* key[ */
  *kp++ =  (char) c;
  do {
    c = fgetc(fp);
    if(c == EOF) return -2;
    *kp++ = (char) c;
  } while(c != '[');
  *(--kp) = '\0';

  /* val] */
  do {
    c = fgetc(fp);
    if(c == EOF) return -3;
    *vp++ = (char) c;
  } while(c != ']');
  *(--vp) = '\0';

  return 0;
}

int CMfcKatsunariDoc::saveScore(const char* fn )
{
  int i, x, y;
  FILE *fp;

  if ((fopen_s(&fp, fn, "w+")) != 0) fp = stdout;

  /* GM: Game種別(1は囲碁) */
  /* FF: SGFのバージョン   */
  fprintf( fp, "(;GM[1]FF[1]" );

  /* SZ: Size of Board     */
  fprintf( fp, "SZ[%d]", initialSetting.boardSize );

  /* PB: Player Black */
  /* PW: Player White */
  if( initialSetting.blackSide == HUMAN ){
    if (initialSetting.blackName == NULL) {
      fprintf( fp, "PB[human]" );
    }else if( strcmp( initialSetting.blackName, "  人間" ) == 0 ){
      fprintf( fp, "PB[human]" );
    } else {
      fprintf( fp, "PB[%s]", initialSetting.blackName );
    }
  }else{
    fprintf( fp, "PB[katsunari]" );
  }
  if( initialSetting.whiteSide == HUMAN ){
    if (initialSetting.whiteName == NULL) {
      fprintf( fp, "PW[human]" );
    }else if( strcmp( initialSetting.whiteName, "  人間" ) == 0 ){
      fprintf( fp, "PW[human]" );
    } else {
      fprintf( fp, "PW[%s]", initialSetting.whiteName );
    }
  }else{
    fprintf( fp, "PW[katsunari]" );
  }

  /* その他の項目 */
  fprintf( fp, "RE[]\n" );        /* RE: Result               */
  fprintf( fp, "DT[]" );          /* DT: Date                 */
  fprintf( fp, "PC[]" );          /* PC: Place                */
  fprintf( fp, "EV[]" );          /* EV: Event                */
  fprintf( fp, "GN[]" );          /* GN: Game Name            */
  fprintf( fp, "TM[]" );          /* TM: Time for each player */
  fprintf( fp, "RU[Japanese]" );  /* RU: Rule                 */

  if( initialSetting.handicap > 0 ){
    /* HA: Handicap */
    fprintf( fp, "\nHA[%d]", initialSetting.handicap );
    /* AB: Places of each stones in Handicap Game */
    switch( initialSetting.handicap ){
    case 2 :
      fprintf( fp, "AB[jd][dj]\n" );
      break;
    case 3 :
      fprintf( fp, "AB[jd][dj][jj]\n" );
      break;
    case 4 :
      fprintf( fp, "AB[jd][dj][jj][dd]\n" );
      break;
    case 5 :
      fprintf( fp, "AB[jd][dj][jj][dd][gg]\n" );
      break;
    case 6 :
      fprintf( fp, "AB[jd][dj][jj][dd][jg][dg]\n" );
      break;
    case 7 :
      fprintf( fp, "AB[jd][dj][jj][dd][jg][dg][gg]\n" );
      break;
    case 8 :
      fprintf( fp, "AB[jd][dj][jj][dd][jg][dg][gd][gj]\n" );
      break;
    case 9 :
      fprintf( fp, "AB[jd][dj][jj][dd][jg][dg][gd][gj][gg]\n" );
      break;
    }
  }else{
    /* KM: Komi */
    fprintf( fp, "KM[5.5]\n" );
  }

  for (i=1;i<=scoreCount;i++) {
/*
    x = (1 <= score[i].y && score[i].y <= 19)? (score[i].y + 'a' - 1): 't';
    y = (1 <= score[i].x && score[i].x <= 19)? (score[i].x + 'a' - 1): 't';
*/
    y = (1 <= score[i].y && score[i].y <= pureBoardSize/*PURE_BOARD_SIZE*/)? (score[i].y + 'a' - 1): 't';
    x = (1 <= score[i].x && score[i].x <= pureBoardSize/*PURE_BOARD_SIZE*/)? (score[i].x + 'a' - 1): 't';
    if(score[i].color == BLACK){
      fprintf(fp, ";B[%c%c]", x, y); 
    } else {
      fprintf(fp, ";W[%c%c]", x, y);
    }
    fprintf(fp, "C[%4d %c %2d %2d]\n",
	    i, score[i].color==BLACK?'b':score[i].color==WHITE?'w':'s',
	    score[i].x, score[i].y );
  }
  fprintf(fp, ")\n");
  
  fclose(fp);
  return 0;
}


/*--------------------------------------------------------*/
/* 棋譜ファイルの読み込み                                 */
/*                                                        */
/* 復帰値                                                 */
/*  0: NO ERROR                                           */
/*  1: fopen() fail                                       */
/*  2: getLine() fail                                     */
/*  3: ungetLine() fail                                   */
/*  4: sscanf() fail (1st)                                */
/*  5: scoreCount overflow                                */
/*  6: sscanf() fail (2nd)                                */
/*--------------------------------------------------------*/
int CMfcKatsunariDoc::loadScore(const char *name )
{
  FILE *fp;
  int c, ret, x, y;
  char key[256], val[256];

  if( (fopen_s(&fp,name, "r")) != 0 ){
    fprintf(stderr, "ERROR: file(%s) open fail\n", name );
    return( 1 );
  }

  initialSetting.boardSize = pureBoardSize/*PURE_BOARD_SIZE*/;
  initialSetting.handicap  = 0;

  do {
    c = fgetc(fp);
    if(c == EOF){
      fclose(fp);
      return( 2 );
    }
  } while(c != '(');

  for(;;){
    ret = _getKeyValue(fp, key, val);
    if(ret < 0){
      fclose(fp);
      return( 3 );
    } else if(ret == 1) break;

    if( (strcmp(key, "PB") == 0) || (strcmp(key, "PlayerBlack") == 0) ){
      if ((initialSetting.blackName = (char *)malloc(strlen(val)+1))!=NULL) {
	strcpy_s(initialSetting.blackName,sizeof(initialSetting.blackName),val);
      }
      if(strcmp(val, "katsunari") == 0){
	initialSetting.blackSide = COMPUTER;
      } else {
	initialSetting.blackSide = HUMAN;
      }
    }else if( (strcmp(key, "PW") == 0) || (strcmp(key, "PlayerWhite") == 0) ){
      if ((initialSetting.whiteName = (char *)malloc(strlen(val)+1))!=NULL) {
	strcpy_s(initialSetting.whiteName,sizeof(initialSetting.whiteName),val);
      }
      if(strcmp(val, "katsunari") == 0){
	initialSetting.whiteSide = COMPUTER;
      }else{
	initialSetting.whiteSide = HUMAN;
      }
    }else if( (strcmp(key, "HA") == 0) ){
      initialSetting.handicap  = atoi(val);
    }
    else if( (strcmp(key, "B") == 0)     || (strcmp(key, "W") == 0) ||
	     (strcmp(key, "Black") == 0) || (strcmp(key, "White") == 0) ){
      break;
    }
  }

  hama[BLACK] = 0;
  hama[WHITE] = 0;
  scoreCount = 0;

  while(ret != 1){
    if (scoreCount >= MOVE_COUNT_MAX) {
      return( 4 );
    }
    if( (strcmp(key, "B") == 0) || (strcmp(key, "Black") == 0) ){
      scoreCount++;
      x = ('a' <= val[0] && val[0] <= 's')? val[0] - 'a' + 1: 0;
      score[scoreCount].x = x;
      y = ('a' <= val[1] && val[1] <= 's')? val[1] - 'a' + 1: 0;
      score[scoreCount].y = y;
      score[scoreCount].color = BLACK;
    } else if( (strcmp(key, "W") == 0) || (strcmp(key, "White") == 0) ){
      scoreCount++;
      x = ('a' <= val[0] && val[0] <= 's')? val[0] - 'a' + 1: 0;
      score[scoreCount].x = x;
      y = ('a' <= val[1] && val[1] <= 's')? val[1] - 'a' + 1: 0;
      score[scoreCount].y = y;
      score[scoreCount].color = WHITE;
    }
    ret = _getKeyValue(fp, key, val);
    if(ret < 0){
      fclose(fp);
      return( 5 );
    }
  }

  fclose(fp);

  return( 0 );
}

void CMfcKatsunariDoc::recordScore(int x,int y,int colorIn,int moveIn)
{
	scoreCount = moveIn;
	score[moveIn].x = x;
	score[moveIn].y = y;
	score[moveIn].color = colorIn;
}

void CMfcKatsunariDoc::getScore(int moveIn,int &x,int &y,int &colorIn)
{
	x=score[moveIn].x;
	y=score[moveIn].y;
	colorIn=score[moveIn].color;
}

void CMfcKatsunariDoc::OnUpdateGoKatsunari(CCmdUI* pCmdUI) 
{
	pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban==HUMAN);
}

void CMfcKatsunariDoc::OnGoKatsunari() 
{
	GoKatsunari();
}

void CMfcKatsunariDoc::OnUpdateNewgame(CCmdUI* pCmdUI) 
{
	pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Initialized);
}

void CMfcKatsunariDoc::OnUpdateLastvalue(CCmdUI* pCmdUI) 
{
	pCmdUI->m_bEnableChanged = TRUE;
	if (!Initialized) {
		pCmdUI->Enable(FALSE);
	} else {
		pCmdUI->Enable(TRUE);
		pCmdUI->SetCheck(DispLastValue);
	}
}

void CMfcKatsunariDoc::OnLastvalue() 
{
	if (DispLastValue) {
		DispLastValue=0;			
	} else {
		DispLastValue=1;
	}
	UpdateAllViews(NULL);
}

void CMfcKatsunariDoc::SetTitle(LPCTSTR lpszTitle) 
{
	CDocument::SetTitle(lpszTitle);
}

int CMfcKatsunariDoc::getSpentTime(int colorIn)
{
	return TimeForKatsunari[colorIn] / 1000;
}

void CMfcKatsunariDoc::ClearGameBoard(int size) {
	ko_x=ko_y=ko_num=0;
	::clearGameBoard(size);	// 思考ルーチン側
	for(int y=0;y<size;y++) {
		for(int x=0;x<size;x++) {
			if (x==0||y==0||x==size-1||y==size-1) {
				Ban[y][x]=OB;
			} else {
				Ban[y][x]=SPACE;
			}
		}
	}
}

void CMfcKatsunariDoc::SetHandicapStoneGameBoard(int Handicap) {
	::setHandicapStoneGameBoard(Handicap);
	if(pureBoardSize == 19)
	{
		switch(Handicap) {
			case 9:
				Ban[ 4][ 4]=BLACK;
				Ban[ 4][10]=BLACK;
				Ban[ 4][16]=BLACK;
				Ban[10][ 4]=BLACK;
				Ban[10][10]=BLACK;
				Ban[10][16]=BLACK;
				Ban[16][ 4]=BLACK;
				Ban[16][10]=BLACK;
				Ban[16][16]=BLACK;
				break;
			case 8:
				Ban[ 4][ 4]=BLACK;
				Ban[ 4][10]=BLACK;
				Ban[ 4][16]=BLACK;
				Ban[10][ 4]=BLACK;
				Ban[10][16]=BLACK;
				Ban[16][ 4]=BLACK;
				Ban[16][10]=BLACK;
				Ban[16][16]=BLACK;
				break;
			case 7:
				Ban[ 4][ 4]=BLACK;
				Ban[ 4][16]=BLACK;
				Ban[10][ 4]=BLACK;
				Ban[10][10]=BLACK;
				Ban[10][16]=BLACK;
				Ban[16][ 4]=BLACK;
				Ban[16][16]=BLACK;
				break;
			case 6:
				Ban[ 4][ 4]=BLACK;
				Ban[ 4][16]=BLACK;
				Ban[10][ 4]=BLACK;
				Ban[10][16]=BLACK;
				Ban[16][ 4]=BLACK;
				Ban[16][16]=BLACK;
				break;
			case 5:
				Ban[ 4][ 4]=BLACK;
				Ban[ 4][16]=BLACK;
				Ban[10][10]=BLACK;
				Ban[16][ 4]=BLACK;
				Ban[16][16]=BLACK;
				break;
			case 4:
				Ban[ 4][ 4]=BLACK;
				Ban[ 4][16]=BLACK;
				Ban[16][ 4]=BLACK;
				Ban[16][16]=BLACK;
				break;
				case 3:
				Ban[ 4][16]=BLACK;
				Ban[16][ 4]=BLACK;
				Ban[16][16]=BLACK;
				break;
			case 2:
				Ban[ 4][16]=BLACK;
				Ban[16][ 4]=BLACK;
				break;
			default:
				break;
		}
	}
}

void CMfcKatsunariDoc::replayFromScoreGameBoard(int moveIn)
{
	ClearGameBoard(boardSize);
	SetHandicapStoneGameBoard( initialSetting.handicap );

	for(int i=1;i<=moveIn;i++) {
		int x,y,colorWk;
		getScore( i, x, y, colorWk );
		if (x!=0 && y!=0) {
			// パスじゃないとき。
			hama[colorWk==BLACK?WHITE:BLACK]+=SetStoneOnGameBoard(x,y,colorWk,i);
		}
	}
}

int CMfcKatsunariDoc::DoRemoveRecursive(int x,int y,int colorIn,int board[BOARD_SIZE][BOARD_SIZE])
{
	if (check[y][x]) return 0;
	check[y][x]=1;
	if (y<1 || x<1 || x>pureBoardSize/*PURE_BOARD_SIZE*/||y>pureBoardSize/*PURE_BOARD_SIZE*/) return 0;
	int ret=0;
	if( board[y][x] == colorIn ){
		if (x>1) {
			ret+=DoRemoveRecursive(x-1, y, colorIn,board);
		}
		if (y>1) {
			ret+=DoRemoveRecursive(x, y-1, colorIn,board);
		}
		if (x<pureBoardSize/*PURE_BOARD_SIZE*/) {
			ret+=DoRemoveRecursive(x+1, y, colorIn,board);
		}
		if (y<pureBoardSize/*PURE_BOARD_SIZE*/) {
			ret+=DoRemoveRecursive(x, y+1, colorIn,board);
		}
		board[y][x]=SPACE;
		ret++;
	}
	return ret;
}

int CMfcKatsunariDoc::DoRemove(int x,int y,int colorIn,int board[BOARD_SIZE][BOARD_SIZE])
{
	memset(check,0,sizeof(check));
	return DoRemoveRecursive(x,y,colorIn,board);
}

int CMfcKatsunariDoc::CheckRemoveRecursive(int x,int y,int colorIn,int board[BOARD_SIZE][BOARD_SIZE])
{
	if (check[y][x]) return TRUE;
	check[y][x]=1;
	if (y<1 || x<1 || x>pureBoardSize/*PURE_BOARD_SIZE*/||y>pureBoardSize/*PURE_BOARD_SIZE*/) return TRUE;
	if (board[y][x]==SPACE) return FALSE;
	if( board[y][x] == colorIn ){
		if (x>1) {
			if (!CheckRemoveRecursive(x-1, y, colorIn,board )) {
				return FALSE;
			}
		}
		if (y>1) {
			if (!CheckRemoveRecursive(x, y-1, colorIn,board )) {
				return FALSE;
			}
		}
		if (x<pureBoardSize/*PURE_BOARD_SIZE*/) {
			if (!CheckRemoveRecursive(x+1, y, colorIn,board )) {
				return FALSE;
			}
		}
		if (y<pureBoardSize/*PURE_BOARD_SIZE*/) {
			if (!CheckRemoveRecursive(x, y+1, colorIn,board )) {
				return FALSE;
			}
		}
	}
	return TRUE;
}

int CMfcKatsunariDoc::CheckRemove(int x,int y,int colorIn,int board[BOARD_SIZE][BOARD_SIZE])
{
	memset(check,0,sizeof(check));
	return CheckRemoveRecursive(x,y,colorIn,board);
}

int CMfcKatsunariDoc::checkSuicide(int x,int y,int colorIn,int board[BOARD_SIZE][BOARD_SIZE])
{
	board[y][x]=colorIn;
	if (CheckRemove(x,y,colorIn,board)) {
		//自殺手かも。
		int opp=(colorIn==WHITE)?BLACK:WHITE;
		if ( (x>1 && board[y][x-1]==opp && CheckRemove(x-1,y,opp,board)) ) return FALSE;
		if ( (y>1 && board[y-1][x]==opp && CheckRemove(x,y-1,opp,board)) ) return FALSE;
		if ( (x<pureBoardSize/*PURE_BOARD_SIZE*/ && board[y][x+1]==opp && CheckRemove(x+1,y,opp,board))) return FALSE;
		if ( (y<pureBoardSize/*PURE_BOARD_SIZE*/ && board[y+1][x]==opp && CheckRemove(x,y+1,opp,board))) return FALSE;
		return TRUE;
	}
	return FALSE;
}

int CMfcKatsunariDoc::checkSetStoneGameBoard(int x,int y, int colorIn, int moveIn)
{
	int board[BOARD_SIZE][BOARD_SIZE];  /* 碁盤 */

	for(int yy=0;yy<boardSize/*BOARD_SIZE*/;yy++) {
		for(int xx=0;xx<boardSize/*BOARD_SIZE*/;xx++) {
			board[yy][xx]=Ban[yy][xx];
		}
	}

	if( x > pureBoardSize/*PURE_BOARD_SIZE*/ || x < 0 || y > pureBoardSize/*PURE_BOARD_SIZE*/ || y < 0 ){
		return( FALSE );  /* FALSE=0 */
	}
	if( (x == 0) || (y == 0) ){
		// パス
		return( TRUE );
	}
	if (Ban[y][x]!=SPACE) return FALSE;
	// コウ　チェック
	if (ko_x==x && ko_y==y && ko_num==moveIn-1) return FALSE;
	if (checkSuicide(x,y,colorIn,board)) return FALSE;

	return TRUE;
}

int CMfcKatsunariDoc::SetStoneOnGameBoard(int x,int y,int colorIn,int moveIn)
{
	::setStoneOnGameBoard(x,y,colorIn,moveIn,pureBoardSize);	// 思考部分のもの
	bool isSingleStone=true;
	if (Ban[y-1][x]==colorIn || Ban[y+1][x]==colorIn || Ban[y][x+1]==colorIn || Ban[y][x-1]==colorIn) {
		isSingleStone=false;
	}
	int tmpBoard[BOARD_SIZE][BOARD_SIZE];
	memcpy(tmpBoard,Ban,sizeof(Ban));

	int ret=0;
	Ban[y][x]=colorIn;

	int opp=(colorIn==WHITE)?BLACK:WHITE;
	if (CheckRemove(x+1,y,opp,Ban)) {
		ret+=DoRemove(x+1,y,opp,Ban);
	}
	if (CheckRemove(x-1,y,opp,Ban)) {
		ret+=DoRemove(x-1,y,opp,Ban);
	}
	if (CheckRemove(x,y+1,opp,Ban)) {
		ret+=DoRemove(x,y+1,opp,Ban);
	}
	if (CheckRemove(x,y-1,opp,Ban)) {
		ret+=DoRemove(x,y-1,opp,Ban);
	}
	if (ret==1 && isSingleStone) {
		ko_num=moveIn;
		// どこを取った？
		if (Ban[y][x+1]==SPACE && tmpBoard[y][x+1]!=SPACE) {
			ko_x=x+1;
			ko_y=y;
		}
		if (Ban[y][x-1]==SPACE && tmpBoard[y][x-1]!=SPACE) {
			ko_x=x-1;
			ko_y=y;
		}
		if (Ban[y+1][x]==SPACE && tmpBoard[y+1][x]!=SPACE) {
			ko_x=x;
			ko_y=y+1;
		}
		if (Ban[y-1][x]==SPACE && tmpBoard[y-1][x]!=SPACE) {
			ko_x=x;
			ko_y=y-1;
		}
	}
	return ret;
}

void CMfcKatsunariDoc::OnUpdateSetColor(CCmdUI* pCmdUI) 
{
	pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(TRUE);
}

void CMfcKatsunariDoc::OnSetColor() 
{
	// ダイアログを開いて、色を設定しよう。
	CColorDialog dlg(RGB(R,G,B),0,AfxGetMainWnd());

	if (dlg.DoModal()==IDOK) {
		int colorWk=dlg.GetColor();
		R=colorWk & 0xff;
		G=(colorWk >> 8) & 0xff;
		B=(colorWk >>16) & 0xff;
		UpdateAllViews(NULL);
	}
}


void CMfcKatsunariDoc::OnUpdateContinue(CCmdUI* pCmdUI) 
{
	pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban==REPLAY);
}

void CMfcKatsunariDoc::OnContinue() 
{
	Teban=BWSide[color];
	Play();
}

void CMfcKatsunariDoc::OnUpdateStop(CCmdUI* pCmdUI) 
{
	pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban!=REPLAY && Teban!=NO_PLAY);
}

void CMfcKatsunariDoc::OnStop() 
{
	Teban=REPLAY;
}

void CMfcKatsunariDoc::SetLastMoveFromReplay() 
{
	if (move>1) {
		hama[BLACK]=PlayLog[move-1].hama[BLACK];
		hama[WHITE]=PlayLog[move-1].hama[WHITE];

		// 最後の手の書き換え
		if (move>2) {
			LastMove[color]=move-2;
			LastMoveX[color]=PlayLog[move-2].x;
			LastMoveY[color]=PlayLog[move-2].y;
		} else {
			LastMove[color]=0;
			LastMoveX[color]=0;
			LastMoveY[color]=0;
		}

		int OppornentColor;
		if (color==WHITE) {
			OppornentColor=BLACK;
		} else {
			OppornentColor=WHITE;
		}
		LastMove[OppornentColor]=move-1;
		LastX=LastMoveX[OppornentColor]=PlayLog[move-1].x;
		LastY=LastMoveY[OppornentColor]=PlayLog[move-1].y;
	} else {
		hama[BLACK]=0;
		hama[WHITE]=0;

		// 最後の手の書き換え
		LastMove[BLACK]=0;
		LastMoveX[BLACK]=0;
		LastMoveY[BLACK]=0;

		LastMove[WHITE]=0;
		LastMoveX[WHITE]=0;
		LastMoveY[WHITE]=0;
	}
}

void CMfcKatsunariDoc::OnMatta() 
{
	move-=2;
	replayFromScoreGameBoard(move-1);

	SetLastMoveFromReplay();

	Teban=HUMAN;		// HUMANのみUNDO可だから、HUMANの手番に戻る
	UndoDepth+=2;
	UpdateAllViews(NULL);
}

void CMfcKatsunariDoc::OnUpdateMatta(CCmdUI* pCmdUI) 
{
    pCmdUI->m_bEnableChanged = TRUE;
	pCmdUI->Enable(Teban==HUMAN && move>2);
}
