//
//
// Slow Chess Winboarding Functions
//
//
#define WIN32_LEAN_AND_MEAN 
#include <stdio.h>
#include "win32compat.h"
#include <cctype>
#include <math.h>
#include "data.h"
#include "transcript.h"
#include "oBook.h"
#include "gui.h"
#include "search.h"
#include "moves.h"
#include "evaluate.h"

int g_bUCI = FALSE;
int g_bQuit = FALSE;

//
// Add Log
//
void AddLog ( char *String )
{
if (strlen (String) <= 0) return;

FILE *FP = fopen ("testlog.txt", "at");
fprintf (FP,"%s",String);
fclose (FP);
}

// -----------------------------------------------------
void wbSendFeatures( )
{
	printf("feature myname=\"%s\" name=1 setboard=1 ping=1\n", g_sSlow);
	fflush(stdout);
	printf("feature done=1\n");
	fflush(stdout);
}

// -----------------------------------------------------
void uciStartup ()
	{
	g_bUCI = TRUE;
	bDisplayInfo = TRUE;

	printf("id name %s\n", g_sSlow);
	fflush(stdout);
	printf("id author Jonathan Kreuzer\n");
	fflush(stdout);
	printf ("option name Hash type spin default 24 min 2 max 196\n");
	fflush(stdout);
	printf ("option name Clear Hash type button\n");
	fflush(stdout);
	printf ("uciok\n");
	fflush(stdout);
	}

// -----------------------------------------------------
void wbSendPonder(char * szMove)
{
	printf("Hint: %s", szMove);
	fflush(stdout);
}

// -----------------------------------------------------
void  wbSendThinking(int ply, int eval, unsigned long tm, unsigned int nodes, int bPV, int bFailHigh, int bFinishedDepth)
{
	if (bDisplayInfo == FALSE || g_bWinBoard == FALSE) return;

	char sPVbuffer [1060];
	char sScoreType[32], sExtra[32];
	int format;
	if (g_bUCI == TRUE) format = 2; else format = 0;

	if (bFailHigh == TRUE) 
		{
		GetPVMoveString (sPVbuffer, PV[MAXDEPTH-1][1], format);
		if (g_bUCI == FALSE) strcat (sPVbuffer, "{++}");
		}
	else
		{
		if (bPV == TRUE)  GetPVString ( sPVbuffer, format );
			else sPVbuffer[0] = NULL;
		}

	if (g_bUCI == FALSE)
		{
		if (g_SearchColor == BLACK && bEvalWhite == FALSE) eval = -eval; // flip eval for black
		printf("%d %d %lu %u %s\n", ply, eval, tm, nodes, sPVbuffer);
		fflush(stdout);
		}
	if (g_bUCI == TRUE)
		{
		unsigned int nps;
		if (tm <= 0) nps = 0;
			else nps = (unsigned int)((double)nodes*100.0f/(double)tm);

		if ( (abs(eval) == abs(DRAWSCORE) || abs(eval) <= 2) && GameBoard.GameTime > 80) eval = 0;
		if (g_SearchColor == BLACK) eval = -eval;
		if (abs(eval) > 9000) 
			{
			strcpy (sScoreType,"mate");
			if (eval < 0) 
					 eval = -(9999 - abs(eval))/2; 
				else eval = (9999 - abs(eval))/2;
			}
			else strcpy (sScoreType, "cp");
		if (eval == 0 && ply == 0) eval = 1;

		if (bFailHigh == TRUE) strcpy (sExtra, "lowerbound ");
			else strcpy (sExtra, "");

		printf ("info depth %d score %s %d %stime %u nodes %u nps %u pv %s\n", ply, sScoreType, eval, sExtra, tm*10,  nodes, nps, sPVbuffer);
		fflush(stdout);
		}
	// Maybe display something for bFinishedDepth in thinking?

	// Send analysis in analysis mode
	if (g_bUCI == FALSE) wbSendThinking2 ( bFinishedDepth );
}

// -----------------------------------------------------
void wbSendResult(char *sResult, char *sWhy)
{
	if (sWhy != NULL)
		printf("%s {%s}\n", sResult, sWhy);
	else
		printf("%s\n", sResult);
	fflush(stdout);
}

// -----------------------------------------------------
void wbTestSendResult ( SBoard &Board )
{
int mate = IsMate (Board, Board.SideToMove );
if (mate == 2) wbSendResult ("1/2-1/2", "Draw by Stalemate");
if (mate == 1)
	{
	if (Board.SideToMove == BLACK) wbSendResult ("1-0", "White Mates");
	if (Board.SideToMove == WHITE) wbSendResult ("0-1", "Black Mates");
	}
if ((Board.nWPawns == 0 && Board.nBPawns == 0) 
	&& (Board.nBP == 0 || Board.nWP == 0) && (Board.nBP <= 1 && Board.nWP <= 1) 
	&& (Board.nWMinors == Board.nWP && Board.nBMinors == Board.nBP ) 
	)
	{
	wbSendResult ("1/2-1/2", "Draw by Insufficent Material to Mate");
	}
if ( (Board.GameTime - Board.LastReversible) >= 100)
	{
	wbSendResult ("1/2-1/2", "Draw by 50 Move Rule");
	}
if ( DrawByRepetition (Board)  > 1 ) 
	{
	wbSendResult ("1/2-1/2", "Draw by Repetition");
	}
}

// -----------------------------------------------------
void wbPonder ( )
{
Thinktime = 1200.0f;
stopthinking = 3;
Sleep(50);
ThinkOfMove ( 1 );
}

// -----------------------------------------------------
//  Enters opponent's move
// -----------------------------------------------------
void wbOppMove ( char *sMove, SBoard &Board )
{
 int sX, sY, dX, dY;
 unsigned char cPiece;
 g_GameResult = NONE;
 GetMoveXYPGN ( sMove, Board, sX, sY, dX, dY, cPiece); 
 PlayerMove (sX, sY, dX, dY, cPiece);
}

// -----------------------------------------------------
//  Starts a new game (sets up board)
// ------------------------------------------------------
void wbNewGame ( )
{
	NewGame (  );
	Thinktime = 2.6f;
	g_GameResult = NONE;
	MaxAhead = 28;
	fPanicTime = 2.0f;
	g_bWinBoard = TRUE;
	PlayerCOLOR = WHITE;
	bAcceptDraw = FALSE;
}

// -----------------------------------------------------
//  Sends the computer's move to Winboard
// -----------------------------------------------------
// For UCI
void inline UCISendMove ( )
{
	char sCurMove[64], sPonderMove[64];
	GetPVMoveString (sCurMove, PV[0][1], 2);

	int nMove;
	if ( GetPonderMove ( nMove ) ) 
		{
		strcpy ( sPonderMove, "ponder ");
		GetPVMoveString (&sPonderMove[7], nMove, 2);
		}
	else strcpy (sPonderMove, "");

	printf ("bestmove %s %s\n", sCurMove, sPonderMove);
	fflush (stdout);
}

// For Winboard
void wbSendMove( )
{
 if (g_bUCI == TRUE) 
	{
	 UCISendMove ();
	 return;
	}
 char *sTranMove;
 GetLastMoveTranscript ( &sTranMove );

 printf("move %s\n", sTranMove);
 fflush(stdout);
}

// -----------------------------------------------------
//   UCI Command Processing
// -----------------------------------------------------
void uciProcessCommand ( char *sBuf)
{
static char buffer [255];
char *pBuf;
unsigned int i;
static int nMode = 0, nSecs = 0, nMoves = 0;
static float fCompTm=60;


		if (strstr (sBuf, "isready"))
			{
			printf("readyok\n");
			fflush(stdout);
			}

		if (strstr (sBuf, "go"))
		{
		if (strstr (sBuf, "infinite"))
				{
				PlayerCOLOR = BOTH;
				nMode = 1;
				wbPonder ();
				}
			else
			{
			// Time Stuff
			if ( (pBuf = strstr (sBuf, "wtime")) )
				{
				sscanf (pBuf, "wtime %f", &fCompTm);
				g_nWhiteTime = (int)(fCompTm*.1f);
				}
			if ( (pBuf = strstr (sBuf, "btime") ))
				{
				sscanf (pBuf, "btime %f", &fCompTm);
				g_nBlackTime = (int)(fCompTm*.1f);
				}
			Seconds = 0;
			if ( GameBoard.SideToMove == WHITE && (pBuf = strstr (sBuf, "winc")) )
				{
				sscanf  (pBuf, "winc %d", &nSecs);
				Seconds = nSecs / 1000;
				}
			if ( GameBoard.SideToMove == BLACK && (pBuf = strstr (sBuf, "binc")) )
				{
				sscanf  (pBuf, "binc %d", &nSecs);
				Seconds = nSecs / 1000;
				}
			// Search Stuff
			MaxAhead = 28;
			if ( (pBuf = strstr (sBuf, "depth") ))
				{
				sscanf (pBuf, "depth %d", &MaxAhead);
				}
			g_nMaxNodes = 0;
			if ( (pBuf = strstr (sBuf, "nodes") ))
				{
				sscanf (pBuf, "nodes %d", &g_nMaxNodes);
				}
			nSecsPerMove = 0;
			if ( (pBuf = strstr (sBuf, "movetime") ))
				{
				sscanf (pBuf, "movetime %d", &nSecsPerMove);
				nSecsPerMove /= 1000;
				}

			if (GameBoard.SideToMove == BLACK) fCompTm = (float)g_nBlackTime; else	fCompTm = (float)g_nWhiteTime;

			if ( (pBuf = strstr (sBuf, "movestogo") )) 
				{
				fCompTm *= .01f;
				sscanf (pBuf, "movestogo %d", &nMoves);
				float fMovesLeft = (float)nMoves;
				Thinktime = fCompTm / ( fMovesLeft +1.0f );
				if (GameBoard.GameTime < 44 && nMoves > 18) 
					{
					Thinktime = (Thinktime + (fCompTm / (fMovesLeft-9.0f))  ) *.5f;
					}
				SetPanicTime ( fCompTm );
				}
			else SetThinkTime ( fCompTm *.01f, nMoves, Seconds );

			nMode = 0;
			if (GameBoard.SideToMove == WHITE) PlayerCOLOR = BLACK; else PlayerCOLOR = WHITE;
			if ( strstr (sBuf, "ponder") )
				{
				nMode = 1;
				ThinkOfMove ( 4 );
				}
			else 
				{
				if (g_ponder !=0) StopThinking ();
				ThinkOfMove ( 0 );
				}
			}
		}

		if (strstr (sBuf, "ponderhit"))
			{
			WaitForSingleObject ( hEngineReady, 1000 );
			if (g_ponder == 1) 
				{
				GameBoard = g_PonderBoard;
				ExtendThinkTime ( );
				g_ponder = 2; 
				}
			else
				{
				GameBoard = g_PonderBoard;
				ThinkOfMove ( 0 );
				}
			}

		if (strstr (sBuf, "stop"))
			{
			PlayerCOLOR = BOTH;
			int nOldThink = thinking;
			StopThinking ( );
			if (nOldThink !=0 || nMode == 1) UCISendMove ( );
			nMode = 0;
			}

		if (strstr (sBuf, "quit"))
			{
			g_bQuit = TRUE;
			}

		if ( strstr (sBuf, "position"))
			{
			if ( strstr (sBuf, "position startpos"))
				{
				NewGame ( );
				}
			if ( strstr (sBuf, "position fen"))
				{
				SetBoardFromText ( GameBoard, strstr (sBuf, "position fen") + 12, strlen (sBuf)- 12, 0 );
				}

			if (strstr (sBuf, "moves "))
				{
				buffer[0] = NULL;
				i = strstr (sBuf, "moves") + 5 - sBuf;

				while (i <= strlen (sBuf))
					{
					int index = 0;
					while (sBuf[i]!= ' ' && sBuf[i]!=NULL)
						buffer[index++] = sBuf[i++];

					DoMovePGN (buffer, GameBoard, TRUE, NULL);
					i++;
					}
				}
			}

		if ( memcmp (sBuf, "setoption name", 14) == 0)
			{
			if ( strstr (sBuf, "setoption name Clear Hash") )
				{
				Clear_Hash ( );
				}

			if ( strstr (sBuf, "setoption name Hash") )
				{
				unsigned int nMegs = 0;
				sscanf (sBuf, "setoption name Hash value %d", &nMegs );
				SetHashMB ( nMegs );
				}
			}
}

//  -----------------------------------------
//      Winboard Command Processing
//  -----------------------------------------
void wbProcessCommand ( char *sBuf )
{
static int nMode = 0, nSecs = 0, nMoves = 0;
static float fCompTm=60, fOppTm=60;

	//
	// UCI mode startup
	//
	if ( memcmp (sBuf, "uci", 3) == 0)
		{
		if ( memcmp (sBuf, "ucinew", 6) !=0 ) uciStartup ();
		return;
		}

	//
	// WINBOARD Mode Commands
	//
		if ( strstr (sBuf, "accepted"))
			{
			return;
			}

		if ( sBuf[0] == 'n' && sBuf[1] == 'a' && sBuf[2] == 'm' && sBuf[3] == 'e')
			{
			strcpy (sUserName, sBuf+5);
			int nLen = strlen (sUserName) - 1;
			for (int i = 0; i < nLen; i++) if (sUserName [ nLen ] == '\n') sUserName [nLen] = NULL;
			return;
			}

		if ( strstr (sBuf, "protover" ) )
			{
			wbSendFeatures();
			}

		if (strcmp (sBuf, "time") )
			{
			sscanf (sBuf, "time %f", &fCompTm);
			if (PlayerCOLOR == BLACK) g_nWhiteTime = Minutes * 6000 - (int)fCompTm;
				else g_nBlackTime = Minutes * 6000 - (int)fCompTm;
			}
		if (strstr (sBuf, "otim") )
			{
			sscanf (sBuf, "otim %f", &fOppTm);
			if (PlayerCOLOR == BLACK) g_nBlackTime = Minutes * 6000 - (int)fOppTm;
				else g_nWhiteTime = Minutes * 6000 - (int)fOppTm;
			}
		// Stop playing
		if ( strstr (sBuf, "force" ) )
			{
			nMode = 0;
			PlayerCOLOR = BOTH;
			stopthinking = 3;
			while (thinking != 0) {Sleep(50);}
			}
		// Opponent Moves
		if (isdigit (sBuf[1]) && isdigit(sBuf[3]) ) 
			{
			if (nMode == 1) stopthinking = 3;

			SetThinkTime ( fCompTm *.01f, nMoves, nSecs );

			wbOppMove ( sBuf, GameBoard );
			if (nMode == 1) wbPonder ();
			} 

		// Just Go
		if ( sBuf[0] == 'g' && sBuf[1]=='o' )
			{
			nMode = 0;
			if (GameBoard.SideToMove == BLACK) PlayerCOLOR = WHITE; else PlayerCOLOR = BLACK;
			stopthinking = 3;
			while (thinking != 0) {Sleep(50);}
			SetThinkTime ( fCompTm *.01f, nMoves, nSecs );
			g_GameResult = NONE;
			if ( CheckGameOver ( GameBoard, FALSE, g_GameResult) == FALSE)	ThinkOfMove ( 0 );
			}

		if ( strstr (sBuf, "setboard"))
			{
			if (nMode == 1) stopthinking = 3;
			SetBoardFromText ( GameBoard, strstr (sBuf, "setboard") + 7, strlen (sBuf)- 7, 0 );
			if (nMode == 1) wbPonder ();
			}
		if (strstr (sBuf, "level" ) )
			{
			int minutes=0;
			sscanf  (sBuf, "level %d %d %d", &nMoves, &minutes, &nSecs);
			Minutes = minutes;
			Seconds = nSecs;
			}
		if ( strstr (sBuf, "post" ) )
			{
			bDisplayInfo = TRUE;
			}
		if ( strstr (sBuf, "nopost" ) )
			{
			bDisplayInfo = FALSE;
			}
		if ( strstr ( sBuf, "hard") )
			{
			bPonder = TRUE;
			}
		if ( strstr ( sBuf, "easy") )
			{
			bPonder = FALSE;
			}
		if ( strstr (sBuf, "new" ) )
			{
			wbNewGame ( );
			}
		if ( strstr (sBuf, "quit" ) )
			{
			g_bWinBoard = FALSE;
			exit (0);
			}

		if ( strstr (sBuf, "undo"))
			{
			if (nMode == 1) stopthinking = 3;
			ReplayGame (GameBoard, GameBoard.GameTime - 1);
			if (nMode == 1) wbPonder ();
			}

		if ( strstr (sBuf, "evaluate"))
			{
			printf ("Evaluation: %d", EvaluateBoard ( GameBoard, -30000, 30000) );
			fflush(stdout);
			}

		if ( strstr (sBuf, "remove"))
			{
			if (nMode == 1) stopthinking = 3;
			ReplayGame (GameBoard, GameBoard.GameTime - 2);
			if (nMode == 1) wbPonder ();
			}

		if ( strstr (sBuf, "result"))
			{
			if (strstr (sBuf, "1-0") != NULL) g_GameResult = WHITEWINS;
			else if (strstr (sBuf, "0-1") != NULL) g_GameResult = BLACKWINS;
			else g_GameResult = DRAW; 
	
			if (GameBoard.GameTime > 2 && SCsettings.bLog == TRUE ) 
				SaveGame (g_GameResult, "WinboardGames.pgn", TRUE);	
			stopthinking = 3;

			// Do Single-Session Defensive Book Learning
			if ((g_GameResult == WHITEWINS && PlayerCOLOR == WHITE)	|| (g_GameResult == BLACKWINS && PlayerCOLOR == BLACK))
				pNewBook->DoLearning ( g_GameResult );
			}

		if ( strstr (sBuf, "draw"))
			{
			if ( bAcceptDraw == TRUE && SCsettings.bDraw == TRUE)
				{
				printf("offer draw\n");
				fflush(stdout);
				}
			}

		if ( strstr (sBuf, "analyze"))
			{
			PlayerCOLOR = BOTH;
			nMode = 1;
			wbPonder ();
			}

		if ( sBuf[0] == '.' && sBuf [1] == '\n')
			{
			wbSendThinking2 ( FALSE );
			}

		if (strstr (sBuf, "sd ") )
			{
			sscanf (sBuf, "sd %d", &MaxAhead );
			}

		if ( sBuf[0] == '?' && sBuf [1] == '\n')
			{
			stopthinking = 1;
			}

		if ( strstr ( sBuf, "ping") )
			{
			int nPing = 0;

			while (thinking != 0)
				{
				stopthinking = 1;
				Sleep(50);
				}
			sscanf (sBuf, "ping %d", &nPing );
			printf("pong %d\n", nPing);
			fflush(stdout);
			}
}

// -----------------------------------------------------
// Main Loop
// -----------------------------------------------------
void wbMainLoop ( )
{
wbNewGame ( );
char	sBuf[8192];

while ( g_bQuit == FALSE ) 
	{
	if (fgets(sBuf, sizeof(sBuf), stdin) != sBuf)
		break;

	if ( g_bUCI == TRUE)
		uciProcessCommand ( sBuf );
		else
		wbProcessCommand ( sBuf );
	}

g_bWinBoard = FALSE;
exit (0);
}