
#include "RobboLito.h"
#include "RobboBaseLibUsage.h"

void MakeTotalBlockIndex (char *); /* extra */
void MakeTripleBlockIndex (char *);
void main_line (typePOS *); /* Robbo_glue.c */

static boolean VERIFY = FALSE; /* DeBug */

static void do_triple_directory (char * INPUT)
{
  FUNC_CALL (LoadTripleBases) (INPUT);
  SEND ("Fact: TripleDirectory %s loaded\n", INPUT);
}

static void do_total_directory (char * INPUT)
{
  FUNC_CALL (RegisterTotalBases) (INPUT);
  SEND ("Fact: TotalDirectory %s loaded\n", INPUT);
}

static void do_set_position (typePOS * POS, char * INPUT)
{
  InitPosition (POS, INPUT);
  if (!BOARD_IS_OK)
    SEND ("Remand: Board buys not the conforming\n");
  else
    SEND ("Fact: Position set %s\n", INPUT);
}

#define dROTT 0x00
#define dVITT 0x01
#define dPATT 0x02
#define dPERD0 (dPATT + 1)
#define DISCO_PATT_PERD(x) ((x) >= dPATT)
#define DISCO_VITTORIA(x) ((x) == dVITT)
#define DISCO_PATTA(x) ((x) == dPATT)
#define DISCO_PERDITA(x) ((x) >= dPERD0)
#define wEnumB wEnumBL
#define bEnumB bEnumBL
#define BlockedPawn 8

static char RIMAP[16] =
  { 0, wEnumP, wEnumN, wEnumK, wEnumB, wEnumB, wEnumR, wEnumQ,
    8, bEnumP, bEnumN, bEnumK, bEnumB, bEnumB, bEnumR, bEnumQ };

boolean Squeeze (typePOS * POSITION, Type_PiSq * PiSq, boolean w)
{
  uint64 T, U, V, W, BLOCKED, A;
  int i, n = 0, c, sq;
  PiSq->wtm = POSITION->wtm;
  PiSq->wK = POSITION->wKsq;
  PiSq->bK = POSITION->bKsq;
  PiSq->pedone = (wBitboardP || bBitboardP);
  U = wBitboardP & (bBitboardP >> 8);
  V = (U << 1) & U & ~FILEa;
  W = (V << 1) & V;
  BLOCKED = (U & ~V) | (W & ~((W << 1) & W));
  PiSq->ep = POSITION->DYN->ep;
  for (i = 0; i < 4; i++)
    PiSq->pi[i] = PiSq->sq[i] = 0;
  T = POSITION->OccupiedBW ^ (wBitboardK | bBitboardK);
  T ^= BLOCKED | (BLOCKED << 8);  
  while (T)
    {
      sq = BSF (T);
      PiSq->pi[n] = RIMAP[POSITION->sq[sq]];
      PiSq->sq[n++] = sq;
      BitClear (sq, T);
    }
  c = n;
  A = BLOCKED;
  while (A)
    {
      sq = BSF (A);
      PiSq->pi[n] = BlockedPawn;
      PiSq->sq[n++] = sq;
      BitClear (sq, A);
    }
  PiSq->n = n;
  if (w ? FUNC_CALL (HAS_TotalBase) (PiSq) : FUNC_CALL (HAS_TripleBase) (PiSq))
    return TRUE;
  while (BLOCKED && n < 4)
    {
      sq = BSF (BLOCKED);
      PiSq->pi[c] = wEnumP;
      PiSq->sq[c++] = sq; /* redundancy */
      PiSq->pi[n] = bEnumP;
      PiSq->sq[n++] = sq + 8;
      PiSq->n = n;
      if (w ? FUNC_CALL (HAS_TotalBase) (PiSq) : FUNC_CALL (HAS_TripleBase) (PiSq))
	return TRUE;
    }
  return FALSE;
}

static boolean RobboTripleBaseScoreWrapper
(typePOS * POSITION, int * va, int x, int y)
{
  Type_PiSq PiSq[1];
  if (!Squeeze (POSITION, PiSq, 0))
    return FALSE;
  if (!FUNC_CALL (RobboTripleLibScore) (PiSq, va, 1))
    return FALSE;
  return TRUE;
}

static boolean RobboTotalBaseScoreWrapper (typePOS * POSITION, int * va)
{
  int va2;
  uint8 v8;
  Type_PiSq PiSq[1];
  if (!Squeeze (POSITION, PiSq, 1))
    return FALSE;
  if (!FUNC_CALL (RobboTotalLibScore) (PiSq, &v8, 1))  
    return FALSE;
  *va = v8;
  if (!VERIFY || !(FUNC_CALL (RobboTripleLibScore) (PiSq, &va2, 1)))
    return TRUE;
  if (DISCO_PERDITA (*va) && va2 != -1) /* VERIFY */
    SEND ("Error: Total %d (loss) Triple %d. Demand for report!\n", *va, va2);
  if (DISCO_PATTA (*va) && va2 != 0)
    SEND ("Error: Total %d (draw) Triple %d. Demand for report!\n", *va, va2);
  if (DISCO_VITTORIA (*va) && va2 != 1)
    SEND ("Error: Total %d (win) Triple %d. Demand for report!\n", *va, va2);
  return TRUE;
}

static boolean do_total_result (typePOS * POSITION)
{
  int va;
  if (!ROBBO_TOTAL_CONDITION (POSITION) ||
      !RobboTotalBaseScoreWrapper (POSITION, &va))
    return FALSE;
  SEND ("TotalResult is %d\n", va);
  return TRUE;
}

static boolean do_triple_result (typePOS * POSITION)
{
  int va;
  if (!ROBBO_TRIPLE_CONDITION (POSITION) || /* Value: TRIPLE_MAX_USAGE */
      !RobboTripleBaseScoreWrapper (POSITION, &va, 0, 0))
    return FALSE;
  SEND ("TripleResult is %d\n", va);
  return TRUE;
}

static boolean do_result (typePOS * POS)
{
  if (do_total_result (POS))
    return TRUE;
  if (do_triple_result (POS))
    return TRUE;
  SEND ("Result is not possible\n");
  return FALSE;
}

#define IS_ILLEGAL \
  ( !POSITION->wtm ? \
    (wBitboardK & POSITION->DYN->bAtt) :   (bBitboardK & POSITION->DYN->wAtt) )

static void ExpandFullWin (typePOS* POSITION, char* MOVE_STRING)
{
  typeMoveList LIST[256];
  typeMoveList* list;
  typeMoveList* p;
  int best = 0xff, va;
  list = CaptureMoves (POSITION, LIST, POSITION->OccupiedBW);
  list = OrdinaryMoves (POSITION, list);
  for (p = LIST; p < list; p++)
    {
      Make (POSITION, p->move);
      Mobility (POSITION);
      if (IS_ILLEGAL)
	{
	  Undo (POSITION, p->move);
	  continue;
	}
      if (!RobboTotalBaseScoreWrapper (POSITION, &va))
	{
	  Undo (POSITION, p->move);
	  SEND ("move %s TotalResult %d nototal\n", MOVE_STRING, 1);
	  return;
	}
      if (DISCO_PERDITA (va) &&
	  (POSITION->DYN->cp || MoveIsProm (p->move) || MoveIsEP (p->move)))
	va = 3; /* HACK */
      if (DISCO_PERDITA (va) && va < best)
	best = va;
      Undo (POSITION, p->move);
    }
  if (best == 0xff)
    SEND ("move %s TotalResult %d best0\n", MOVE_STRING, 1);
  else
    SEND ("move %s TotalResult %d\n", MOVE_STRING, 0x1000 + 1 + (best - 3));
}

static void do_move_results (typePOS * POSITION)
{
  typeMoveList LIST[256];
  typeMoveList* list;
  typeMoveList* p;
  int va;
  char STRING[16];
  if (!do_result (POSITION))
    return;
  list = CaptureMoves (POSITION, LIST, POSITION->OccupiedBW);
  list = OrdinaryMoves (POSITION, list);
  for (p = LIST; p < list; p++)
    {
      Make (POSITION, p->move);
      Mobility (POSITION);
      if (IS_ILLEGAL)
	{
	  Undo (POSITION, p->move);
	  continue;
	}
      if (RobboTotalBaseScoreWrapper (POSITION, &va))
	{
	  if (DISCO_PERDITA (va) &&
	      (POSITION->DYN->cp || MoveIsProm (p->move) || MoveIsEP (p->move)))
	    SEND ("move %s TotalResult %d\n", Notate (p->move, STRING), 3);
	  else if (DISCO_VITTORIA (va) &&
	      (POSITION->DYN->cp || MoveIsProm (p->move) || MoveIsEP (p->move)))
	    SEND ("move %s TotalResult %d\n", Notate (p->move, STRING), 0x1000);
	  else if (!DISCO_VITTORIA (va))
	    SEND ("move %s TotalResult %d\n", Notate (p->move, STRING), va);
	  else
	    ExpandFullWin (POSITION, Notate (p->move, STRING));
	}
      else if (RobboTripleBaseScoreWrapper (POSITION, &va, FALSE, TRUE))
	SEND ("move %s TripleResult %d\n", Notate (p->move, STRING), va);
      else
	SEND ("move %s NoResultKnown -100\n", Notate (p->move, STRING));
      Undo (POSITION, p->move);
    }
  SEND ("Complete with moves\n");
}

static void inform ()
{
  int r[12] = { 0, 7, 6, 4, 2, 1, 8, 15, 14, 12, 10, 9 };
  int a, b, c, d;
  char BUFFER[0x1000];
  Type_PiSq PiSq[1];
  for (d = 0; d < 12; d++)
    for (c = d; c < 12; c++)
      for (b = c; b < 12; b++)
	for (a = b; a < 12; a++)
	  {
	    sprintf (BUFFER, "%c%c%c%c ", /* RobboExplorer type // HACK */
		     'a' + r[d], 'a' + r[c], 'a' + r[b], 'a' + r[a]);
	    PiSq->pi[0] = a;
	    PiSq->pi[1] = b;
	    PiSq->pi[2] = c;
	    PiSq->pi[3] = d;
	    FUNC_CALL (TotalBase_FileNames) (PiSq, BUFFER);
	    FUNC_CALL (TripleBase_FileNames) (PiSq, BUFFER);
	    if (strlen (BUFFER) == 5)
	      continue;
	    strcat (BUFFER, "\n");
	    SEND (BUFFER);
	  }
  SEND ("Informatory complete\n");
}

static void files_info (typePOS * POSITION)
{
  Type_PiSq PiSq[1];
  char BUFFER[0x10000];
  BUFFER[0] = 0;
  if (Squeeze (POSITION, PiSq, 1))
    FUNC_CALL (FilesInfo_TotalBase) (PiSq, BUFFER);
  SEND (BUFFER);
  BUFFER[0] = 0;
  if (Squeeze (POSITION, PiSq, 0))
    FUNC_CALL (FilesInfo_TripleBase) (PiSq, BUFFER);
  SEND (BUFFER);
  SEND ("FilesInfo has completion\n");
}

#ifndef STATIC_LINKERY
static boolean DYNAMIC_LIBRARY_LOADED = FALSE;
#include <dlfcn.h> /* dynamic load */
#define GAIN(x) dlerror (); /* clear */ \
  x = dlsym (so_file, #x); /* load */ \
  err = dlerror (); /* again */ \
  if (err) { printf ("%s\n", err); return FALSE; } 

boolean LoadDynamicLibrary (char * STRING)
{
  void * so_file;
  char * err;

  DYNAMIC_LIBRARY_LOADED = FALSE;
  so_file = dlopen (STRING, RTLD_LAZY); /* local */
  if (!so_file)
    {
      SEND ("%s not detected\n", STRING);
      return FALSE;
    }
  SEND ("%s found (file name), to do dynamic load functions\n", STRING);
  GAIN (RobboTotalLibScore);
  GAIN (RobboTripleLibScore);
  GAIN (GetRobboBaseLibraryVersion);

  GAIN (RegisterTotalBases);
  GAIN (DeRegisterTotalBases);
  GAIN (RegisterRobboTotalBasesInDirectory);
  GAIN (SetCacheTotalBase);
  GAIN (HAS_TotalBase);

  GAIN (SetCacheTripleBase);
  GAIN (SetLoadOnWeakProbeBackGround);
  GAIN (AllowBackGroundLoader);
  GAIN (PauseBackGroundLoader);

  GAIN (LoadTripleBases);
  GAIN (UnLoadTripleBases);
  GAIN (LoadRobboTripleBasesInDirectory);
  GAIN (HAS_TripleBase);
  
  GAIN (RobboLibBulkLoadFile);
  GAIN (RobboLibBulkDetachFile);
  GAIN (RobboLibBulkLoadDirectory);
  GAIN (RobboLibBulkDetachDirectory);

  GAIN (RobboBaseLibStartUp);
  GAIN (RobboBaseLibExit);

  GAIN (TotalBase_FileNames);
  GAIN (TripleBase_FileNames);
  GAIN (FilesInfo_TotalBase);
  GAIN (FilesInfo_TripleBase);
  DYNAMIC_LIBRARY_LOADED = TRUE;
  return TRUE;
}

void TryLoadDynamicLibrary ()
{
  boolean b;
  b = LoadDynamicLibrary ("./RobboBaseLib.so");
  if (!b)
    LoadDynamicLibrary ("../RobboBaseLib.so"); /* HACK */
}
#endif

static void attend_loop ()
{
  char INPUT[1024];
  typePOS POSITION[1];
  POSITION->DYN_ROOT = malloc (MAXIMUM_PLY * sizeof (typeDYNAMIC));
  POSITION->DYN = POSITION->DYN_ROOT + 1;
  while (TRUE)
    {
      fgets (INPUT, 256, stdin);
      if (INPUT[strlen (INPUT) - 1] == '\n')
	INPUT[strlen (INPUT) - 1] = 0;
      if (!strcmp (INPUT, "quit"))
	return;
      if (!strcmp (INPUT, "setup Robbo check"))
	SEND ("check for Robbo\n");
#ifndef STATIC_ILNKERY
      if (!memcmp (INPUT, "RobboBaseLibLocation", 20))
	LoadDynamicLibrary (INPUT + 21);
      if (!DYNAMIC_LIBRARY_LOADED)
	{
	  SEND ("Must load Dynamic Library\n");
	  return;
	}
#endif
      if (!memcmp (INPUT, "TripleDirectory ", 16))
	do_triple_directory (INPUT + 16);
      if (!memcmp (INPUT, "TotalDirectory ", 15))
	do_total_directory (INPUT + 15);
      if (!memcmp (INPUT, "fen ", 4))
	do_set_position (POSITION, INPUT);
      if (!strcmp (INPUT, "verify"))
	VERIFY = TRUE;
      if (!strcmp (INPUT, "result"))
	do_result (POSITION);
      if (!strcmp (INPUT, "files-info"))
	files_info (POSITION);
      if (!strcmp (INPUT, "move-results"))
	do_move_results (POSITION);
      if (!strcmp (INPUT, "inform"))
	inform ();
      if (!strcmp (INPUT, "mainline"))
	main_line (POSITION);
      if (!memcmp (INPUT, "TotalBlockIndex", 15))
	MakeTotalBlockIndex (INPUT + 16);
      if (!memcmp (INPUT, "TripleBlockIndex", 16))
	MakeTripleBlockIndex (INPUT + 17);
    }
}

#if 0
void loop () /* util */
{
  int r[12] = { 0, 7, 6, 4, 2, 1, 8, 15, 14, 12, 10, 9 };
  int a, b, c, d;
  char BUFFER[0x1000];
  Type_PiSq PiSq[1];
  for (d = 0; d < 12; d++)
    for (c = d; c < 12; c++)
      for (b = c; b < 12; b++)
	for (a = b; a < 12; a++)
	  {
	    PiSq->pi[0] = r[a];
	    PiSq->pi[1] = r[b];
	    PiSq->pi[2] = r[c];
	    PiSq->pi[3] = r[d];
	    BUFFER[0] = 0x0;
	    FUNC_CALL (FilesInfo_TotalBase) (PiSq, BUFFER);
	    FUNC_CALL (FilesInfo_TripleBase) (PiSq, BUFFER);
	    SEND (BUFFER);	    
	  }
}
#endif

/* Copyright (c) Roberto Pescatore plus Decembrists (all), PUBLICDOMAIN (workers) */

int main (int argc, char ** argv)
{
  char STRING[0x40];
  InitArrays ();
  TRIPLE_MAX_USAGE = 6;
#ifdef STATIC_LINKERY
  SEND ("StartUp STATIC_LINK RobboExplorer\n");
#else
  SEND ("StartUp DynamicLink RobboExplorer\n");
  TryLoadDynamicLibrary ();
#endif
  FUNC_CALL (RobboBaseLibStartUp) (FALSE);
  FUNC_CALL (SetCacheTotalBase) (64);
  FUNC_CALL (SetCacheTripleBase) (64);
  FUNC_CALL (GetRobboBaseLibraryVersion) (STRING);
  SEND ("ready in RobboExplorer, version %s, Library %s\n", VERSION, STRING);
  attend_loop ();
  return 0;
}
