
#include "RobboLito.h"
#include "RobboTotalBase.h"
#include "Robbo_gen_mossa.h"

#include <stdarg.h>
void SEND (char* fmt, ...)
{
  va_list Value;
  va_start (Value, fmt);
  vfprintf (stdout, fmt, Value);
  va_end (Value);
  fflush (stdout);
}

void DrawBoard (typePOS* POSITION)
{
  char C[16] = "0PNKBBRQ";
  char ARR[256];
  int t, c, u;
  printf ("      +---+---+---+---+---+---+---+---+\n");
  for (t = R8; t >= R1; t--)
    {
      printf("   %i  |", t+1);
      for (c = FA; c <= FH; c++)
	{
	  u = POSITION->sq[c + 8 * t];
	  if (u >= 8)
	    printf ("<%c>", C[u - 8]);
	  else if (u > 0)
	    printf (" %c ", C[u]);
	  else if ((c + t) & 1)
	    printf ("   ");
	  else
	    printf (" . ");
	  printf ("|");
	}
      printf ("\n      +---+---+---+---+---+---+---+---+\n");
    }
  printf ("        a   b   c   d   e   f   g   h\n");
  printf ("%s\n", ARR);
}

static void BITBOARD (typePOS *POSITION, int wK, int bK, type_PiSq * PiSq)
{
  uint8 *Squ = PiSq->sq;
  int i;
  POSITION->wKsq = wK;
  POSITION->bKsq = bK;
  wBitboardK = SqSet[wK];
  bBitboardK = SqSet[bK];
  wBitboardR = wBitboardQ = wBitboardN =
    wBitboardBL = wBitboardBD = wBitboardP = 0;
  bBitboardR = bBitboardQ = bBitboardN =
    bBitboardBL = bBitboardBD = bBitboardP = 0;
  PiSq->pedone = FALSE;
  for (i = 0; i < 4; i++)
    {
      if (PiSq->pi[i] == wEnumB && SqSet[Squ[i]] & DARK)
	{
	  wBitboardBD |= SqSet[Squ[i]];
	  continue;
	}
      if (PiSq->pi[i] == bEnumB && SqSet[Squ[i]] & DARK)
	{
	  bBitboardBD |= SqSet[Squ[i]];
	  continue;
	}
      if (PiSq->pi[i] == BlockedPawn)
	{
	  wBitboardP |= SqSet[Squ[i]];
	  bBitboardP |= SqSet[Squ[i] + 8];
	  PiSq->pedone = TRUE;
	  continue;
	}
      if (PiSq->pi[i] == bEnumP || PiSq->pi[i] == wEnumP)
	PiSq->pedone = TRUE;
      POSITION->bitboard[PiSq->pi[i]] |= SqSet[Squ[i]];
    }
}

#define Bitboard2(x,y) ( ( (uint64) 1) << (x) ) | ( ( (uint64) 1) << (y) )

static const uint64 EPtable[8] =
  { Bitboard2 (B4, B4), Bitboard2 (A4, C4),
    Bitboard2 (B4, D4), Bitboard2 (C4, E4),
    Bitboard2 (D4, F4), Bitboard2 (E4, G4),
    Bitboard2 (F4, H4), Bitboard2 (G4, G4) };

static void SetDraw (RobboTotalBase * TotalBase,
		     uint64 PositionNumber, uint64 d, uint8* DATA)
{
  DATA[PositionNumber] = VALUE_DRAW;
  if (d != -1)
    DATA[d] = VALUE_DRAW;
}

static void ROTTO (RobboTotalBase * TotalBase, uint64 PositionNumber, uint64 d,
		   uint8* DATA, uint8* MAR, uint8* VIT)
{
  DATA[PositionNumber] = VALU_ROTTO;
  UnSet (MAR, PositionNumber);
  Set (VIT, PositionNumber);
  if (d == -1)
    return;
  DATA[d] = VALU_ROTTO;
  UnSet (MAR, d);
  Set (VIT, d);
}

#define MARCO(ptr, A, B) { Set (ptr, A); if (B != -1) Set (ptr, B); }

static void inizRIMA (RobboTotalBase *TotalBase, uint64 PositionNumber,
		      uint64 d, int RIMA, uint8* DATA, uint8* PER)
{
  if (RIMA > MASSIMO_RIMA)
    {
      printf ("MASSIMO RIMA\n");
      exit (1);
    }
  if (RIMA == 0)
    RIMA = PRIMERA_PERDITA;
  DATA[PositionNumber] = RIMA;
  if (RIMA == PRIMERA_PERDITA)
    {
      Set (PER, PositionNumber);
      if (d != -1)
	Set (PER, d);
    }
  if (d != -1)
    DATA[d] = RIMA;
}

#define UnderProm(x) \
  { Undo (POSITION, move); move = (move & 07777) + ( (x) << 12 ); \
    MakeWhite (POSITION, move); RobboTotalBaseScore (POSITION, &Value); \
    if (DISCO_PERDITA (Value)) \
      { MARCO (MAR, PositionNumber, d); \
        MARCO (VIT, PositionNumber, d); \
	UndoWhite (POSITION, move); return; } \
    if (DISCO_PATTA (Value)) \
      { SetDraw (TotalBase, PositionNumber, d, DATA); RIMA++; } }

static void SetupPosition
(typePOS *POSITION, RobboTotalBase *TotalBase, type_PiSq *PiSq,
 uint8* DATA, uint8* MAR, uint8* VIT, uint8* PER)
{
  typeMoveList LIST[256], *list;
  int move, i, Value, RIMA, rf;
  uint64 PositionNumber, d, ep;
  boolean NON = TRUE;
  uint64 A;
  int b;
  uint32 m3;
  int vc;

  ott_indice (PiSq, &PositionNumber, &d, &rf);
  if (IS_ILLEGAL)
    {
      ROTTO (TotalBase, PositionNumber, d, DATA, MAR, VIT);
      return;
    }
  list = WhiteCaptures (POSITION, LIST, bBitboardOcc);

  RIMA = 0;
  for (i = 0; i < list - LIST; i++)
    {
      move = LIST[i].move;
      if (((move & 070000) >> 12) == 4)
	continue;
      MakeWhite (POSITION, move);
      Mobility (POSITION);
      if (IS_ILLEGAL)
	{
	  UndoWhite (POSITION, move);
	  continue;
	}
      if (!RobboTotalBaseScore (POSITION, &Value))
	{
	  printf ("SetupPosition %d (RTBScore)\n",Value);
	  DrawBoard (POSITION);
	  exit (1);
	}
      NON = FALSE;
      if (DISCO_PERDITA (Value))
	{
	  MARCO (MAR, PositionNumber, d);
	  MARCO (VIT, PositionNumber, d);
	  UndoWhite (POSITION, move);
	  return;
	}
      if (DISCO_PATTA (Value))
	{
	  SetDraw (TotalBase, PositionNumber, d, DATA);
	  RIMA++;
	}
      if (((move & 070000) >> 12) == 7)
	{
	  UnderProm (6);
	  UnderProm (5);
	  UnderProm (4);
	}
      UndoWhite (POSITION, move);
    }
  RIMA += CountWhite (POSITION, &ep);
  if (ep == -1)
    {
      ROTTO (TotalBase, PositionNumber, d, DATA, MAR, VIT);
      return;
    }
  while (ep)
    {
      b = BSF (ep);
      BitClear (b, ep);
      move = ((b - 16) << 6) | b;
      MakeWhite (POSITION, move);
      A = EPtable[FILE (POSITION->DYN->ep)] & bBitboardP;
      while (A)
	{
	  b = BSF (A);
	  BitClear (b, A);
	  m3 = FlagEP | (b << 6) | (POSITION->DYN->ep);
	  Make (POSITION, m3);
	  RobboTotalBaseScore (POSITION, &vc);
	  Undo (POSITION, m3);
	  if (DISCO_PERDITA (vc))
	    {
	      RIMA--;
	      break;
	    }
	}
      UndoWhite (POSITION, move);
    }
  if (NON && !RIMA && !IN_CHECK)
    SetDraw (TotalBase, PositionNumber, d, DATA);
  else
    inizRIMA (TotalBase, PositionNumber, d, RIMA, DATA, PER);
  return;
}

static int S[4], T[4];
static RobboTotalBase* TOT;

void RE_FETTA (int rf)
{
  uint64 i, ci, d;
  int j, sq, s[4], wK, bK, king, re_FETTA, f1, f2;
  uint8* pi;
  RobboTotalBase* Tot;
  typePOS POSITION[1];
  uint8* DATA;
  uint8* MAR;
  uint8* VIT;
  uint8* PER;

  Tot = TOT;
  king = Tot->PEDONE ? inverso_pedone_re[rf] : inverso_normale_re[rf];
  POSITION->DYN_ROOT = malloc(MAXIMUM_PLY * sizeof(typeDYNAMIC));
  POSITION->DYN = POSITION->DYN_ROOT + 1;
  for (sq = A1; sq <= H8; sq++)
    POSITION->sq[sq] = 0;
  POSITION->DYN->reversible = POSITION->DYN->oo = POSITION->DYN->ep = 0;
  pi = Tot->p;
  wK = king >> 6;
  bK = king & 63;
  DATA = Tot->data + (rf * Tot->PawnMult);
  MAR = Tot->MAR + (rf * Tot->PawnMult8);
  VIT = Tot->VIT + (rf * Tot->PawnMult8);
  PER = Tot->PER + (rf * Tot->PawnMult8);
  for (i = 0; i < Tot->PawnMult; i++)
    DATA[i] = VALU_INCOG;
  for (s[0] = S[0]; s[0] <= T[0]; s[0]++)
    for (s[1] = S[1]; s[1] <= T[1]; s[1]++)
      for (s[2] = S[2]; s[2] <= T[2]; s[2]++)
	for (s[3] = S[3]; s[3] <= T[3]; s[3]++)
	  {
	    type_PiSq PiSq[1];
	    if (Tot->efgh2 != -1)
	      {
		f1 = Tot->efgh_file[Tot->efgh1];
		f2 = FILE (s[Tot->efgh1]);
		f2 = MIN (f2, FH - f2) & 1;
		if (f1 != f2)
		  continue;
		f1 = Tot->efgh_file[Tot->efgh2];
		f2 = FILE (s[Tot->efgh2]);
		f2 = MIN (f2, FH - f2) & 1;
		if (f1 != f2)
		  continue;		
	      }
	    else if (Tot->efgh1 != -1)
	      {
		f1 = Tot->efgh_file[Tot->efgh1];
		f2 = FILE (s[Tot->efgh1]);
		f2 = MIN (f2, FH - f2);
		if (f1 != f2)
		  continue;
	      }
	    if (pi[0] == pi[1] && s[0] <= s[1] && pi[0])
	      continue;
	    if (pi[1] == pi[2] && s[1] <= s[2] && pi[1])
	      continue;
	    if (pi[2] == pi[3] && s[2] <= s[3] && pi[2])
	      continue;
	    for (j = 0; j < 4; j++)
	      PiSq->sq[j] = s[j];
	    for (j = 0; j < 4; j++)
	      PiSq->pi[j] = pi[j];
	    PiSq->wK = wK;
	    PiSq->bK = bK;
	    PiSq->wtm = TRUE;
	    if (wK == s[0] || wK == s[1] || bK == s[0] || bK == s[1]
		|| wK == s[2] || bK == s[2] || wK == s[3] || bK == s[3]
		|| (pi[1] && s[0] == s[1]) || (pi[2] && s[0] == s[2])
		|| (pi[2] && s[1] == s[2]) || (pi[3] && s[0] == s[3])
		|| (pi[3] && s[1] == s[3]) || (pi[3] && s[2] == s[3])
		|| ((AttK[wK] | SqSet[wK]) & SqSet[bK]) ||
		(pi[0] == BlockedPawn &&
		 (wK == s[0] + 8 || bK == s[0] + 8
		  || (pi[1] && s[0] + 8 == s[1]) || (pi[2] && s[0] + 8 == s[2])
		  || (pi[3] && s[0] + 8 == s[3]))) ||
		(pi[1] == BlockedPawn &&
		 (wK == s[1] + 8 || bK == s[1] + 8
		  || (pi[0] && s[1] + 8 == s[0]) || (pi[2] && s[1] + 8 == s[2])
		  || (pi[3] && s[1] + 8 == s[3]))) ||
		(pi[2] == BlockedPawn &&
		 (wK == s[2] + 8 || bK == s[2] + 8
		  || (pi[0] && s[2] + 8 == s[0]) || (pi[1] && s[2] + 8 == s[1])
		  || (pi[3] && s[2] + 8 == s[3]))) ||
		(pi[3] == BlockedPawn &&
		 (wK == s[3] + 8 || bK == s[3] + 8
		  || (pi[0] && s[3] + 8 == s[0]) || (pi[1] && s[3] + 8 == s[1])
		  || (pi[2] && s[3] + 8 == s[2]))) ||
		(pi[0] == BlockedPawn && pi[1] == BlockedPawn
		 && FILE (s[1]) != FH && s[0] == s[1] + 1) ||
		(pi[1] == BlockedPawn && pi[2] == BlockedPawn
		 && FILE (s[2]) != FH && s[1] == s[2] + 1) ||
		(pi[2] == BlockedPawn && pi[3] == BlockedPawn
		 && FILE (s[3]) != FH && s[2] == s[3] + 1))		    
	      {
		BITBOARD (POSITION, wK, bK, PiSq);
		POSITION->wtm = TRUE;
		PiSq->wtm = TRUE;
		ott_indice (PiSq, &ci, &d, &re_FETTA);
		ROTTO (Tot, ci, d, DATA, MAR, VIT);
		goto AGAIN;
	      }
	    for (j = 0; j < 4; j++)
	      if (pi[j])
		POSITION->sq[s[j]] = pi[j];
	    for (j = 0; j < 4; j++)
	      if (pi[j] == wEnumB && SqSet[s[j]] & DARK)
		POSITION->sq[s[j]] = wEnumBD;
	    for (j = 0; j < 4; j++)
	      if (pi[j] == bEnumB && SqSet[s[j]] & DARK)
		POSITION->sq[s[j]] = bEnumBD;
	    for (j = 0; j < 4; j++)
	      if (pi[j] == BlockedPawn)
		{
		  POSITION->sq[s[j]] = wEnumP;
		  POSITION->sq[s[j] + 8] = bEnumP;
		}
	    POSITION->sq[wK] = wEnumK;
	    POSITION->sq[bK] = bEnumK;
	    POSITION->wtm = TRUE;
	    BITBOARD (POSITION, wK, bK, PiSq);
	    PiSq->wtm = TRUE;
	    wBitboardOcc = wBitboardK | wBitboardQ | wBitboardR |
	                   wBitboardB | wBitboardN | wBitboardP;
	    bBitboardOcc = bBitboardK | bBitboardQ | bBitboardR |
	                   bBitboardB | bBitboardN | bBitboardP;
	    POSITION->OccupiedBW = wBitboardOcc | bBitboardOcc;
#ifndef MAGIC_BITBOARDS
	    POSITION->OccupiedL90 = SqSet[Left90[wK]] | SqSet[Left90[bK]];
	    POSITION->OccupiedL45 = SqSet[Left45[wK]] | SqSet[Left45[bK]];
	    POSITION->OccupiedR45 = SqSet[Right45[wK]] | SqSet[Right45[bK]];
	    for (j = 0; j < 4; j++)
	      if (pi[j])
		{
		  POSITION->OccupiedL90 |= SqSet[Left90[s[j]]];
		  POSITION->OccupiedL45 |= SqSet[Left45[s[j]]];
		  POSITION->OccupiedR45 |= SqSet[Right45[s[j]]];
		  if (pi[j] == BlockedPawn)
		    {
		      POSITION->OccupiedL90 |= SqSet[Left90[s[j] + 8]];
		      POSITION->OccupiedL45 |= SqSet[Left45[s[j] + 8]];
		      POSITION->OccupiedR45 |= SqSet[Right45[s[j] + 8]];
		    }
		}
#endif
	    Mobility (POSITION);
	    SetupPosition (POSITION, Tot, PiSq, DATA, MAR, VIT, PER);
	  AGAIN:
	    POSITION->sq[wK] = POSITION->sq[bK] = 0;
	    for (j = 0; j < 4; j++)
	      if (pi[j])
		POSITION->sq[s[j]] = 0;
	    for (j = 0; j < 4; j++)
	      if (pi[j] == BlockedPawn)
		POSITION->sq[s[j] + 8] = 0;
	  }
  free (POSITION->DYN_ROOT);
}

#ifdef WINDOWS
#define POPOLARE_THREAD() DWORD WINAPI popolare_thread (LPWORD A)
#else
#define POPOLARE_THREAD() void* popolare_thread (void* A)
#endif
static int NEXT_RF;

POPOLARE_THREAD()
{
  int v;
  while (TRUE)
    {
      LOCK (RB_SMP);
      v = --NEXT_RF;
      UNLOCK (RB_SMP);
      if (v < 0)
	break;
      RE_FETTA (v);
    }
  return NULL;
}

void SAVE_RTB (RobboTotalBase*, uint8**, uint64, char*);
void popolare (RobboTotalBase * Tot)
{
  int RF, cpu;
  uint64 pro, i, PR, Clock = GetClock ();
  uint8* pi;

  pi = Tot->p;
  S[0] = S[1] = S[2] = S[3] = A1;
  T[0] = T[1] = T[2] = T[3] = H8;
  if (pi[0] == 0)
    S[0] = T[0] = -1;
  if (pi[1] == 0)
    S[1] = T[1] = -1;
  if (pi[2] == 0)
    S[2] = T[2] = -1;
  if (pi[3] == 0)
    S[3] = T[3] = -1;
  if (pi[0] == wEnumP || pi[0] == bEnumP)
    {
      S[0] = A2;
      T[0] = H7;
    }
  if (pi[1] == wEnumP || pi[1] == bEnumP)
    {
      S[1] = A2;
      T[1] = H7;
    }
  if (pi[2] == wEnumP || pi[2] == bEnumP)
    {
      S[2] = A2;
      T[2] = H7;
    }
  if (pi[3] == wEnumP || pi[3] == bEnumP)
    {
      S[3] = A2;
      T[3] = H7;
    }
  if (pi[0] == BlockedPawn)
    {
      S[0] = A2;
      T[0] = H6;
    }
  if (pi[1] == BlockedPawn)
    {
      S[1] = A2;
      T[1] = H6;
    }
  if (pi[2] == BlockedPawn)
    {
      S[2] = A2;
      T[2] = H6;
    }
  if (pi[3] == BlockedPawn)
    {
      S[3] = A2;
      T[3] = H6;
    }
  pro = Tot->PEDONE ? 1806 : 462;
  RF = Tot->PEDONE ? 1806 : 462;
  for (i = 0; i < 4; i++)
    pro *= Tot->m[i];
  for (i = 0; i < 8; i++)
    Tot->supp_indice[i] = 0xffffffff;
  Tot->pro = pro;
  Tot->PawnMult8 = (Tot->PawnMult + 7) >> 3;
  Tot->Fsupp = NULL;
  
  Tot->data = malloc (Tot->pro);
  PR = RF * Tot->PawnMult8;
  posix_memalign ((void*) &Tot->MAR, 64, PR);
  posix_memalign ((void*) &Tot->VIT, 64, PR);
  posix_memalign ((void*) &Tot->PER, 64, PR);
  for (i = 0; i < PR; i++)
    Tot->MAR[i] = Tot->VIT[i] = Tot->PER[i] = 0;

  TOT = Tot;

  NEXT_RF = RF;
  LOCK_INIT (RB_SMP);
  for (cpu = 0; cpu < RB_CPUS; cpu++)
    PTHREAD_CREATE (&PTHREAD[cpu], NULL, popolare_thread, NULL);
  for (cpu = 0; cpu < RB_CPUS; cpu++)
    PTHREAD_JOIN (PTHREAD[cpu]); /* dispel nanosleep in WINDOWS */
  LOCK_DESTROY (RB_SMP);

  Tot->shift = 8;
  int num_locks =
    (Tot->PawnMult8 * 8 * RF + (1 << Tot->shift) - 1) >> Tot->shift;
  printf ("shift %d num_locks %d*%ld CPUS %d\n",
	  Tot->shift, num_locks, sizeof (ROBBO_LOCK_TYPE), RB_CPUS);
  Tot->locks = malloc (num_locks * sizeof (ROBBO_LOCK_TYPE));
  for (i = 0; i < num_locks; i++)
    ROBBO_LOCK_INIT (&(Tot->locks)[i]);

  if (DISK_SWAP && !Tot->simm)
    {
      SAVE_RTB (Tot, &Tot->MAR, Tot->PawnMult8, "MAR");
      SAVE_RTB (Tot, &Tot->VIT, Tot->PawnMult8, "VIT");
      if (Tot->is_uno)
	{	
	  SAVE_RTB (Tot, &Tot->data, Tot->PawnMult, "DATA");
	  SAVE_RTB (Tot, &Tot->PER, Tot->PawnMult8, "PER");
	}
    }
  printf ("INIZ %s [%.2fs]\n",
	  Tot->string, ((double) (GetClock () - Clock)) / 1000000.0);
}
