#include "RobboLito.h"

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

const uint64 WhiteEP[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)
  };

const uint64 BlackEP[8] =
  { Bitboard2 (B5, B5), Bitboard2 (A5, C5),
    Bitboard2 (B5, D5), Bitboard2 (C5, E5),
    Bitboard2 (D5, F5), Bitboard2 (E5, G5),
    Bitboard2 (F5, H5), Bitboard2 (G5, G5)
  };

#ifdef MAGIC_BITBOARDS
#define ClearOccupied(M, x) { POSITION->OccupiedBW &= M; }
#define SetOccupied(M, x) { POSITION->OccupiedBW |= M; }
#else
#define ClearOccupied(M, x) \
  { POSITION->OccupiedBW &= M; \
    POSITION->OccupiedL90 &= ClearL90[x]; \
    POSITION->OccupiedL45 &= ClearL45[x]; \
    POSITION->OccupiedR45 &= ClearR45[x]; }
#define SetOccupied(M, x) \
  { POSITION->OccupiedBW |= M; \
    POSITION->OccupiedL90 |= SetL90[x]; \
    POSITION->OccupiedL45 |= SetL45[x]; \
    POSITION->OccupiedR45|=SetR45[x]; }
#endif

const static int PromW[8] = { 0, 0, 0, 0, wEnumN, wEnumBL, wEnumR, wEnumQ };

void
MakeWhite (typePOS *POSITION, uint32 move)
{
  int fr, to, pi, cp, z;
  uint64 mask;
  memcpy (POSITION->DYN + 1, POSITION->DYN, 32);
  fr = FROM (move);
  to = TO (move);
  pi = POSITION->sq[fr];
  POSITION->DYN++;
  POSITION->DYN->ep = 0;
  POSITION->sq[fr] = 0;
  mask = SqClear[fr];
  wBitboardOcc &= mask;
  POSITION->bitboard[pi] &= mask;
  ClearOccupied (mask, fr);
  cp = POSITION->sq[to];
  POSITION->DYN->cp = cp;
  POSITION->wtm ^= 1;
  if (pi == wEnumK)
    POSITION->wKsq = to;
  if (cp)
    {
      mask = SqClear[to];
      bBitboardOcc &= mask;
      POSITION->bitboard[cp] &= mask;
    }
  else
    {
      mask = SqSet[to];
      SetOccupied (mask, to);
    }
  POSITION->sq[to] = pi;
  wBitboardOcc |= SqSet[to];
  POSITION->bitboard[pi] |= SqSet[to];
  if (pi == wEnumP)
    {
      if (MoveIsEP (move))
	{
	  z = to ^ 8;
	  mask = SqClear[z];
	  bBitboardOcc &= mask;
	  bBitboardP &= mask;
	  ClearOccupied (mask, z);
	  POSITION->sq[z] = 0;
	}
      else if (MoveIsProm (move))
	{
	  pi = PromW[(move & FLAG_MASK) >> 12];
	  if (pi == wEnumBL && SqSet[to] & DARK)
	    pi = wEnumBD;
	  POSITION->sq[to] = pi;
	  wBitboardP &= SqClear[to];
	  POSITION->bitboard[pi] |= SqSet[to];
	}
      else if ((to ^ fr) == 16)
	{
	  if (WhiteEP[to & 7] & bBitboardP)
	    {
	      z = (fr + to) >> 1;
	      POSITION->DYN->ep = z;
	    }
	}
    }
}

void
UndoWhite (typePOS *POSITION, uint32 move)
{
  int fr, to, pi, cp, z;
  uint64 mask;
  fr = FROM (move);
  to = TO (move);
  pi = POSITION->sq[to];
  POSITION->wtm ^= 1;
  if (MoveIsProm (move))
    {
      POSITION->bitboard[pi] &= SqClear[to];
      pi = wEnumP;
    }
  POSITION->sq[fr] = pi;
  POSITION->sq[to] = POSITION->DYN->cp;
  if (pi == wEnumK)
    POSITION->wKsq = fr;
  mask = SqSet[fr];
  wBitboardOcc |= mask;
  POSITION->bitboard[pi] |= mask;
  SetOccupied (mask, fr);
  mask = SqClear[to];
  wBitboardOcc &= mask;
  POSITION->bitboard[pi] &= mask;
  cp = POSITION->DYN->cp;
  if (cp)
    {
      mask = ~mask;
      bBitboardOcc |= mask;
      POSITION->bitboard[cp] |= mask;
    }
  else
    {
      ClearOccupied (mask, to);
      if (MoveIsEP (move))
	{
	  z = to ^ 8;
	  POSITION->sq[z] = bEnumP;
	  mask = SqSet[z];
	  bBitboardOcc |= mask;
	  bBitboardP |= mask;
	  SetOccupied (mask, z);
	}
    }
  POSITION->DYN--;
}

const static int PromB[8] = { 0, 0, 0, 0, bEnumN, bEnumBL, bEnumR, bEnumQ };

void
MakeBlack (typePOS *POSITION, uint32 move)
{
  int fr, to, pi, cp, z;
  uint64 mask;
  memcpy (POSITION->DYN + 1, POSITION->DYN, 32);
  fr = FROM (move);
  to = TO (move);
  pi = POSITION->sq[fr];
  POSITION->DYN++;
  POSITION->DYN->ep = 0;
  POSITION->sq[fr] = 0;
  mask = SqClear[fr];
  bBitboardOcc &= mask;
  POSITION->bitboard[pi] &= mask;
  ClearOccupied (mask, fr);
  cp = POSITION->sq[to];
  POSITION->DYN->cp = cp;
  POSITION->wtm ^= 1;
  if (pi == bEnumK)
    POSITION->bKsq = to;
  if (cp)
    {
      mask = SqClear[to];
      wBitboardOcc &= mask;
      POSITION->bitboard[cp] &= mask;
    }
  else
    {
      mask = SqSet[to];
      SetOccupied (mask, to);
    }
  POSITION->sq[to] = pi;
  bBitboardOcc |= SqSet[to];
  POSITION->bitboard[pi] |= SqSet[to];
  if (pi == bEnumP)
    {
      if (MoveIsEP (move))
	{
	  z = to ^ 8;
	  mask = SqClear[z];
	  wBitboardOcc &= mask;
	  wBitboardP &= mask;
	  ClearOccupied (mask, z);
	  POSITION->sq[z] = 0;
	}
      else if (MoveIsProm (move))
	{
	  pi = PromB[(move & FLAG_MASK) >> 12];
	  if (pi == bEnumBL && SqSet[to] & DARK)
	    pi = bEnumBD;
	  POSITION->sq[to] = pi;
	  bBitboardP &= SqClear[to];
	  POSITION->bitboard[pi] |= SqSet[to];
	}
      else if ((to ^ fr) == 16)
	{
	  if (BlackEP[to & 7] & wBitboardP)
	    {
	      z = (fr + to) >> 1;
	      POSITION->DYN->ep = z;
	    }
	}
    }
}

void
UndoBlack (typePOS *POSITION, uint32 move)
{
  int fr, to, pi, cp, z;
  uint64 mask;
  fr = FROM (move);
  to = TO (move);
  pi = POSITION->sq[to];
  POSITION->wtm ^= 1;
  if (MoveIsProm (move))
    {
      POSITION->bitboard[pi] &= SqClear[to];
      pi = bEnumP;
    }
  POSITION->sq[fr] = pi;
  POSITION->sq[to] = POSITION->DYN->cp;
  if (pi == bEnumK)
    POSITION->bKsq = fr;
  mask = SqSet[fr];
  bBitboardOcc |= mask;
  POSITION->bitboard[pi] |= mask;
  SetOccupied (mask, fr);
  mask = SqClear[to];
  bBitboardOcc &= mask;
  POSITION->bitboard[pi] &= mask;
  cp = POSITION->DYN->cp;
  if (cp)
    {
      mask = ~mask;
      wBitboardOcc |= mask;
      POSITION->bitboard[cp] |= mask;
    }
  else
    {
      ClearOccupied (mask, to);
      if (MoveIsEP (move))
	{
	  z = to ^ 8;
	  POSITION->sq[z] = wEnumP;
	  mask = SqSet[z];
	  wBitboardOcc |= mask;
	  wBitboardP |= mask;
	  SetOccupied (mask, z);
	}
    }
  POSITION->DYN--;
}

void
Make (typePOS *POSITION, uint32 move)
{
  if (POSITION->wtm)
    MakeWhite (POSITION, move);
  else
    MakeBlack (POSITION, move);
}

void
Undo (typePOS *POSITION, uint32 move)
{
  if (!POSITION->wtm)
    UndoWhite (POSITION, move);
  else
    UndoBlack (POSITION, move);
}
