// Slow Chess 
// by Jonathan Kreuzer
// copyright 2003
//
// Transcript Functions

#include <windows.h>
#include <stdio.h>
#include <time.h>
#include "data.h"
#include "gui.h"
#include "moves.h"
#include "search.h"
#include "transcript.h"
#include "oBook.h"

extern clock_t g_startTime; // from gui.cpp

STranscript *Transcript = (STranscript *) calloc (sizeof (STranscript), 1);

// --------------------
// Read a Piece from a char to the defines used by slow chess
// --------------------
int inline GetPiece ( char cTest, unsigned char &cPiece, int lwr)
{
if ( cTest == 'p' ) {cPiece = WPAWN; return 1; }
if ( cTest == 'K' ) {cPiece = WKING;   return 1; }
if ( cTest == 'N' || (cTest == 'n' && lwr == TRUE) ) {cPiece = WKNIGHT; return 1; }
if ( cTest == 'B' || (cTest == 'b' && lwr == TRUE)  ) {cPiece = WBISHOP; return 1; }
if ( cTest == 'Q' || (cTest == 'q' && lwr == TRUE)  ) {cPiece = WQUEEN;  return 1; }
if ( cTest == 'R' || (cTest == 'r' && lwr == TRUE)  ) {cPiece = WROOK;   return 1; }
return 0;
}

// --------------------
// Get Move XY PGN
// --------------------
void GetMoveXYPGN (char textline[], SBoard &Board, int &sX, int &sY, int &dX, int &dY, unsigned char &piece)
{
int y = 0;
// Castling?
	 if ( ( textline[y] == 'o' || textline [ y ] == 'O' || textline [ y ] == '0' ) && ( textline [ y + 1 ] == '-' ) && (textline [ y + 2 ] == 'o' || textline [ y + 2] == 'O' || textline [ y + 2 ] == '0') )
			{
			if (Board.SideToMove == WHITE) {sX = (Board.WPiece[15]-22)%10; sY = (Board.WPiece[15]-22)/10;}
			if (Board.SideToMove == BLACK) {sX = (Board.BPiece[15]-22)%10; sY = (Board.BPiece[15]-22)/10;}
			dY = sY;
			if (textline [ y+3 ] == '-' && (textline [ y+4 ] == 'o' || textline [ y+4 ] == 'O' || textline [ y+4 ] == '0' ) )
				 dX = sX - 2;
			else dX = sX + 2;
			return;
			}

upgrade = WQUEEN; 
// Piecetype to dst moves
piece = WPAWN;
 sX = -1; sY = -1;  dX = -1;   dY = -1;
y += GetPiece ( textline[y], piece, FALSE );
if ( Board.SideToMove == BLACK) piece+=8;

if ( textline[y] == 'x' || textline[y] == '-')  { y++; }
if ( textline[y] >= 'a' && textline[y] <= 'h' ) { dX = textline[y] -'a'; y++ ; }
if ( textline[y] == 'x' || textline[y] == '-')  { y++; }
if ( textline[y] >= '1' && textline[y] <= '8' ) { dY = '8'-textline[y];  y++ ; }
if ( textline[y] == 'x' || textline[y] == '-')  { y++; }
if ( textline[y] >= 'a' && textline[y] <= 'h' ) { sX = dX; dX = textline[y] -'a'; y++; }
if ( textline[y] >= '1' && textline[y] <= '8' ) { sY = dY; dY = '8' - textline[y]; y++; }

// upgrade type?
if (textline[y] == '=') y++;
y+=GetPiece ( textline[y], upgrade, TRUE );
}

// --------------------
// Do a move spefified by text textline (PGN format)
// --------------------
int DoMovePGN(char textline[], SBoard &Board, int bAddTran, STranscript *pTranscript)
	{
	int sX = 1, sY = 1, dX = 1, dY = 1, nTemp = 0, nTemp2 = 0;
	unsigned char piece = 0;

	if (textline[0] == 'N' && textline[1] == 'u' && textline[2] == 'l' && textline[3] == 'l')
		{Board.SideToMove ^=3;
		 Board.GameTime ++;
		 return 1;
		}
	GetMoveXYPGN ( textline, Board, sX, sY, dX, dY, piece);

	if (dX !=-1 && dY !=-1)
		return DoMoveXY (Board, sX, sY, dX, dY, Board.SideToMove, piece, bAddTran, pTranscript, nTemp, nTemp2);

	return 0;
	}


//
// SkipComments, etc...
//
int NextChar ( char text[], int &y, int nLength )
{
	int depth = 0;
	y++;

	if ( text[y] == '$' )
		{
		y++;
		while ( text[y] >= '0' && text[y] <= '9'  && y<nLength ) y++;
		return y;
		}

	if ( text[y] == '(' ) 
		{
		depth++;
		y++;
		while ( depth > 0 && y<nLength)
			{
			if ( text[y] == '(' ) depth++;
			if ( text[y] == ')' ) depth--;
			y++;
			}
		}

	if ( text[y] == '[' ) 
		{
		depth = 1;
		y++;
		while ( depth > 0 && y<nLength)
			{
			if ( text[y] == '[' ) depth++;
			if ( text[y] == ']' ) depth--;
			y++;
			}
		}

	if ( text[y] == '{' ) 
		{
		depth++;
		y++;
		while ( depth > 0 && y<nLength)
			{
			if ( text[y] == '{' ) depth++;
			if ( text[y] == '}' ) depth--;
			y++;
			}
		}

	return y;
}

// --------------------
// Replay an entire transcript
// --------------------
void GetMovesPGN(char text[], int bDoMove)
{
char sMove [ 40 ];
int y = 0, x = 0;
int nLength = 0;

while (text [y++] != NULL) nLength++;

y = -1;
NextChar (text, y, nLength);

	while (y < nLength) 
		{
		if ( text[y] >= '0' && text[y] <= '9' )
			{
				NextChar (text, y, nLength);
				while ( text[y] >= '0' && text[y] <= '9' ) NextChar (text, y, nLength);
				if ( text[y] == '.') NextChar (text, y, nLength);
				while ( (text[y] == ' ' || text[y] <= 13) ) NextChar (text, y, nLength);
				x = 0;
				while ( (text[y] != ' ' && text[y] > 13) && x < 39 && y < nLength)  
					{sMove [ x++ ] = text [ y ];
					NextChar (text, y, nLength);
					}
				sMove [x++] = NULL;
				DoMovePGN ( sMove, GameBoard, TRUE, Transcript );

			if (GameBoard.SideToMove == BLACK)
				{
				while ( (text[y] == ' ' || text[y] <= 13) ) NextChar (text, y, nLength);
				x = 0;
				while ( (text[y] != ' ' && text[y] > 13 )  && x < 39  && y < nLength)
					{
					if (text[y] == '.' ) 
						{
						while (text[y] == '.' || text[y] == ' ' || text[y] <= 13 )
							NextChar (text, y, nLength);
						x = 0;
						}
					sMove [ x++ ] = text [ y ];
					NextChar (text, y, nLength);
					}
				sMove [x++] = NULL;
				DoMovePGN ( sMove, GameBoard, TRUE, Transcript  );
				}
			}
		NextChar (text, y, nLength);
		}
}


// ============================================
//
// Add Moves to Transcript
// Use the Transcript to Take Back and Redo Moves
//
// ============================================
void ClearTranscript ( STranscript *pTranscript )
{
	if ( pTranscript == NULL || pTranscript == Transcript) 
		{
		pTranscript = Transcript;
		}
	ZeroMemory ( pTranscript, sizeof ( STranscript ) );
}

// --------------------
void GetLastMoveTranscript ( char **sMove )
	{
	 *sMove = Transcript->Moves [ Transcript->nMoves-1 ];
	}

//
void DoTakeBack ( SBoard &Board, STranscript *pTranscript )
	{
	if (pTranscript == NULL) pTranscript = Transcript;
	if (pTranscript->nMoves > 0) 
		{
		pTranscript->nMoves--;
		// Replay the game
		Board = g_StartBoard;
		for (int i = 0; i < pTranscript->nMoves; i++ )
			{
			DoMovePGN ( pTranscript->Moves [ i ], Board, FALSE, NULL );
			}
		}
	}
// ----------------------
//   Add Move to Game Transcript
// ----------------------
void AddTranscript (STranscript *pTranscript, int nMove, const SBoard &Board)
        {
          int src, dst;
		  int x = 0;
		  char *sMove;

		  if (pTranscript == NULL) pTranscript = Transcript;

		  if (pTranscript->nMoves + 4 >= MAX_MOVES)
			{
			  return;
			}

		  // This also means a new move in the game
		  if (pTranscript == Transcript) // update main info but only on main transcript
			{
			if (Board.GameTime >= pTranscript->nMoves)
				{
				clock_t endTime = clock ();
				if ((Board.GameTime % 2) == 1 )  g_nWhiteTime += (100*(endTime - g_startTime))/CLOCKS_PER_SEC;
					else g_nBlackTime += (100*(endTime - g_startTime))/CLOCKS_PER_SEC;
				}
			g_startTime = clock ();

		    // Repetition Board
            AddRepBoard( Board );
			g_GameResult = NONE;
			}
       
		  pTranscript->nMoves = Board.GameTime;

		  sMove = pTranscript->Moves [ Board.GameTime - 1 ];
         
		  sMove [ 6 ] = NULL;
		  sMove [ 7 ] = NULL;

		  if (nMove == -1) 
			{strcpy (sMove, "Null   ");
			 return;
			}
		  src = MSrc(nMove);
          dst = MDst(nMove);

          if (src == 124 || src == 126) 
				{
				 strcpy (sMove, "O-O    ");
                 return;
				}
          if (src == 125 || src == 127) 
				{
			     strcpy (sMove, "O-O-O  ");
                 return;
				}
          
		  sMove [ x++ ] = PieceChars [ Board.Sq[src]&7 ];
		  if (sMove [ 0 ] == 'p') x = 0;
          sMove [ x++ ] = ((src - 22) % 10) + 'a';
          sMove [ x++ ] = '8' - ((src - 22) / 10);
          if (Board.Sq [dst] == EMPTY) sMove [ x++ ] = '-'; else sMove [ x++ ] = 'x';
          sMove [ x++ ] = ((dst - 22) % 10) + 'a';
          sMove [ x++ ] = '8' - ((dst - 22) / 10);
          if ( (Board.Sq[src]&7) == WPAWN && (((dst - 22) / 10) == 7 || ((dst - 22) / 10) == 0) )
			{
			  sMove[ x++ ] = '=';
              sMove [ x++ ] = PieceChars [ upgrade ];
			}
		  SBoard TempBoard = Board;
		  if (x!=7) if  ( CheckingMove (TempBoard.SideToMove, TempBoard, nMove ) != 0) sMove[x++] = '+';
		  while (x < 7) sMove [ x++ ] = ' ';

         }
// ----------------------
// Returns number of moves in the Transcript
// ----------------------
int NumTranMoves ( )
{
 return Transcript->nMoves;
}

void SetTranMoves ( SBoard &Board )
{
 Transcript->nMoves = Board.GameTime-1;
}
// ----------------------
//  Replay Game up to move number nEnd
// ----------------------
void ReplayGame ( SBoard &Board, int nEnd)
     {
	  int i = 0;
	  Board = g_StartBoard;
	  while ( i < (nEnd-1) && i < Transcript->nMoves )
			{
			DoMovePGN ( Transcript->Moves [ i ], Board, FALSE, NULL );
			i++;
			}
      }

// ============================================
//
// Save and Load Transcripts
//
// ============================================
// ------------------------
// Save Game Transcript to Disk
// ------------------------
int SaveGame (int gameResult, char buffer[], int bAppend)
  {
  FILE *fp;
  char *sNone = "Unknown";
  char *sWhiteName, *sBlackName;
  char sResult[20];
  char sFEN [ 256 ];
  int nBlackELO = 0, nWhiteELO = 0;

	strcpy (sOppName , g_sSlow);

	if (PlayerCOLOR == WHITE) 
		{
		sWhiteName = sUserName; 
		nWhiteELO = ELO;
		sBlackName = sOppName;
		}

	if (PlayerCOLOR == BLACK) 
		{
		sWhiteName = sOppName;  
		sBlackName = sUserName;
		nBlackELO = ELO;
		}
	if (PlayerCOLOR == NONE)
		{
		sWhiteName = g_sSlow;  
		sBlackName = g_sSlow;
		}

	if (PlayerCOLOR == BOTH)
		{
		sWhiteName = sUserName;  
		sBlackName = sUserName;
		}
       
  if (bAppend == TRUE) fp = fopen (buffer, "at");
		else fp = fopen (buffer, "wt");
       
   struct tm *newtime;
   time_t long_time;
   time( &long_time );              
   newtime = localtime( &long_time ); 
   int year = newtime->tm_year;
   year+=1900;

   if (gameResult == WHITEWINS ) sprintf (sResult, "1-0");
   else if (gameResult == BLACKWINS ) sprintf (sResult, "0-1");
   else if (gameResult == DRAW ) sprintf (sResult, "1/2-1/2");
   else sprintf (sResult, "*"); // Unknown, in progress, or abandoned.
  
   if (fp==NULL)
	{
	   return 0;
	}
 
	fprintf (fp, "[Event \"Slow Chess Game\"]\n");
	fprintf (fp, "[Date \"%d/%d/%d\"]\n", newtime->tm_mon + 1, newtime->tm_mday, year );
	fprintf (fp, "[Time \"%.2d:%.2d:%.2d\"]\n", newtime->tm_hour, newtime->tm_min, newtime->tm_sec);
	fprintf (fp, "[TimeControl \"%d+%d\"]\n", Minutes * 60, Seconds);
	fprintf (fp, "[Current Ply \"%d\"]\n", GameBoard.GameTime );
	fprintf (fp, "[White \"%s\"]\n", sWhiteName );
	fprintf (fp, "[Black \"%s\"]\n", sBlackName );
	if (nBlackELO!=0 ) fprintf (fp, "[BlackElo \"%d\"]\n", nBlackELO );
	if (nWhiteELO!=0 ) fprintf (fp, "[WhiteElo \"%d\"]\n", nWhiteELO );
	fprintf (fp, "[Result \"%s\"]\n", sResult );

	// Started from a FEN position?
	SBoard TempBoard;
	Setup_StartBoard (TempBoard, 0);
	if (memcmp ( &TempBoard, &g_StartBoard, sizeof (SBoard)) != 0)
		{
		 CopyPosition ( sFEN, g_StartBoard );
		 fprintf (fp, "[FEN \"%s\"]\n", sFEN );
		 fprintf (fp, "[SetUp \"1\"]\n");
		}
	fprintf (fp, "\n");

	// Write out the moves
    for (int i = 0; i < Transcript->nMoves; i++)
           {
			if ( (i%2) == 0 ) fprintf ( fp, "%d. ", i/2 + 1 );
			fprintf ( fp, "%s ", Transcript ->Moves [ i ]);
            if ( (i%2) == 1 ) fputc ('\n', fp);
           }
	fprintf ( fp, "\n\n", i );
	fclose (fp);
	return 1;
  }

// Read from sIn into sOut until a quotation mark
void ReadString ( char *sIn, char *sOut)
{
int x = 0;
	while ( sIn[x] != '\"' )
		{
		sOut[x] = sIn[x];
		x++;
		}
	sOut[x] = NULL;
}


void SGameInfo::Reset ( )
	{
	tempTime = -1;
	strcpy (sWhitePlayer, "X");
	strcpy (sBlackPlayer, "X");
	strcpy (sResult, "X");
	strcpy (sTimeControl, "X");
	strcpy (sFEN, ""); 
	}

//
// Read a Game, return true if there's another game afterwards
//
int ReadGame ( char *cliptext, FILE *fp, SGameInfo &Info)
	{
	    char *pS;

		Info.Reset();
		int i = 0;
		fgets (cliptext, MAX_LEN, fp);
		while ( cliptext[0] == '[' )
			{
			if (strstr ( cliptext, "Current Ply" ) != NULL )
				sscanf ( cliptext, "[Current Ply \"%d\"]", &Info.tempTime);
		    if ( (pS = strstr ( cliptext, "White \"" )) != NULL )
				ReadString ( pS + 7, Info.sWhitePlayer);
		    if ( (pS = strstr ( cliptext, "Black \"" )) != NULL )
				ReadString ( pS + 7, Info.sBlackPlayer);
		    if ( (pS = strstr ( cliptext, "Result \"" )) != NULL )
				ReadString ( pS + 8, Info.sResult);
			if ( (pS = strstr ( cliptext, "Event \"" )) != NULL )
				ReadString ( pS + 7, Info.sEvent);
			if ( (pS = strstr ( cliptext, "TimeControl \"" )) != NULL )
				ReadString ( pS + 13, Info.sTimeControl);
			if ( (pS = strstr ( cliptext, "FEN \"" )) != NULL )
				ReadString ( pS + 5, Info.sFEN);
			fgets (cliptext, MAX_LEN , fp);
			}

		while (!feof (fp ) && i < MAX_LEN )
			{
			cliptext[ i ] = (char) fgetc ( fp );
			if (cliptext[ i ] == NULL) cliptext [ i ] = 32;
			if (cliptext[ i-5 ] == '[' && cliptext[ i-4 ] == 'E' && cliptext[ i-3 ] == 'v' && cliptext[ i-2 ] == 'e' && cliptext[ i-1 ] == 'n' )
				{
				cliptext [ i ] = NULL;
				fseek ( fp, -6 , SEEK_CUR);
				return TRUE;
				}
			i++;
			}
		cliptext [ i ] = NULL;
		return FALSE;
	}

// ------------------------
// Load Transcript From Disk
// ------------------------
int LoadGame ( char buffer[],  int nGame, int bDisplay)
	{
     FILE *fp;
	 SGameInfo Info;
		
     fp = fopen (buffer, "rt");
     if (fp == NULL) return 0;
 
		char *cliptext = (char* ) malloc ( MAX_LEN + 1 );

		int numGames = 1;
		while ( ReadGame ( cliptext, fp, Info) == TRUE && numGames < nGame )
			{
			numGames++;
			}

		fclose (fp);

		NewGame(  ); // Set board to start position
		// Set positon from FEN if there is one.
		if (Info.sFEN[0]!=NULL) SetBoardFromText (GameBoard, Info.sFEN, strlen ( Info.sFEN ), 0 );

		GetMovesPGN ( cliptext, TRUE ); // Read in ( &play) the moves

		free (cliptext);

		if (Info.tempTime != -1)
			{
			ReplayGame (GameBoard, Info.tempTime);
			}

		g_bGameStarted = FALSE;
        return numGames;
        }

// ------------------------
// Create a book file from a PGN of games.
// ------------------------
int PGNtoBook ( char buffer[],  int nGame, int nStart, int nEnd, int nTimes, int nAddResults)
        {
        FILE *fp;
		SGameInfo Info;
		int nResult, bNextGame = TRUE;

		Info.tempTime = -1;
		strcpy (Info.sResult, "X");
        
        fp = fopen (buffer, "rt");
        if (fp == NULL) return 0;
 
		char *cliptext = (char* ) malloc ( MAX_LEN + 1 );
		Setup_StartBoard ( GameBoard, 0 );
		g_StartBoard = GameBoard;

		int numGames = 1;
		while ( numGames <= nGame && bNextGame == TRUE )
			{
			bNextGame = ReadGame ( cliptext, fp, Info);
			GameBoard = g_StartBoard;
			GetMovesPGN ( cliptext, FALSE );
			GameBoard = g_StartBoard;

			if (strstr (Info.sResult, "1-0") != NULL) nResult=3;
			else if (strstr (Info.sResult, "0-1") != NULL) nResult=1;
			else nResult=2;

			for (int i = 0; i< (nEnd*2); i++)
				{
				DoMovePGN ( Transcript->Moves [ i ], GameBoard, FALSE, NULL );
				if (i>= ((nStart-1)*2))
					{
					// Draw
					if (nResult==2 && (nAddResults&2)==2 ) pNewBook->AddPosition ( GameBoard, 101, TRUE );
					// Win
					if ( (nAddResults&1)==1 )
						{
						// SideToMove == BLACK means this position is from white's move.
						if (GameBoard.SideToMove == BLACK && nResult==3 ) pNewBook->AddPosition ( GameBoard, 101, TRUE );
						if (GameBoard.SideToMove == WHITE && nResult==1 ) pNewBook->AddPosition ( GameBoard, 101, TRUE );
						}
					// Loss
					if ( (nAddResults&4)==4 )
						{
						if (GameBoard.SideToMove == BLACK && nResult==1 ) pNewBook->AddPosition ( GameBoard, 101, TRUE );
						if (GameBoard.SideToMove == WHITE && nResult==3 ) pNewBook->AddPosition ( GameBoard, 101, TRUE );
						}
					}
				}
			numGames++;
			}

		fclose (fp);
		free (cliptext);
		NewGame ( );

		pNewBook->Save ( "TempGen5.scb", nTimes);
		delete pNewBook;
		pNewBook = new COpeningBook;
		pNewBook->Load ( "TempGen5.scb" );
		DeleteFile ("TempGen5.scb");

        return numGames;
        }
