// includes

#include <string.h>

#include "util.h"
#include "bitboard.h"
#include "square.h"
#include "piece.h"

// variables

bitboard_t bitboards;
unsigned char _BitCount[65536];
unsigned char _lzArray[65536];

short distance[64][64];
short taxicab[64][64];
short directions[64][64];

const short raybeg[7] = { 0, 0, 0, 0, 4, 0, 0 };
const short rayend[7] = { 0, 0, 0, 4, 8, 8, 0 };

int range[8] = { 0, 0, 0, 1, 1, 1, 0, 0 };


/*  Data used for generating MoveArray  */

static const int dir[8][8] =
{
  { 0, 0, 0, 0, 0, 0, 0, 0 },
  { 9, 11, 0, 0, 0, 0, 0, 0 },
  { -21, -19, -12, -8, 8, 12, 19, 21 },
  { -11, -9, 9, 11, 0, 0, 0, 0 },
  { -10, -1, 1, 10, 0, 0, 0, 0 },
  { -11, -10, -9, -1, 1, 9, 10, 11 },
  { -11, -10, -9, -1, 1, 9, 10, 11 },
  { -9, -11, 0, 0, 0, 0, 0, 0 }
};
static const int ndir[8] = 
{ 0, 2, 8, 4, 4, 8, 8, 2 };

static const int map[120] =
{
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1,  0,  1,  2,  3,  4,  5,  6,  7, -1,
  -1,  8,  9, 10, 11, 12, 13, 14, 15, -1,
  -1, 16, 17, 18, 19, 20, 21, 22, 23, -1,
  -1, 24, 25, 26, 27, 28, 29, 30, 31, -1,
  -1, 32, 33, 34, 35, 36, 37, 38, 39, -1,
  -1, 40, 41, 42, 43, 44, 45, 46, 47, -1,
  -1, 48, 49, 50, 51, 52, 53, 54, 55, -1,
  -1, 56, 57, 58, 59, 60, 61, 62, 63, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 
};


// functions


#define NBITS 16

static void InitLzArray (void)
/***************************************************************************
 *
 *  The lzArray is created.  This array is used when the position
 *  of the leading non-zero bit is required.  The convention used
 *  is that the leftmost bit is considered as position 0 and the
 *  rightmost bit position 63.
 *
 ***************************************************************************/
{
   int i, j, s, n;

   s = n = 1;
   for (i = 0; i < NBITS; i++) {
       for (j = s; j < s + n; j++){
	   _lzArray[j] = NBITS - 1 - i;
       }
       s += n;
       n += n;
   }
}




static void InitBitCount (void)
/**************************************************************************
 *
 *  The BitCount array returns the no. of bits present in the 16 bit
 *  input argument.  This is use for counting the number of bits set
 *  in a BitBoard (e.g. for mobility count).
 *
 **************************************************************************/
{
   int i, j, n;
 
   _BitCount[0] = 0;
   _BitCount[1] = 1; 
   i = 1;
   for (n = 2; n <= 16; n++)
   {
      i <<= 1;
      for (j = i; j <= i + (i-1); j++)  
         _BitCount[j] = 1 + _BitCount[j - i]; 
   }
} 

static void InitBitPosArray (void)
{
   BitBoard b;
   int i;

   b = (BitBoard) 1;  
   for (i = 63; i >= 0; i--, b <<= 1)
   {
      bitboards.BitPosArray[i] = b;
      bitboards.NotBitPosArray[i] = ~b;
   }
}
 
static void InitMoveArray (void)
{
   int piece, fsq, tsq, f, t, n;
   BitBoard *b;

   for (piece = pawn; piece <= bpawn; piece++)
   {
      for (fsq = 0; fsq < 120; fsq++)
      {
         if ((f = map[fsq]) == -1) continue;
         b = &bitboards.MoveArray[piece][f];
         *b = NULLBITBOARD;
         for (n = 0; n < ndir[piece]; n++)
         {
            tsq = fsq;
            do
            {
               tsq += dir[piece][n];
               if ((t = map[tsq]) != -1)
                  SETBIT (*b, t);
            } while (range[piece] && t != -1);
         }
      }
   }
}



static void InitRay (void)
{
   int piece, fsq, tsq, f, t, n, ray;
   BitBoard *b;

   memset (directions, -1, sizeof (directions));
   for (fsq = 0; fsq < 120; fsq++)
   {
      if ((f = map[fsq]) == -1) continue;
      ray = -1;
      for (piece = bishop; piece <= rook; piece++)
      {
         for (n = 0; n < ndir[piece]; n++)
         {
            b = &bitboards.Ray[f][++ray];
            *b = NULLBITBOARD;
            tsq = fsq;
            do
            {
               tsq += dir[piece][n];
               if ((t = map[tsq]) != -1)
	       {
                  SETBIT (*b, t);
	          directions[f][t] = ray;
	       }
            } while (t != -1);
         }
      }
   }
}


static void InitFromToRay (void)
{
   int piece, fsq, tsq, f, t, n;
   BitBoard *b;

   memset (bitboards.FromToRay, 0, sizeof (bitboards.FromToRay));
   for (piece = bishop; piece <= rook; piece++)
   {
      for (fsq = 0; fsq < 120; fsq++)
      {
         if ((f = map[fsq]) == -1) continue;
         for (n = 0; n < ndir[piece]; n++)
         {
            tsq = fsq;
            t = map[tsq];
            do
            {
               b = &bitboards.FromToRay[f][t];
               tsq += dir[piece][n];         
               if ((t = map[tsq]) != -1)
               {
                  SETBIT (bitboards.FromToRay[f][t], t);
                  bitboards.FromToRay[f][t] |= *b;
               }
            } while (t != -1);
         }
      }
   }
}


static void InitRankFileBit (void)
{
   BitBoard b;
   int i;

   i = 8;
   b = (BitBoard) 255;
   while (i--)
   {
      bitboards.RankBit[i] = b;
      b <<= 8;
   }
   
   i = 8;
   b = ULL(0x0101010101010101);
   while (i--)
   {
      bitboards.FileBit[i] = b;
      b <<= 1;
   }
}

static void InitRandomMasks (void)
{
  bitboards.mask_kr_trapped_w[0]=bitboards.BitPosArray[H2];
  bitboards.mask_kr_trapped_w[1]=bitboards.BitPosArray[H1]|bitboards.BitPosArray[H2];
  bitboards.mask_kr_trapped_w[2]=bitboards.BitPosArray[G1]|bitboards.BitPosArray[H1]|bitboards.BitPosArray[H2];
  bitboards.mask_qr_trapped_w[0]=bitboards.BitPosArray[A2];
  bitboards.mask_qr_trapped_w[1]=bitboards.BitPosArray[A1]|bitboards.BitPosArray[A2];
  bitboards.mask_qr_trapped_w[2]=bitboards.BitPosArray[A1]|bitboards.BitPosArray[B1]|bitboards.BitPosArray[A2];
  bitboards.mask_kr_trapped_b[0]=bitboards.BitPosArray[H7];
  bitboards.mask_kr_trapped_b[1]=bitboards.BitPosArray[H8]|bitboards.BitPosArray[H7];
  bitboards.mask_kr_trapped_b[2]=bitboards.BitPosArray[H8]|bitboards.BitPosArray[G8]|bitboards.BitPosArray[H7];
  bitboards.mask_qr_trapped_b[0]=bitboards.BitPosArray[A7];
  bitboards.mask_qr_trapped_b[1]=bitboards.BitPosArray[A8]|bitboards.BitPosArray[A7];
  bitboards.mask_qr_trapped_b[2]=bitboards.BitPosArray[A8]|bitboards.BitPosArray[B8]|bitboards.BitPosArray[A7];


   SETBIT (bitboards.stonewall[white], D4); /* SMC */
   SETBIT (bitboards.stonewall[white], E3); /* SMC */
   SETBIT (bitboards.stonewall[white], F4); /* SMC */

   SETBIT (bitboards.stonewall[black], D5); /* SMC */
   SETBIT (bitboards.stonewall[black], E6); /* SMC */
   SETBIT (bitboards.stonewall[black], F5); /* SMC */

   bitboards.rings[0] = ULL(0x0000001818000000); /* 4 & 5 th file; 4 & 5 th rank */
   bitboards.rings[1] = ULL(0x00003C24243C0000); /* 3 & 6 th file; 3 & 6 th rank */
   bitboards.rings[2] = ULL(0x007E424242427E00); /* 2 & 7 th file; 2 & 7 th rank */
   bitboards.rings[3] = ULL(0xFF818181818181FF); /* 1 & 8 th file; 1 & 8 th rank */

   bitboards.boxes[0] = ULL(0x00003C3C3C3C0000); /* rings[0] | rings[1] */
   bitboards.boxes[1] = ULL(0x007E7E7E7E7E7E00); /* rings[0] | rings[1] | rings[2] */

   bitboards.boardhalf[white] = bitboards.RankBit[0]|bitboards.RankBit[1]|bitboards.RankBit[2]|bitboards.RankBit[3];
   bitboards.boardhalf[black] = bitboards.RankBit[4]|bitboards.RankBit[5]|bitboards.RankBit[6]|bitboards.RankBit[7];
   bitboards.boardside[ks] = bitboards.FileBit[4]|bitboards.FileBit[5]|bitboards.FileBit[6]|bitboards.FileBit[7];
   bitboards.boardside[qs] = bitboards.FileBit[0]|bitboards.FileBit[1]|bitboards.FileBit[2]|bitboards.FileBit[3];

   bitboards.WhiteSquares= ULL(0x55AA55AA55AA55AA);
   bitboards.BlackSquares= ULL(0xAA55AA55AA55AA55);

}

static void InitPassedPawnMask (void)
{
   unsigned int sq;

   memset (bitboards.PassedPawnMask, 0, sizeof (bitboards.PassedPawnMask));

   /*  Do for white pawns first */
   for (sq = 0; sq < 64; sq++)
   {
      bitboards.PassedPawnMask[white][sq] = bitboards.Ray[sq][7];
      if (FILE_(sq) != 0)
	bitboards.PassedPawnMask[white][sq] |= bitboards.Ray[sq-1][7];
      if (FILE_(sq) != 7)
         bitboards.PassedPawnMask[white][sq] |= bitboards.Ray[sq+1][7];
   }

   /*  Do for black pawns */
   for (sq = 0; sq < 64; sq++)
   {
     bitboards.PassedPawnMask[black][sq] = bitboards.Ray[sq][4];
      if (FILE_(sq) != 0)
         bitboards.PassedPawnMask[black][sq] |= bitboards.Ray[sq-1][4];
      if (FILE_(sq) != 7)
         bitboards.PassedPawnMask[black][sq] |= bitboards.Ray[sq+1][4];
   }
}

static void InitIsolaniMask (void)

/**************************************************************************
 *
 *  IMPORTANT:!!!!
 *  Make sure this routine is called AFTER InitRankFileBit().
 *
 **************************************************************************/
{
   int i;

   bitboards.IsolaniMask[0] = bitboards.FileBit[1];
   bitboards.IsolaniMask[7] = bitboards.FileBit[6];
   for (i = 1; i <= 6; i++)
      bitboards.IsolaniMask[i] = bitboards.FileBit[i-1] | bitboards.FileBit[i+1];
      
}

static void InitSquarePawnMask (void)
{
   unsigned int sq;
   int len, i, j;

   memset (bitboards.SquarePawnMask, 0, sizeof (bitboards.PassedPawnMask));
   for (sq = 0; sq < 64; sq++)
   {
      /*  White mask  */
      len = 7 - RANK (sq);
      i = MAX (sq & 56, sq - len);
      j = MIN (sq | 7, sq + len);
      while (i <= j)
      {
         bitboards.SquarePawnMask[white][sq] |= (bitboards.BitPosArray[i] | bitboards.FromToRay[i][i|56]);
         i++;
      }

      /*  Black mask  */
      len = RANK (sq);
      i = MAX (sq & 56, sq - len);
      j = MIN (sq | 7, sq + len);
      while (i <= j)
      {
         bitboards.SquarePawnMask[black][sq] |= (bitboards.BitPosArray[i] | bitboards.FromToRay[i][i&7]);
         i++;
      }
   }

   /*  For pawns on 2nd rank, they have same mask as pawns on 3rd rank */
   for (sq = A2; sq <= H2; sq++)
      bitboards.SquarePawnMask[white][sq] = bitboards.SquarePawnMask[white][sq+8];
   for (sq = A7; sq <= H7; sq++)
      bitboards.SquarePawnMask[black][sq] = bitboards.SquarePawnMask[black][sq-8];
}

 
void InitDistance (void)
/**************************************************************************
 *
 *  There are two arrays dealing with distances.  The distance[s1][s2]
 *  array gives the minimum number of moves a king takes to get from s1
 *  to s2.  The taxicab[s1][s2] array is the taxicab distance.  Example:
 *  distance[A1][B3] = 2;  taxicab[A1][B3] = 3;
 *
 *************************************************************************/
{
   int f, t, j;
   int d1, d2;

   for (f = 0; f < 64; f++)
     for (t = 0; t < 8; t++)
       bitboards.DistMap[f][t] = ULL(0);


   for (f = 0; f < 64; f++)
      for (t = f; t < 64; t++)
      {
         d1 = (t & 0x07) - (f & 0x07);
         if (d1 < 0) d1 = -d1;
         d2 = (t >> 3) - (f >> 3);
         if (d2 < 0) d2 = -d2;
         distance[f][t] = MAX (d1, d2);
         distance[t][f] = MAX (d1, d2);
         taxicab[f][t] = d1 + d2;
         taxicab[t][f] = d1 + d2;
      }

   for (f = 0; f < 64; f++)
     for (t = 0; t < 64; t++)
	 bitboards.DistMap[f][distance[t][f]] |= bitboards.BitPosArray[t];

   for (f = 0; f < 64; f++)
     for (t = 0; t < 8; t++)
       for (j = 0; j < t; j++)
	 bitboards.DistMap[f][t] |= bitboards.DistMap[f][j];
}

// Straight from the Chess Programming Wiki.

/**
 * Flip a bitboard vertically about the centre ranks.
 * Rank 1 is mapped to rank 8 and vice versa.
 * @param x any bitboard
 * @return bitboard x flipped vertically
 */

BitBoard BitBoardFlipVertical(BitBoard x){
    const BitBoard k1 = ULL(0x00FF00FF00FF00FF);
    const BitBoard k2 = ULL(0x0000FFFF0000FFFF);
    x = ((x >>  8) & k1) | ((x & k1) <<  8);
    x = ((x >> 16) & k2) | ((x & k2) << 16);
    x = ( x >> 32)       | ( x       << 32);
    return x;
}

/**
 * Mirror a bitboard horizontally about the center files.
 * File a is mapped to file h and vice versa.
 * @param x any bitboard
 * @return bitboard x mirrored horizontally
 */

BitBoard BitBoardFlipHorizontal (BitBoard x) {
   const BitBoard k1 = ULL(0x5555555555555555);
   const BitBoard k2 = ULL(0x3333333333333333);
   const BitBoard k4 = ULL(0x0f0f0f0f0f0f0f0f);
   x = ((x >> 1) & k1) | ((x & k1) << 1);
   x = ((x >> 2) & k2) | ((x & k2) << 2);
   x = ((x >> 4) & k4) | ((x & k4) << 4);
   return x;
}

void BitBoardShow (BitBoard b)
/*****************************************************************************
 *
 * Just to print a lousy ascii board  
 *
 *****************************************************************************/
{
   int r, c;
   for (r = 56; r >= 0; r -= 8)
   {
      for (c = 0; c < 8; c++)
      {
	  if (b & bitboards.BitPosArray[r + c]){
	      Output("1 ");
	  }else{
	      Output(". "); 
	  }
      }
      Output("\n");
   }
   Output("\n");
}


void BitBoardInit(){
   InitBitPosArray ();
   InitMoveArray ();
   InitRay ();
   InitFromToRay (); 
   InitRankFileBit ();
   InitPassedPawnMask ();
   InitIsolaniMask ();
   InitSquarePawnMask ();
   InitLzArray ();
   InitBitCount ();
   InitRandomMasks ();
   InitDistance ();
}
