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

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

boolean BITBORDO_PQ (typePOS* POSITION, type_PiSq* PiSq)
{
  uint64 T;
  int n = 0, sq;
  T = POSITION->OccupiedBW ^ (wBitboardK | bBitboardK);
  PiSq->wtm = POSITION->wtm;
  PiSq->wK = POSITION->wKsq;
  PiSq->bK = POSITION->bKsq;
  PiSq->pedone = FALSE;
  while (T && n < 4)
    {
      sq = BSF (T);
      if (PiSq->Blocked & SqSet[sq])
	{
	  PiSq->pi[n] = BlockedPawn;
	  T ^= SqSet[sq + 8];
	}
      else
	PiSq->pi[n] = RIMAP[POSITION->sq[sq]];
      if (PiSq->pi[n] == wEnumP || PiSq->pi[n] == bEnumP
	  || PiSq->pi[n] == BlockedPawn)
	PiSq->pedone = TRUE;
      PiSq->sq[n++] = sq;
      BitClear (sq, T);
    }
  PiSq->n = n;
  if (n == 4 && T)
    return FALSE;
  return TRUE;
}

#define LANCpez(u, x) (x) = lanc_pedone[u][x]
#define LANCnor(u, x) (x) = lanc_norm[u][x]

boolean tot_ind_ott
 (type_PiSq* PiSq, int* tb_num, uint64* ind, uint64* Double, int* re_fetta)
{
  int K1, K2, rf;
  int i = 0, v, PI[4], Squ[4], x;
  RobboTotalBase* TotalBase;
  boolean simm = FALSE;

  if (Double != NULL)
    *Double = -1;
  if (PiSq->wtm)
    {
      K1 = PiSq->wK;
      K2 = PiSq->bK;
    }
  else
    {
      K1 = PiSq->bK;
      K2 = PiSq->wK;
      for (i = 0; i < 4; i++)
	PiSq->pi[i] = col_scamb[PiSq->pi[i]];
    }
  v = TotalBaseTable[PiSq->pi[0]][PiSq->pi[1]][PiSq->pi[2]][PiSq->pi[3]];
  TotalBase = TABLE_TOTAL_BASES + (v % 65536);

  if ((v % 65536) == -1)
    return FALSE;
  for (i = 0; i < 4; i++)
    {
      x = (v >> (16 + 2 * i)) & 3;
      Squ[i] = PiSq->sq[x];
      PI[i] = PiSq->pi[x];
    }
  if (PiSq->pedone)
    {
      int w = (2 * (FILE (K2) >= FE)) + (!PiSq->wtm);
      for (i = 0; i < 4; i++)
	{
	  LANCpez (w, Squ[i]);
	  if (PI[i] == BlockedPawn && !PiSq->wtm)
            Squ[i] -= 8;
	}
      LANCpez (w, K1);
      LANCpez (w, K2);
      rf = RE_pedone[64 * K1 + K2];
    }
  else
    {
      uint8 w = oLANC[K1][K2];
      for (i = 0; i < 4; i++)
	LANCnor (w, Squ[i]);
      LANCnor (w, K1);
      LANCnor (w, K2);
      if (RANK (K1) == FILE (K1) && RANK (K2) == FILE (K2))
	simm = TRUE;
      rf = RE_normale[64 * K1 + K2];
    }
  if (rf == -1)
    return FALSE;
  *re_fetta = rf;
  for (i = 0; i < 4; i++)
    {
      if (!PI[i])
	Squ[i] = 0;
      if (PI[i] == wEnumP || PI[i] == bEnumP || PI[i] == BlockedPawn)
	Squ[i] -= 8;
    }
  *tb_num = v % 65536;
  if (TotalBase->efgh2 != -1)
    {
      int f1 = FILE (Squ[TotalBase->efgh1]);
      int f2 = FILE (Squ[TotalBase->efgh2]);
      f1 = MIN (f1, (FH - f1)) & 1;
      f2 = MIN (f2, (FH - f2)) & 1;
      *tb_num = TotalBase->efgh[f1 + 2 * f2];
    }
  else if (TotalBase->efgh1 != -1)
    {
      int f = FILE (Squ[TotalBase->efgh1]);
      *tb_num = TotalBase->efgh[MIN (f, (FH - f))];
    }
  if (*tb_num == -1 || *tb_num == 0xffff)
    return FALSE;
  for (i = 0; i < 4; i++)
    Squ[i] >>= TotalBase->efgh_shift[i];
  if (PI[0] == PI[3] && PI[3])
    {
      /* Squ[0] = STESSO4[Squ[0]][Squ[1]][Squ[2]][Squ[3]]; */
      int m0, m1, m2, m3, t0, t1, u0, u1;
      t0 = MAX (Squ[0], Squ[1]);
      u0 = MIN (Squ[0], Squ[1]);
      t1 = MAX (Squ[2], Squ[3]);
      u1 = MIN (Squ[2], Squ[3]);
      m0 = MAX (t0, t1);
      m1 = MIN (t0, t1);
      m2 = MAX (u0, u1);
      m3 = MIN (u0, u1);
      if (m1 < m2)
	SCAMB (m1, m2);
      Squ[0]= (m0 * (m0 - 1) * (m0 - 2) * (m0 - 3)) / 24 +
	(m1 * (m1 - 1) * (m1 - 2)) / 6 + (m2 * (m2 - 1)) / 2 + m3;
      Squ[1] = Squ[2] = Squ[3] = 0;
    }
  else if (PI[0] == PI[2] && PI[2])
    {
      Squ[0] = STESSO3[Squ[0]][Squ[1]][Squ[2]];
      Squ[1] = Squ[2] = 0;
    }
  else if (PI[1] == PI[3] && PI[3])
    {
      Squ[1] = STESSO3[Squ[1]][Squ[2]][Squ[3]];
      Squ[2] = Squ[3] = 0;
    }
  else
    {
      if (PI[0] == PI[1] && PI[1])
	{
	  Squ[0] = STESSO2[Squ[0]][Squ[1]];
	  Squ[1] = 0;
	}
      if (PI[1] == PI[2] && PI[2])
	{
	  Squ[1] = STESSO2[Squ[1]][Squ[2]];
	  Squ[2] = 0;
	}
      if (PI[2] == PI[3] && PI[3])
	{
	  Squ[2] = STESSO2[Squ[2]][Squ[3]];
	  Squ[3] = 0;
	}
    }
  *ind = ((Squ[0] * TotalBase->m[1] + Squ[1])
	  * TotalBase->m[2] + Squ[2]) * TotalBase->m[3] + Squ[3];
  if (Double == NULL)
    return TRUE;
  if (simm)
    {
      if (PI[0] == PI[3] && PI[3])
	{
	  i = Sq4normale[Squ[0]];
	  Squ[0] = i & 077;
	  Squ[1] = (i >> 6) & 077;
	  Squ[2] = (i >> 12) & 077;
	  Squ[3] = i >> 18;
	}
      else if (PI[0] == PI[2] && PI[2])
	{
	  i = Sq3normale[Squ[0]];
	  Squ[0] = i & 077;
	  Squ[1] = (i >> 6) & 077;
	  Squ[2] = i >> 12;
	}
      else if (PI[1] == PI[3] && PI[3])
	{
	  i = Sq3normale[Squ[1]];
	  Squ[1] = i & 077;
	  Squ[2] = (i >> 6) & 077;
	  Squ[3] = i >> 12;
	}
      else
	{
	  if (PI[0] == PI[1] && PI[1])
	    {
	      i = Sq2Normal[Squ[0]];
	      Squ[0] = i & 077;
	      Squ[1] = i >> 6;
	    }
	  if (PI[1] == PI[2] && PI[2])
	    {
	      i = Sq2Normal[Squ[1]];
	      Squ[1] = i & 077;
	      Squ[2] = i >> 6;
	    }
	  if (PI[2] == PI[3] && PI[3])
	    {
	      i = Sq2Normal[Squ[2]];
	      Squ[2] = i & 077;
	      Squ[3] = i >> 6;
	    }
	}
      for (i = 0; i < 4; i++)
	LANCnor (1, Squ[i]);
      if (PI[0] == PI[3] && PI[3])
	{
	  /* Squ[0] = STESSO4[Squ[0]][Squ[1]][Squ[2]][Squ[3]]; */
	  int m0, m1, m2, m3, t0, t1, u0, u1;
	  t0 = MAX (Squ[0], Squ[1]);
	  u0 = MIN (Squ[0], Squ[1]);
	  t1 = MAX (Squ[2], Squ[3]);
	  u1 = MIN (Squ[2], Squ[3]);
	  m0 = MAX (t0, t1);
	  m1 = MIN (t0, t1);
	  m2 = MAX (u0, u1);
	  m3 = MIN (u0, u1);
	  if (m1 < m2)
	    SCAMB (m1, m2);
	  Squ[0]= (m0 * (m0 - 1) * (m0 - 2) * (m0 - 3)) / 24 +
	    (m1 * (m1 - 1) * (m1 - 2)) / 6 + (m2 * (m2 - 1)) / 2 + m3;
	  Squ[1] = Squ[2] = Squ[3] = 0;
	}
      else if (PI[0] == PI[2] && PI[2])
	{
	  Squ[0] = STESSO3[Squ[0]][Squ[1]][Squ[2]];
	  Squ[1] = Squ[2] = 0;
	}
      else if (PI[1] == PI[3] && PI[3])
	{
	  Squ[1] = STESSO3[Squ[1]][Squ[2]][Squ[3]];
	  Squ[2] = Squ[3] = 0;
	}
      else
	{
	  if (PI[0] == PI[1] && PI[1])
	    {
	      Squ[0] = STESSO2[Squ[0]][Squ[1]];
	      Squ[1] = 0;
	    }
	  if (PI[1] == PI[2] && PI[2])
	    {
	      Squ[1] = STESSO2[Squ[1]][Squ[2]];
	      Squ[2] = 0;
	    }
	  if (PI[2] == PI[3] && PI[3])
	    {
	      Squ[2] = STESSO2[Squ[2]][Squ[3]];
	      Squ[3] = 0;
	    }
	}
      *Double = ((Squ[0] * TotalBase->m[1] + Squ[1])
		 * TotalBase->m[2] + Squ[2]) * TotalBase->m[3] + Squ[3];
    }
  else
    *Double = -1;
  return TRUE;
}

boolean fare_PQ
(type_PiSq* PiSq, int* Value, uint64* ind, uint64* Double, int* rf)
{
  int tb;
  RobboTotalBase* TotalBase;
  *Value = 0;
  if (!tot_ind_ott (PiSq, &tb, ind, Double, rf))
    return FALSE;
  TotalBase = TABLE_TOTAL_BASES + tb;
  *Value = tot_valu (TotalBase, (TotalBase->PawnMult) * (*rf) + (*ind));
  if ((*Value) != dROTT)
    return TRUE;
  return FALSE;
}

static boolean TotalBaseScore (typePOS* POSITION, int* Value)
{
  uint64 ind, U, V, W;
  int rf;
  type_PiSq PiSq[1];
  boolean b;
  PiSq->pi[0] = PiSq->pi[1] = PiSq->pi[2] = PiSq->pi[3] = 0;
  PiSq->sq[0] = PiSq->sq[1] = PiSq->sq[2] = PiSq->sq[3] = 0;
  U = wBitboardP & (bBitboardP >> 8);
  V = (U << 1) & U & ~FILEa;
  W = (V << 1) & V;
  PiSq->Blocked = (U & ~V) | (W & ~((W << 1) & W));
  b = BITBORDO_PQ (POSITION, PiSq);
  if (b)
    b = fare_PQ (PiSq, Value, &ind, NULL, &rf);
  while (!b && PiSq->Blocked && PiSq->n < 4) /* exp e-f-g-h? */
    {
      BitClear (0, PiSq->Blocked);
      b = BITBORDO_PQ (POSITION, PiSq);
      if (b) /* TRUE */
	b = fare_PQ (PiSq, Value, &ind, NULL, &rf);
    }
  return b;
}

boolean ott_indice (type_PiSq* PiSq, uint64* ind, uint64* Double, int* rf)
{
  int tb;
  if (tot_ind_ott (PiSq, &tb, ind, Double, rf))
    return TRUE;
  return FALSE;
}

boolean RobboTotalBaseScore (typePOS* POSITION, int* Value)
{
  int ep = POSITION->DYN->ep, move, w, vb = 255;
  boolean b, b1, b2;
  if (POSITION->DYN->oo)
    return FALSE;
  if (!ep)
    return TotalBaseScore (POSITION, Value);
  b = TotalBaseScore (POSITION, Value);
  if (!b)
    return FALSE;
  if (*Value == dVITT)
    return TRUE;
  if (POSITION->wtm)
    {
      if (FILE (ep) != FH && (wBitboardP & SqSet[ep - 7]))
	{
	  move = ((ep - 7) << 6) | ep | FlagEP;
	  Make (POSITION, move);
	  Mobility (POSITION);
	  if (!IS_ILLEGAL)
	    {
	      b1 = TotalBaseScore (POSITION, &w);
	      if (w < vb)
		vb = w;
	    }
	  Undo (POSITION, move);
	}
      if (FILE (ep) != FA && (wBitboardP & SqSet[ep - 9]))
	{
	  move = ((ep - 9) << 6) | ep | FlagEP;
	  Make (POSITION, move);
	  Mobility (POSITION);
	  if (!IS_ILLEGAL)
	    {
	      b2 = TotalBaseScore (POSITION, &w);
	      if (w < vb)
		vb = w;
	    }
	  Undo (POSITION, move);
	}
    }
  else
    {
      if (FILE (ep) != FH && (bBitboardP & SqSet[ep + 9]))
	{
	  move = ((ep + 9) << 6) | ep | FlagEP;
	  Make (POSITION, move);
	  Mobility (POSITION);
	  if (!IS_ILLEGAL)
	    {
	      b1 = TotalBaseScore (POSITION, &w);
	      if (w < vb)
		vb = w;
	    }
	  Undo (POSITION, move);
	}
      if (FILE (ep) != FA && (bBitboardP & SqSet[ep + 7]))
	{
	  move = ((ep + 7) << 6) | ep | FlagEP;
	  Make (POSITION, move);
	  Mobility (POSITION);
	  if (!IS_ILLEGAL)
	    {
	      b2 = TotalBaseScore (POSITION, &w);
	      if (w < vb)
		vb = w;
	    }
	  Undo (POSITION, move);
	}
    }
  if (DISCO_PERDITA (vb))
    vb = dVITT;
  else if (vb == dVITT)
    vb = dPERD0;
  if (DISCO_PERDITA (vb) && DISCO_PERDITA (*Value))
    *Value = MAX (*Value, vb);
  else
    *Value = MIN (*Value, vb);
  return TRUE;
}
