
#include "RobboLito_def.h"
#include "RobboTotalBase.h"

#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, int* re_fetta)
{
  int K1, K2, rf;
  int i = 0, v, PI[4], Squ[4], x;
  RobboTotalBase* TotalBase;
  boolean simm = FALSE;

  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]]; /* alter */
    }
  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++) /* PiSq->pi[n] ? */
    {
      x = (v >> (16 + 2 * i)) & 3;
      Squ[i] = PiSq->sq[x];
      PI[i] = PiSq->pi[x];
    }
  if (!PiSq->wtm)
    for (i = 0; i < 4; i++)
      PiSq->pi[i] = col_scamb[PiSq->pi[i]]; /* modify (backed) */
  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];
  return TRUE;
}

#include <string.h> /* memcpy */
static boolean PiSqMakeMove_ep_only /* ep */
(Type_PiSq * BeFore, Type_PiSq * After, int from, int to, int wtm)
{
  int i = 0, w1 = -1, t1 = -1, w2 = -1, t2 = -1;
  memcpy (After, BeFore, 16);
  After->ep = 0; /* clear */
  if (BeFore->wtm)
    {
      for (i = 0; i < BeFore->n; i++)
	{
	  if (BeFore->sq[i] == from &&
	      (BeFore->pi[i] == 1 || BeFore->pi[i] == 8))
	    {
	      w1 = i;
	      t1 = BeFore->pi[i]; /* Pawn 1, BlockedPawn 8 */
	    }
	  if (BeFore->sq[i] == to - 8 && BeFore->pi[i] == 9)
	    {
	      w2 = i;
	      t2 = 9; /* BlackPawn 9 */
	    }
	  if (BeFore->sq[i] == to - 16 && BeFore->pi[i] == 8)
	    {
	      w2 = i;
	      t2 = 8; /* BlockedPawn 8 */
	    }
	}
    }
  else /* btm */
    {
      for (i = 0; i < BeFore->n; i++)
	{
	  if (BeFore->sq[i] == to + 8 &&
	      (BeFore->pi[i] == 1 || BeFore->pi[i] == 8))
	    {
	      w2 = i;
	      t2 = BeFore->pi[i]; /* Pawn 1, BlockedPawn 8 */
	    }
	  if (BeFore->sq[i] == from && BeFore->pi[i] == 9)
	    {
	      w1 = i;
	      t1 = 9; /* BlackPawn 9 */
	    }
	  if (BeFore->sq[i] == from - 8 && BeFore->pi[i] == 8)
	    {
	      w1 = i;
	      t1 = 8; /* BlockedPawn 8 */
	    }
	}
    }
  if (t1 == -1 || t2 == -1 || w1 == -1 || w2 == -1)
    return FALSE; /* ERROR */
  if (t1 != 8 && t2 != 8) /* no is BlockedPawn */
    {
      After->sq[w1] = to;
      After->pi[w1] = t1; /* switch ed */
      if (w2 != After->n - 1)
	{
	  After->pi[w2] = After->pi[After->n - 1];
	  After->sq[w2] = After->sq[After->n - 1]; /* movable */
	}
      After->n--;
      After->pi[After->n] = After->sq[After->n] = 0; /* comply */
    }
  if (t1 == 8 && t2 == 8) /* both is BlockedPawn */
    {
      if (After->n == 4)
	return FALSE; /* impel */
      if (After->wtm)
	{
	  After->pi[w2] = 1; /* WhitePawn */
	  After->pi[w1] = 1; /* WhitePawn */
	  After->sq[w1] = to;
	  After->pi[After->n] = 9; /* BlackPawn */
	  After->sq[After->n] = from + 8;
	  After->n++; /* plus */
	}
      else
	{
	  After->pi[w2] = 9; /* BlackPawn */
	  After->sq[w2] = to + 16;
	  After->pi[w1] = 9; /* BlackPawn */
	  After->sq[w1] = to;
	  After->pi[After->n] = 1; /* WhitePawn */
	  After->sq[After->n] = from - 8;
	  After->n++; /* plus */
	}
    }
  if (t1 == 8 && t2 != 8) /* from is BlockedPawn */
    {
      if (After->wtm)
	{
	  After->sq[w1] = to;
	  After->pi[w1] = 1; /* WhitePawn */
	  After->sq[w2] = from + 8;
	  After->pi[w2] = 9; /* BlackPawn */
	}
      else
	{
	  After->sq[w1] = to;
	  After->pi[w1] = 9; /* BlackPawn */
	  After->sq[w2] = from - 8;
	  After->pi[w2] = 1; /* WhitePawn */
	}
    }
  if (t1 != 8 && t2 == 8) /* to is BlockedPawn */
    {
      After->sq[w1] = to;
      if (After->wtm)
	{
	  After->pi[w2] = 1; /* WhitePawn */
	}
      else /* btm */
	{
	  After->pi[w2] = 9; /* BlackPawn */
	  After->sq[w2] = After->sq[w2] + 8;
	}
    }
  After->wtm ^= 1;  /* switch */
  return TRUE;
}

static boolean fare_ep
(Type_PiSq * PiSq, int from, int to, int wtm, uint8 * v, int type)
{
  int tb;
  RobboTotalBase* TotalBase;
  uint64 ind;
  int rf;
  boolean b;
  Type_PiSq PiSq2[1];

  b = PiSqMakeMove_ep_only (PiSq, PiSq2, from, to, 1);
  if (!b)
    return FALSE;
  b = tot_ind_ott (PiSq2, &tb, &ind, &rf);
  if (!b)
    return FALSE;
  TotalBase = TABLE_TOTAL_BASES + tb;
  return tot_valu_type (TotalBase, (TotalBase->PawnMult) * rf + ind, type, v);
}

#define UP_DATE_EP() \
   if (b) { if (DISCO_PERDITA (v)) { *Value = dVITT; return TRUE; } \
	    if (v > vb) vb = v ; } else Bad = TRUE;

static boolean fare_PQ_is_ep (Type_PiSq * PiSq, uint8 * Value, int type)
{
  int tb;
  RobboTotalBase* TotalBase;
  uint64 ind;
  int rf;
  boolean b, Bad = FALSE;
  uint8 v, vb = 0x0;

  if (!tot_ind_ott (PiSq, &tb, &ind, &rf))
    return FALSE;
  TotalBase = TABLE_TOTAL_BASES + tb;
  b = tot_valu_type (TotalBase, (TotalBase->PawnMult) * rf + ind, type, Value);
  if (!b || (*Value) == dROTT)
    return FALSE;
  if (*Value == dVITT)
    return TRUE;
  if (PiSq->wtm) /* and deem, ep buys not legal ? */
    {
      if ((PiSq->ep & 07) != 07) /* target: not file number h */
	{
	  b = fare_ep (PiSq, PiSq->ep - 7, PiSq->ep, 1, &v, type);
	  UP_DATE_EP (); /* IvanHoe plus ComStock bug */
	}
      if ((PiSq->ep & 07) != 00) /* target: not file number a */
	{
	  b = fare_ep (PiSq, PiSq->ep - 9, PiSq->ep, 1, &v, type);
	  UP_DATE_EP (); /* IvanHoe plus ComStock bug */
	}
      if (Bad)
	return FALSE;
    }
  else /* btm */
    {
      if ((PiSq->ep & 07) != 07) /* target: not file number h */
	{
	  b = fare_ep (PiSq, PiSq->ep + 9, PiSq->ep, 0, &v, type);
	  UP_DATE_EP ();
	}
      if ((PiSq->ep & 07) != 00) /* target: not file number a */
	{
	  b = fare_ep (PiSq, PiSq->ep + 7, PiSq->ep, 0, &v, type);
	  UP_DATE_EP ();
	}
      if (Bad)
	return FALSE;
    }
  if (vb == dVITT)
    vb = dPERD0;
  if (vb == dROTT)
    vb = *Value;
  if (DISCO_PERDITA (vb) && DISCO_PERDITA (*Value))
    *Value = MAX (*Value, vb);
  else
    *Value = MIN (*Value, vb);
  return TRUE;
}

uint64 AttK[0100]; /* extern */
boolean fare_PQ_with_ep_with_type (Type_PiSq* PiSq, uint8* Value, int type)
{
  int tb, rf, i;
  RobboTotalBase* TotalBase;
  uint64 ind, B = 0;
  boolean b;
  Type_PiSq Copy[1];

  *Value = 0;
  PiSq->nodes = 0; /* AllWays */
  memcpy (Copy, PiSq, 16);
  Copy->pedone = FALSE;
  Copy->n = 0;
  for (i = 0; i < 4; i++)
    {
      if ((PiSq->pi[i] & 07) == 1 || PiSq->pi[i] == 8)
	Copy->pedone = TRUE; /* incur */
      if (PiSq->pi[i])
	Copy->n++; /* valid */
      if (PiSq->pi[i] == 8) /* BlockedPawn */
	B |= (1ULL << PiSq->sq[i]);
    }
  if ((B & ~FILEh) & (B << 1)) /* desist */
    return FALSE;
  if (PiSq->wK == PiSq->bK)
    return FALSE;
  if ((1ULL << PiSq->wK) & AttK[PiSq->bK]) /* broken */
    return FALSE;
  if (PiSq->ep & 077) /* HACK */
    return fare_PQ_is_ep (Copy, Value, type);
  if (!tot_ind_ott (Copy, &tb, &ind, &rf))
    return FALSE;
  TotalBase = TABLE_TOTAL_BASES + tb;
  b = tot_valu_type (TotalBase, (TotalBase->PawnMult) * rf + ind, type, Value);
  return b && ((*Value) != dROTT);
}
