#include "win32compat.h"
#include <stdio.h>
#include <time.h>

#include "data.h"
#include "transcript.h"
#include "oBook.h"
#include "search.h"
#include "gui.h"
#include "moves.h"

// ============================================
//
//   OPENING BOOK Functions. 
//
// ============================================
void COpeningBook::AddPosition ( SBoard &Board, short wValue, int bQuiet )
{
 unsigned long ulKey   = Hash_Board  ( Board );
 unsigned long ulCheck = Hash_Board2 ( Board );
 AddPosition (ulKey, ulCheck, wValue, bQuiet );
}

//
//
int COpeningBook::GetValue ( SBoard &Board )
{
 unsigned long ulKey   = Hash_Board  ( Board );
 unsigned long ulCheck = Hash_Board2 ( Board );

 int nEntry = ulKey&131071;
 SBookEntry *pEntry;

 pEntry = &m_pHash[ nEntry ];
 while (pEntry != NULL && pEntry->wValue != NOVAL)
	{
	if (pEntry->ulCheck == ulCheck  && pEntry->wKey == (ulKey>>16) )
		{
		return pEntry->wValue;
		}
	pEntry = pEntry->pNext;
	}

return NOVAL;
}

//
//
//
void COpeningBook::AddPosition ( unsigned long ulKey,  unsigned long ulCheck, short wValue, int bQuiet )
{
 char sTemp[ 255 ];
 int nEntry = ulKey&131071;

 SBookEntry *pEntry = &m_pHash[ nEntry ]; 

 while (pEntry->wValue != NOVAL)
	{
	if (pEntry->ulCheck == ulCheck && pEntry->wKey == (ulKey>>16) )
		{
		if (bQuiet != TRUE) 
			{
			 sprintf ( sTemp, "Position Exists. %d\nValue was %d now %d", nEntry, pEntry->wValue, wValue);
			}
		if (wValue == 101 && pEntry->wValue > 100) pEntry->wValue++; // counting for pgn add
			else if (wValue!=101) pEntry->wValue = wValue; 
		return;
		}
	if (pEntry->pNext == NULL)
		{
		if (m_nListSize >= BOOK_EXTRA_SIZE)
			{
			return;
			}
		pEntry->pNext = &m_pExtra[ m_nListSize ];
		m_nListSize++;
		}
	pEntry = pEntry->pNext;
	}

 // New Position
 m_nPositions++;
 pEntry->ulCheck = ulCheck;
 pEntry->wKey = (unsigned short)(ulKey>>16);
 pEntry->wValue = wValue;
}

// ----------------------------------
//  Remove a position from the book.
//  (if this position is in the list, it stays in memory until the book is saved and loaded.)
// ----------------------------------
void COpeningBook::RemovePosition ( SBoard &Board, int bQuiet )
{
 unsigned long ulKey   = Hash_Board  ( Board );
 unsigned long ulCheck = Hash_Board2 ( Board );
 int nEntry = ulKey&131071;
 SBookEntry *pEntry = &m_pHash[ nEntry ];
 SBookEntry *pPrev = NULL; 

 while (pEntry != NULL && pEntry->wValue != NOVAL)
	{
	if (pEntry->ulCheck == ulCheck && pEntry->wKey == (ulKey>>16) )	
		{
		m_nPositions--;
		if (pPrev != NULL) pPrev->pNext = pEntry->pNext;
			else 
				{
				if (pEntry->pNext != NULL) *pEntry = *pEntry->pNext;
					else pEntry->wValue = NOVAL;
				}
		return;
		}
	 pPrev = pEntry;
	 pEntry = pEntry->pNext;
	}
}

// --------------------
//
// FILE I/O For Opening Book
//
// --------------------

// LOAD
int COpeningBook::Load ( char *sFileName )
{
 FILE *fp;
 SBookEntry TempEntry;
 int i = 0;

 if ( (fp = fopen (sFileName, "rb")) == NULL) 
	{
	return FALSE;
	}
  
  unsigned long wKey = TempEntry.Load ( fp );
  while (!feof ( fp ) )
	{
	  i++;
	  wKey += (TempEntry.wKey << 16);
	  AddPosition ( wKey, TempEntry.ulCheck, TempEntry.wValue, TRUE );
	  wKey = TempEntry.Load ( fp );
	}

  fclose ( fp );
  return TRUE;
}

//
// SAVE
//
// the book generation code muddles this up a bit, nTimes is 
// the number of times this position must have occured to be saved.
//
void COpeningBook::Save ( char *sFileName, int nTimes )
{
 FILE *fp;
 fp = fopen (sFileName, "wb");
 if (fp == NULL) return;

 SBookEntry *pEntry;
 int Num = 0;
 nTimes+=100;
 
 for (int i = 0; i < 131072; i++)
	{
	 pEntry = &m_pHash [ i ];
	 if (pEntry->wValue != NOVAL)
			{
			if (pEntry->wValue >= nTimes) pEntry->wValue = 0;
			if (pEntry->wValue < 100 ) {
				 pEntry->Save ( fp, i );
				 Num++;}
	
			while (pEntry->pNext != NULL)
				{
				pEntry = pEntry->pNext;
				if (pEntry->wValue >= nTimes) pEntry->wValue = 0;
				if (pEntry->wValue < 100) {
					 pEntry->Save ( fp, i );
					 Num++;}
				}
			}
	}
  fclose ( fp );
}

// --------------------
//
// Find a Move from the opening Book
//
// Do all legal moves, then check to see if the position is in the opening book.
// --------------------
int COpeningBook::FindMoves ( SBoard &Board, int OutMoves[], short wValues[] )
{
int u1, u2, i, val, nFound;
Movestruct Moves;
SBoard TempBoard;

if (Board.SideToMove == WHITE)	FindMovesWhiteCh ( Board, Moves);
if (Board.SideToMove == BLACK)	FindMovesBlackCh ( Board, Moves);

upgrade = WQUEEN; 
nFound = 0;

for (i = 1; i<= Moves.nummoves + 50; i++)
	{
	TempBoard = Board;
	if (i == Moves.numAttackmoves + 1) i = 51;
	Do_Move ( TempBoard, Moves.Moves[i], u1, u2, WQUEEN );
	val = GetValue ( TempBoard );
	// Add move if it leads to a position in the book 
	// (and doesn't lead to a repeated position, since when storing positions this can happen.)
	if ( val != NOVAL && DrawByRepetition ( TempBoard ) == 0) 
		{
		OutMoves [nFound] = Moves.Moves [ i ];
		wValues[ nFound ] = val;
		nFound++;
		}
	}

return nFound;
}

// --------------------
// Try to do a move in the opening book
// --------------------
int COpeningBook::DoMove ( SBoard &Board, int &nBestMove )
{
 srand (clock()); // Randomize
 int nVal = NOVAL, nMove = -1;
 int u1, u2, nMax, i, nGood;
 int Moves [ 60 ];
 int GoodMoves [ 60 ];
 short wValues [60], wGoodVal [60];

 int nFound = FindMoves ( Board, Moves, wValues );

 if (Board.SideToMove == BLACK) nMax = 29000;
 if (Board.SideToMove == WHITE) nMax = -29000;

 nGood = 0;
 for (i = 0; i < nFound; i++) 
	{
	 if ( (Board.SideToMove == BLACK && wValues[i] <= 0) || (Board.SideToMove == WHITE && wValues[i] >= 0) )
			{
			 nMax = wValues[i];
			 wGoodVal [nGood] = wValues [i];
			 GoodMoves [nGood] = Moves[i];
			 nGood++;
			}
	}

  if (nGood == 0) return NOVAL;

  nMove = rand()%nGood;
  nVal = wGoodVal[ nMove ];

  if (abs(nVal) != 2)
	{
	 nMove = rand()%nGood;
	 nVal = wGoodVal[ nMove ];
	}
 if (abs(nVal) == 0)
	{
	 nMove = rand()%nGood;
	 nVal = wGoodVal[ nMove ];
	}

 if (nMove!=-1)
	{
	nBestMove = GoodMoves [ nMove ];
	AddTranscript (NULL, GoodMoves[nMove], Board);
	Do_Move ( Board, GoodMoves [nMove], u1, u2, WQUEEN);
	}

 return nVal;
}

// --------------------
// Don't play the *exact* same losing line again
// --------------------
int COpeningBook::DoLearning ( int nResult )
{
int Moves [ 50 ];
short wValues [ 50 ];
SBoard TempBoard = g_StartBoard;
int nEnd = GameBoard.GameTime - 1;
if (nEnd > 60) nEnd = 60;
if (nResult != WHITEWINS && nResult!=BLACKWINS) return FALSE;

for (int i = nEnd; i > 0; i--)
	{
	ReplayGame (TempBoard, i);
	if ((TempBoard.SideToMove == WHITE && nResult == WHITEWINS) // STM==WHITE means black moved to this position 
	   || (TempBoard.SideToMove == BLACK && nResult == BLACKWINS) )
		{
		 RemovePosition ( TempBoard, TRUE); // Remove this position
		}
	else
		{
		 if (FindMoves (TempBoard, Moves, wValues ) > 0) return TRUE; // Another branch is in the book, so our job is done
		}
	}

return FALSE;
}
