#include "RobboLito.h"
#include <string.h>
#include "control.h"

#if 0 /* UCI? */
int GetNumCPU() /*Get number of CPUs */
{
#ifdef WINDOWS /*Windows*/
  SYSTEM_INFO si;
  GetSystemInfo( &si );
  CPUS_SIZE = si.dwNumberOfProcessors;
#else /*LINUX */
#include <unistd.h>
  CPUS_SIZE = sysconf( _SC_NPROCESSORS_CONF );
#endif
  /*
#ifdef APPLE
#include <sys/sysctl.h>
CPUS_SIZE = 1;
size_t size=sizeof(CPUS_SIZE) ;
sysctlbyname("hw.ncpu",&CPUS_SIZE,&size,NULL,0);
#endif
  */
  return CPUS_SIZE;
} /*End Get number of CPUs*/
#endif

#if defined(UTILITIES)
#define SWAP_TWO(x, y) { (x) ^= (y); (y) ^= (x); (x) ^= (y); }
#define SHUFFLE_TWO(x) { (x) = ((((x) & 0x3) << 2) | (((x) & 0xc) >> 2)); }
static void InvertBoard (typePOS* POSITION)
{
  int f, r, sq;
  for (f = FA; f <= FH; f++)
    for (r = R1; r <= R4; r++)
      SWAP_TWO (POSITION->sq[8 * r + f], POSITION->sq[8 * (7 - r) + f]);
  for (sq = A1; sq <= H8; sq++)
    {
      if (POSITION->sq[sq])
	POSITION->sq[sq] ^= 8;
      if ((POSITION->sq[sq] & 0x6) == 0x4)
	POSITION->sq[sq] ^= 1; /* HACK, color bishop */
    }
  POSITION->wtm ^= 1;
  if (POSITION->DYN->ep)
    POSITION->DYN->ep ^= 0x7 << 3; /* HACK, flip 3rd/6th */
  SHUFFLE_TWO (POSITION->DYN->oo);
  InitBitboards (POSITION);
}

static void EvalTest (typePOS* POSITION)
{
  int v;
  EvalExplanation (POSITION, -32767, 32767, 0);
  v = POSITION->DYN->Value;
  Eval (POSITION, -32767, 32767, 0);
  if (v != POSITION->DYN->Value)
    {
      printf ("ERROR with EvalExplanation compared to EvalEvaluation %d %d\n",
	      v, POSITION->DYN->Value);
    }
  InvertBoard (POSITION);
  Eval (POSITION, -32767, 32767, 0);
  if (v != POSITION->DYN->Value)
    {
      printf ("ERROR in InvertBoard Eval compared to Invert %d %d\n",
	      v, POSITION->DYN->Value);
      printf ("FEN is: %s\n", EmitFen (ROOT_POSIZIONE0, STRING1[0]));
    }
  InvertBoard (POSITION);
}

static int UCI_fen ()
{
  SEND ("info string %s\n", EmitFen (ROOT_POSIZIONE0, STRING1[0]));
  return TRUE;
}
#endif

#if 0
#define READYOK() readyok ()
#else
#define READYOK() /* */
#endif

typedef enum { UCI_SPIN, UCI_CHECK, UCI_BUTTON, UCI_STRING } enum_UCI;
typedef struct
{
  char name[0x100];
  int type; sint32 min, max, def;
  void* var;
  int (*action)(int);
} UCI_type;

UCI_type UCI_OPTIONS[256]=
  {
    { "Hash", UCI_SPIN, 1, 65536, 32, &CURRENT_HASH_SIZE, &InitHash },
    { "PawnsHash", UCI_SPIN, 1, 1024, 2, NULL, &InitPawnHashWrapper },
    { "PVHash", UCI_SPIN, 1, 1024, 2, NULL, &InitPVHash }, /* 2 for LARGE */
    { "EvalHash", UCI_SPIN, 1, 1048576, 256, NULL, &InitEvalHash },
    { "SmallEvalHash", UCI_CHECK, 0, 0, FALSE, &SMALL_EVAL_HASH },
#ifdef YUSUF_MULTICORE
    { "TitanicMode", UCI_CHECK, 0, 0, FALSE, &TITANIC_MODE, &ivan_init_smp },
    { "TitanicCPUsSizings", UCI_SPIN, 1, MAX_CPUS, 1, &CPUS_SIZE, &ivan_init_smp },
#endif
#ifdef MODE_GAME_PLAY
    { "Ponder", UCI_CHECK, 0, 0, FALSE, &UCI_PONDER, NULL },
#endif
#ifdef MODE_ANALYSIS
    { "AllowNullMove", UCI_CHECK, 0, 0, TRUE, &NULL_MOVE_INTACT, NULL},
#endif
    { "VerifyNullMove", UCI_CHECK, 0, 0, TRUE, &VERIFY_NULL, NULL },
    { "AlternativeTimeUsage", UCI_CHECK, 0, 0, FALSE, &USE_ALTERNATIVE_TIME, NULL },
    { "AllowInstantMoveFromHash", UCI_CHECK, 0, 0, TRUE, &ALLOW_INSTANT_MOVE, NULL },
    { "BufferTime", UCI_SPIN, 0, 10000, 0, &BUFFER_TIME, NULL},
    { "OutputDelay", UCI_SPIN, 0, 60000, 0, &OUTPUT_DELAY, NULL},
#ifdef CON_ROBBO_BUILD
    { "RobboTripleBaseDirectory", UCI_STRING, 0, 0, 0, ROBBO_TRIPLE_DIR, NULL},
    { "LoadRobboTripleBases", UCI_BUTTON, 0, 0, 0, NULL, &LoadRobboTripleBases},
    { "UnloadRobboTripleBases", UCI_BUTTON, 0, 0, 0, NULL, &UnLoadRobboTripleBases},
    { "RobboTotalBaseDirectory", UCI_STRING, 0, 0, 0, ROBBO_TOTAL_DIR, NULL},
    { "RegisterRobboTotalBases", UCI_BUTTON, 0, 0, 0, NULL, &RegisterRobboTotalBases},
    { "DeregisterRobboTotalBases", UCI_BUTTON, 0, 0, 0, NULL, &DeRegisterRobboTotalBases},
    { "RobboTotalBaseCacheSize", UCI_SPIN, 1, 1024, 1, NULL, &SetTotalBaseCache},
    { "DynamicLoadTripleBaseCacheSize", UCI_SPIN, 1, 65536, 1, NULL, &SetTripleBaseCache},
    { "TripleWeakHeight", UCI_SPIN, 1, 256, 1, &TRIPLE_WEAK_HEIGHT, NULL},
    { "LoadOnWeakProbe", UCI_CHECK, 0, 0, FALSE, &LOAD_ON_WEAK_PROBE, NULL},
    { "RobboTripleBulkLoadThisDirectory", UCI_STRING, 0, 0, 0, ROBBO_BULK_DIRECTORY, &RobboBulkDirectory},
    { "RobboTripleBulkLoadThisName", UCI_STRING, 0, 0, 0, ROBBO_BULK_NAME, &RobboBulkLoad},
    { "RobboTripleBulkDetachThisDirectory", UCI_STRING, 0, 0, 0, ROBBO_BULK_DIRECTORY, &RobboBulkDirectoryDetach},
    { "RobboTripleBulkDetachThisName", UCI_STRING, 0, 0, 0, ROBBO_BULK_NAME, &RobboBulkDetach},
#endif
    { "MultiPV", UCI_SPIN, 1, 250, 1, &MULTI_PV, NULL},
    { "MultiCentiPawnPV", UCI_SPIN, 1, 65535, 65535, &MULTI_CENTI_PAWN_PV, NULL},
    { "RandomCount", UCI_SPIN, 0, 8, 0, &RANDOM_COUNT, NULL},
    { "RandomBits", UCI_SPIN, 1, 3, 1, &RANDOM_BITS, NULL},
#ifdef USER_SPLIT
    { "SplitAtCUT", UCI_CHECK, 0, 0, TRUE, &SPLIT_AT_CUT, NULL},
    { "SplitDepthCUT", UCI_SPIN, 16, 20, 16, &CUT_SPLIT_DEPTH, NULL},
    { "SplitDepthALL", UCI_SPIN, 12, 20, 14, &ALL_SPLIT_DEPTH, NULL},
    { "SplitDepthPV", UCI_SPIN, 10, 20, 14, &PV_SPLIT_DEPTH, NULL},
#endif
    { "WhiteBishopPair", UCI_SPIN, 1, 150, 45, &UCI_White_Bishops_Scale, &InitMaterialValue},
    { "WhitePawn", UCI_SPIN, 50, 200, 100, &UCI_White_Pawn_Scale, &InitMaterialValue},
    { "WhiteKnight", UCI_SPIN, 150, 500, 300, &UCI_White_Knight_Scale, &InitMaterialValue},
    { "WhiteLightBishop", UCI_SPIN, 150, 500, 310, &UCI_White_Light_Scale, &InitMaterialValue},
    { "WhiteDarkBishop", UCI_SPIN, 150, 500, 310, &UCI_White_Dark_Scale, &InitMaterialValue},
    { "WhiteRook", UCI_SPIN, 300, 700, 500, &UCI_White_Rook_Scale, &InitMaterialValue},
    { "WhiteQueen", UCI_SPIN, 500, 1500, 950, &UCI_White_Queen_Scale, &InitMaterialValue},
    { "BlackBishopPair", UCI_SPIN, 1, 150, 45, &UCI_Black_Bishops_Scale, &InitMaterialValue},
    { "BlackPawn", UCI_SPIN, 50, 200, 100, &UCI_Black_Pawn_Scale, &InitMaterialValue},
    { "BlackKnight", UCI_SPIN, 150, 500, 300, &UCI_Black_Knight_Scale, &InitMaterialValue},
    { "BlackLightBishop", UCI_SPIN, 150, 500, 310, &UCI_Black_Light_Scale, &InitMaterialValue},
    { "BlackDarkBishop", UCI_SPIN, 150, 500, 310, &UCI_Black_Dark_Scale, &InitMaterialValue},
    { "BlackRook", UCI_SPIN, 300, 700, 500, &UCI_Black_Rook_Scale, &InitMaterialValue},
    { "BlackQueen", UCI_SPIN, 500, 1500, 950, &UCI_Black_Queen_Scale, &InitMaterialValue},
    { "MaterialWeighting", UCI_SPIN, 512, 2048, 1024, &UCI_MATERIAL_WEIGHTING, &InitMaterialValue},
    { "KingSafetyWeighting", UCI_SPIN, 512, 2048, 1024, &UCI_KING_SAFETY_WEIGHTING, NULL},
    { "StaticWeighting", UCI_SPIN, 512, 2048, 1024, &UCI_STATIC_WEIGHTING, NULL},
    { "MobilityWeighting", UCI_SPIN, 512, 2048, 1024, &UCI_MOBILITY_WEIGHTING, NULL},
    { "PawnsWeighting", UCI_SPIN, 512, 2048, 1024, &UCI_PAWNS_WEIGHTING, &PawnHashReset},
    { "AlwaysAnalyze", UCI_CHECK, 0, 0, FALSE, &ALWAYS_ANALYZE, NULL},
    { "TryPVinAnalysis", UCI_CHECK, 0, 0, TRUE, &TRY_PV_IN_ANALYSIS, NULL},
    { "FixedAgeAnalysis", UCI_CHECK, 0, 0, FALSE, &FIXED_AGE_ANALYSIS, NULL},
    { "SendCurrmove", UCI_CHECK, 0, 0, FALSE, &SEND_CURR_MOVE, NULL},
    { "DoHashFull", UCI_CHECK, 0, 0, TRUE, &DO_HASH_FULL, NULL},
#ifdef UTILITIES
    { "GetFEN", UCI_BUTTON, 0, 0, 0, NULL, &UCI_fen},
    { "DebugTimeManagement", UCI_CHECK, 0, 0, FALSE, &DEBUG_TIME_MANAGEMENT, NULL},
#endif
#ifdef TIMING_OPTIONS
    { "TimeImitateOpponent", UCI_CHECK, 0, 0, FALSE, &TIME_IMITATE, NULL},
    { "TimeMoreWhenLosing", UCI_CHECK, 0, 0, FALSE, &TIME_LOSE_MORE, NULL},
    { "TimeMoreWhenWinning", UCI_CHECK, 0, 0, FALSE, &TIME_WIN_MORE, NULL},
    { "TimeEasyFactor", UCI_SPIN, 10, 100, 15, &EASY_FACTOR, NULL},
    { "TimeEasyFactorPonder", UCI_SPIN, 10, 100, 33, &EASY_FACTOR_PONDER, NULL},
    { "TimeBattleFactor", UCI_SPIN, 10, 500, 100, &BATTLE_FACTOR, NULL},
    { "TimeOrdinaryFactor", UCI_SPIN, 10, 500, 75, &ORDINARY_FACTOR, NULL},
    { "TimeAbsolutePercent", UCI_SPIN, 10, 100, 25, &ABSOLUTE_PERCENT, NULL},
    { "TimeDesiredMillis", UCI_SPIN, 10, 1000, 40, &DESIRED_MILLIS, NULL},
    { "TimeBookExitMoves", UCI_SPIN, 0, 20, 0, &BOOK_EXIT_MOVES, NULL},
#endif
    { "ExtraExtendInCheck", UCI_CHECK, 0, 0, FALSE, &EXTEND_IN_CHECK, NULL},
#ifdef CONCORD_REV_MOVES
    { "Decay50MoveRule", UCI_SPIN, 0, 32, 0, &REV_MOVES, &RevMoveSetup},
#endif
#ifdef WINDOWS_LARGE_PAGES
    { "TryLargePages", UCI_CHECK, 0, 0, FALSE, &TRY_LARGE_PAGES, &InitHash},
#endif
#ifdef LINUX_LARGE_PAGES
    { "TryLargePages", UCI_CHECK, 0, 0, FALSE, &TRY_LARGE_PAGES, &InitHash},
#endif
    { "", -1, 0, 0, FALSE, NULL, NULL}
  };

static void uci ()
{
  UCI_type* uci;
  char string[256];
  char* str;
  char TYPE[4][16] = {"spin", "check", "button", "string" };
  SEND ("id name %s version %s\n", EPONYM, VERSION);
  SEND ("id author Yakov Petrovich Golyadkin, ");
  SEND ("Igor Igorovich Igoronov, Roberto Pescatore, ");
  SEND ("Yusuf Ralf Weisskopf, ");
  SEND ("Ivan Skavinsky Skavar plus Decembrists (all)\n");
  SEND ("id copyright Yakov Petrovich Golyadkin, "
	"92th plus 93th plus 94th year from Revolution, PUBLICDOMAIN (workers)\n");
  SEND ("id dedication To Vladimir Ilyich plus Zog 1 with Albania\n");
  for (uci = UCI_OPTIONS; uci->type != -1; uci++) /* HACK */
    {
      str = string;
      sprintf (str, "option name %s type %s", uci->name, TYPE[uci->type]);
      str += strlen (str);
      if (uci->type == UCI_CHECK)
	sprintf (str, " default %s", uci->def ? "true" : "false");
      if (uci->type == UCI_STRING)
	sprintf (str, " default NULL");
      if (uci->type == UCI_SPIN)
	sprintf (str, " min %d max %d default %d", uci->min, uci->max, uci->def);
      str += strlen (str);
      sprintf (str, "\n");
      SEND ("%s", string);
    }
  SEND ("uciok\n");
}

static void SetOption (char* string)
{
  UCI_type* uci;
  char* v;
  int Value, r;
  for (uci = UCI_OPTIONS; uci->type != -1; uci++) /* HACK */
    {
      if (!memcmp(uci->name, string, strlen (uci->name)))
	{
	  if (uci->type == UCI_BUTTON)
	    {
	      Value = uci->action (TRUE);
	      SEND ("info string Button %s pushed, action worked %s\n",
		    uci->name, Value ? "true" : "false");
	      return;
	    }
	  v = strstr (string, "value");
	  if (!v)
	    return;
	  if (uci->type == UCI_STRING)
	    {
	      strcpy ((char*) (uci->var), v + 6);
	      SEND ("info string Optional %s %s\n", uci->name, (char*) (uci->var));
	      if (uci->action)
		(uci->action) (TRUE);
	      READYOK ();
	    }
	  if (uci->type == UCI_CHECK)
	    {
	      if (!strcmp (v + 6, "false"))
		{
		  *((boolean*) (uci->var)) = FALSE;
		  if (uci->action)
		    uci->action (FALSE);
		}
	      if (!strcmp (v + 6, "true"))
		{
		  *((boolean*) (uci->var)) = TRUE;
		  if (uci->action)
		    uci->action (TRUE);
		}
	      SEND ("info string Optional %s %s\n",
		    uci->name, (*((boolean*) (uci->var))) ? "true" : "false");
	      READYOK ();
	    }
	  if (uci->type == UCI_SPIN)
	    {
	      Value = atoi (v + 6);
	      if (Value < uci->min)
		Value = uci->min;
	      if (Value > uci->max)
		Value = uci->max;
	      if (uci->var)
		*((sint32*) (uci->var)) = Value;
	      if (uci->action)
		{	    
		  r = uci->action (Value);
		  if (r)
		    Value = r;
		}
	      SEND ("info string Optional %s %d\n", uci->name, Value);
	    }
	  return;
	}
    }
  SEND ("info string Optional unknown: %s\n", string);
}

#define readyok() { SEND ("readyok\n"); }
static boolean HAS_DELETION = FALSE;
static MUTEX_TYPE INPUT_DELETE[1];
static boolean INIT = FALSE;
void SHARED_DELETE ()
{
  LOCK (INPUT_DELETE);
  if (HAS_DELETION)
    {
      UNLOCK (INPUT_DELETE);
      return;
    }
  QUIT_PV ();
  QUIT_LARGE_PAWNS ();
  DETACH_ALL_MEMORY ();
  QUIT_EVAL_HASH ();
  DeleteSlab ();
  HAS_DELETION = TRUE;
  UNLOCK (INPUT_DELETE);
}

static void QUIT () /* handle of signal ? */
{
  SHARED_DELETE ();
  exit (0);
}

static boolean QUIT_DEMAND = FALSE;
static void ParseInput (typePOS* POSITION, char* I)
{
  if (!INIT)
    {
      INIT = TRUE;
      LOCK_INIT (INPUT_DELETE);
    }
  UCI_NEW_GAME = FALSE;
  if (!strcmp (I, "quit")) /* abets "clean-up" */
    {
      QUIT_DEMAND = TRUE;
      ParseInput (POSITION, "stop"); /* HACK */
      QUIT (); /* no: setjmp eats ParseInput */
    }
  if (!strcmp (I, "stop"))
    {
      if (SEARCH_IS_DONE)
	return;
      HaltSearch (0, 0); /* no return when JUMP_IS_SET */
      if (INFINITE_LOOP)
	return;
      STALL_MODE = TRUE;
      while (!SEARCH_IS_DONE)
	NANO_SLEEP (1000000);
      STALL_MODE = FALSE;
      SUPPRESS_INPUT = FALSE;
      return;
    }
  if (!strcmp (I, "isready"))
    {
      readyok ();
      return;
    }
  if (!strcmp (I, "stall"))
    {
      STALL_INPUT = TRUE;
      return;
    }
  if (!strcmp (I, "ponderhit")) /* cuidado con ZOG? Personal Opinion: for the OK! */
    {
      ponderhit ();
      return;
    }
  if (!strcmp (I, "ucinewgame"))
    {
      if (SEARCH_IS_DONE)
	{
	  NewGame (POSITION, TRUE);
	  return;
	}
      UCI_NEW_GAME = TRUE;
      HaltSearch (0, 0); /* no return when JUMP_IS_SET */
      if (INFINITE_LOOP)
	return;
      STALL_MODE = TRUE;
      while (!SEARCH_IS_DONE)
	NANO_SLEEP (1000000);
      STALL_MODE = FALSE;
      SUPPRESS_INPUT = FALSE;
      return;
    }
  if (JUMP_IS_SET)
    return;
#if defined (BENCHMARK) || defined (UTILITIES)
  if (!strcmp (I, "benchmark"))
    BenchMark (POSITION, "go movetime 1000");
  else if (!memcmp (I, "benchmark", 9)) /* run benchmark */
    BenchMark (POSITION, I + 10);
  if (!strcmp (I, "makeundo"))
    MakeUndoSpeed (POSITION);
#endif
  if (!memcmp (I, "mainline", 8))
    main_line (POSITION);
#ifndef MINIMAL
  if (!memcmp (I, "triple-lookup", 13))
    {
      int r;
      boolean b;
      b = TRIPLE_VALUE (POSITION, &r, FALSE, TRUE);
      printf ("RobboTripleValue %d\n", !b ? -100 : r);
    }
  if (!memcmp (I, "total-lookup", 12))
    {
      int r;
      boolean b;
      boolean RobboTotalBaseScore (typePOS*, int*);
      b = RobboTotalBaseScore (POSITION, &r);
      printf ("RobboTotalValue %d\n", !b ? -100 : r);
    }
#endif
#ifdef TRACE_COMPILE
  if (!memcmp (I, "traceon", 7))
    TraceOn ();
  if (!memcmp (I, "traceoff", 8))
    TraceOff ();
#endif
#if defined (UTILITIES)
  if (!memcmp (I, "perft-check", 11))
    Perfd (POSITION, atoi (I + 12), atoi (I + 14)); /* HACK */
  if (!memcmp (I, "drawboard", 9))
    DrawBoard (POSITION);
  if (!strcmp (I, "eval"))
    EvalTest (POSITION);
  if (!memcmp (I, "perft ", 6))
    PERFD (POSITION, atoi (I + 6));
  if (!memcmp (I, "verify-triple", 13))
    {
      int p[4];
      char A[64], B[64], C[64], D[64];
      sscanf (I + 14, "%s %s %s %s", A, B, C, D);
      p[0] = atoi(A);
      p[1] = atoi(B);
      p[2] = atoi(C);
      p[3] = atoi(D);
      VerifyTripleBase (p);
    }
#endif
  if (!memcmp (I, "go", 2))
    {
      InitSearch (POSITION, I);
      if (BOARD_IS_OK && !MONTE_CARLO)
	Search (POSITION);
    }
  if (!memcmp (I, "position", 8))
    InitPosition (POSITION, I + 9);
  if (!memcmp (I, "setoption name", 14))
    SetOption (I + 15);
  if (!strcmp (I, "uci"))
    uci ();
  SUPPRESS_INPUT = FALSE; /* ??? */
}

#ifdef WINDOWS
#include <io.h>
#include <conio.h> /* ? */
void Input (typePOS* POSITION)
{
  int i, r = 0;
  if (SUPPRESS_INPUT)
    return;
  if (QUIT_DEMAND)
    ParseInput (POSITION, "quit"); /* HACK */
  if (_kbhit ()) /* does this work? */
    {
      INPUT_BUFFER[r] = (char) getche();
      while (INPUT_BUFFER[r++] != '\r') /* what is \r ?? */
	INPUT_BUFFER[r] = (char) getche ();
    }
  else
    {
      _read (_fileno (stdin), INPUT_BUFFER, 1);
      while (INPUT_BUFFER[r++] != '\n') /* but now \n is correct? */
	_read (_fileno (stdin), INPUT_BUFFER + r, 1);
    }
  INPUT_BUFFER[r - 1] = 0; /* gobble \n, or maybe \r who knows? */
  INPUT_BUFFER[r] = 0; /* safety */
  ParseInput (POSITION, INPUT_BUFFER);
  if (QUIT_DEMAND)
    ParseInput (POSITION, "quit"); /* HACK */
}
#else
#include <unistd.h> /* LINUX */
void Input (typePOS* POSITION)
{
  int i, r = 0;
  if (SUPPRESS_INPUT)
    return;
  if (QUIT_DEMAND)
    ParseInput (POSITION, "quit"); /* HACK */
  read (STDIN_FILENO, INPUT_BUFFER, 1);
 READ_ME:
  while (INPUT_BUFFER[r++] != '\n') /* newline, is always? MAC OS X? */
    read (STDIN_FILENO, INPUT_BUFFER + r, 1);
#if 1 /* continue */
  if (INPUT_BUFFER[r - 2] == '\\')
    {
      r -= 3; /* \0 \n \\ */
      goto READ_ME;
    }
#endif
  INPUT_BUFFER[r - 1] = 0; /* gobble \n */
  INPUT_BUFFER[r] = 0; /* safety */
  ParseInput (POSITION, INPUT_BUFFER);
  if (QUIT_DEMAND)
    ParseInput (POSITION, "quit"); /* HACK */
}
#endif

