
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "utils.h"
#include "chess.h"
#include "vectors.h"

#include "log.h"

void castling_to_str(const struct t_position position, char* str, const size_t str_size)
/*
  Format X-FEN.
  http://www.open-aurec.com/wbforum/viewtopic.php?f=4&t=3116
*/
{
  int x, n, i;
  char c;
  char c2[2];
  c2[1] = '\0';
  
  memset(str, '\0', str_size);
  
  if (position.white.k_rook_x != NIL) {
    x = 9; n = 0; c = UNDEFINED; i = xy2sqr(x, 0);
    while (x > 0 && position.board[i] != 'K' && c == UNDEFINED) {
      if (position.board[i] == 'R') n ++;
      if (n == 1 && x == position.white.k_rook_x) c = 'K';
      else if (n == 2 && x == position.white.k_rook_x) c = x + 'A';
      else { x --; i = xy2sqr(x, 0); }
    }
    c2[0] = c;
    strcat(str, c2);
  }
  
  if (position.white.q_rook_x != NIL) {
    x = 0; n = 0; c = UNDEFINED; i = xy2sqr(x, 0);
    while (x < 9 && position.board[i] != 'K' && c == UNDEFINED) {
      if (position.board[i] == 'R') n ++;
      if (n == 1 && x == position.white.q_rook_x) c = 'Q';
      else if (n == 2 && x == position.white.q_rook_x) c = x + 'A';
      else { x ++; i = xy2sqr(x, 0); }
    }
    c2[0] = c;
    strcat(str, c2);
  }
  
  if (position.black.k_rook_x != NIL) {
    x = 9; n = 0; c = UNDEFINED; i = xy2sqr(x, 7);
    while (x > 0 && position.board[i] != 'k' && c == UNDEFINED) {
      if (position.board[i] == 'r') n ++;
      if (n == 1 && x == position.black.k_rook_x) c = 'k';
      else if (n == 2 && x == position.black.k_rook_x) c = x + 'a';
      else { x --; i = xy2sqr(x, 7); }
    }
    c2[0] = c;
    strcat(str, c2);
  }
  
  if (position.black.q_rook_x != NIL) {
    x = 0; n = 0; c = UNDEFINED; i = xy2sqr(x, 7);
    while (x < 9 && position.board[i] != 'k' && c == UNDEFINED) {
      if (position.board[i] == 'r') n ++;
      if (n == 1 && x == position.black.q_rook_x) c = 'q';
      else if (n == 2 && x == position.black.q_rook_x) c = x + 'a';
      else { x ++; i = xy2sqr(x, 7); }
    }
    c2[0] = c;
    strcat(str, c2);
  }
  
  if (strlen(str) == 0)
  {
    strcpy(str, "-");
  }
}

void print_position(const struct t_position position)
{
  int i;
  
  for (i = 0; i < BOARD_SIZE; i++)
  {
    printf("%c", position.board[i]);
    
    if ((i + 1) % 15 == 0)
    {
      printf("\n");
    }
  }
  
  printf(
    "ACTIVE %c CASTLING %d, %d, %d, %d PASSANT %d HALFMOVE %d FULLMOVE %d\n",
    position.active,
    position.white.k_rook_x,
    position.white.q_rook_x,
    position.black.k_rook_x,
    position.black.q_rook_x,
    position.passant,
    position.halfmove,
    position.fullmove
  );
}

#define BOARD position.board

void prettyprint_position(const struct t_position position)
{
  char wksqr[3], bksqr[3];
  const char* chessboard_size[] = { "8x8", "10x8" };
  
  printf(
    "   o=======================================o\n"
    " 8 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   |---+---+---+---+---+---+---+---+---+---|\n"
    " 7 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   |---+---+---+---+---+---+---+---+---+---|\n"
    " 6 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   |---+---+---+---+---+---+---+---+---+---|\n"
    " 5 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   |---+---+---+---+---+---+---+---+---+---|\n"
    " 4 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   |---+---+---+---+---+---+---+---+---+---|\n"
    " 3 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   |---+---+---+---+---+---+---+---+---+---|\n"
    " 2 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   |---+---+---+---+---+---+---+---+---+---|\n"
    " 1 | %c | %c | %c | %c | %c | %c | %c | %c | %c | %c |\n"
    "   o=======================================o\n"
    "     A   B   C   D   E   F   G   H   I   J  \n",
    BOARD[ 42], BOARD[ 57], BOARD[ 72], BOARD[ 87], BOARD[102], BOARD[117], BOARD[132], BOARD[147], BOARD[162], BOARD[177],
    BOARD[ 41], BOARD[ 56], BOARD[ 71], BOARD[ 86], BOARD[101], BOARD[116], BOARD[131], BOARD[146], BOARD[161], BOARD[176],
    BOARD[ 40], BOARD[ 55], BOARD[ 70], BOARD[ 85], BOARD[100], BOARD[115], BOARD[130], BOARD[145], BOARD[160], BOARD[175],
    BOARD[ 39], BOARD[ 54], BOARD[ 69], BOARD[ 84], BOARD[ 99], BOARD[114], BOARD[129], BOARD[144], BOARD[159], BOARD[174],
    BOARD[ 38], BOARD[ 53], BOARD[ 68], BOARD[ 83], BOARD[ 98], BOARD[113], BOARD[128], BOARD[143], BOARD[158], BOARD[173],
    BOARD[ 37], BOARD[ 52], BOARD[ 67], BOARD[ 82], BOARD[ 97], BOARD[112], BOARD[127], BOARD[142], BOARD[157], BOARD[172],
    BOARD[ 36], BOARD[ 51], BOARD[ 66], BOARD[ 81], BOARD[ 96], BOARD[111], BOARD[126], BOARD[141], BOARD[156], BOARD[171],
    BOARD[ 35], BOARD[ 50], BOARD[ 65], BOARD[ 80], BOARD[ 95], BOARD[110], BOARD[125], BOARD[140], BOARD[155], BOARD[170]
  );
  
  sqr2str(position.white_king_square, wksqr, sizeof(wksqr));
  sqr2str(position.black_king_square, bksqr, sizeof(bksqr));
  
  printf(
    "ACTIVE %c CASTLING %d, %d, %d, %d PASSANT %d HALFMOVE %d FULLMOVE %d SIZE %s WKSQR %s BKSQR %s\n",
    position.active,
    position.white.k_rook_x,
    position.white.q_rook_x,
    position.black.k_rook_x,
    position.black.q_rook_x,
    position.passant,
    position.halfmove,
    position.fullmove,
    chessboard_size[position.board_10x8],
    wksqr,
    bksqr
  );
}

void erase_position(struct t_position* position)
{
  int i, x, y;
  
  memset(position->board, BORDER, BOARD_SIZE);
  
  for (i = 0; i < BOARD_SIZE; i++)
  {
    sqr2xy(i, &x, &y);
    
    if (x >= 0 && x <= 9 && y >= 0 && y <= 7)
    {
      (position->board)[i] = '/';
    }
  }
  
  position->active = UNDEFINED;

  position->white.k_rook_x = NIL;
  position->white.q_rook_x = NIL;
  position->black.k_rook_x = NIL;
  position->black.q_rook_x = NIL;
  
  position->passant = NIL;
  
  position->halfmove = 0;
  position->fullmove = 0;
  
  position->board_10x8 = 0;
  
  position->white_king_square = NIL;
  position->black_king_square = NIL;
}

void load_position(struct t_position* position, char placement[], char active, char castling[], char passant[], char halfmove[], char fullmove[])
{
  int i, x, y;
  char c;
  int king_x;
  char* ptr;
  int ii;
  
  memset(position->board, BORDER, BOARD_SIZE);
  position->white_king_square = NIL;
  position->black_king_square = NIL;
  
  x = 0;
  y = 7;
  
  for (i = 0; i < strlen(placement); i++)
  {
    c = placement[i];
    
    if (c >= '0' && c <= '9')
    {
      if (i < strlen(placement))
      {
        if (c == '1' && placement[i + 1] == '0')
        {
          continue;
        }
      }
      
      if (i > 0)
      {
        if (c == '0' && placement[i - 1] == '1')
        {
          (position->board)[15 * x + y + 35] = EMPTY_SQUARE;
          x ++;
          c = '9';
        }
      }
      
      while (c > '0')
      {
        (position->board)[15 * x + y + 35] = EMPTY_SQUARE;
        x ++;
        c --;
      }
    }
    else if (c == '/')
    {
      if (y == 7)
      {
        if (x == 8)
        {
          position->board_10x8 = 0;
        }
        else  if (x == 10)
        {
          position->board_10x8 = 1;
        }
        else
        {
          LOG_LINE("WARN Unexpected squares number [%d] (%s, line %d)\n", x, __FILE__, __LINE__)
        }
      }
      x = 0;
      y --;
    }
    else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
    {
      (position->board)[15 * x + y + 35] = c;
      
      if (c == 'K')
      {
        position->white_king_square = 15 * x + y + 35;
      }
      else
      if (c == 'k')
      {
        position->black_king_square = 15 * x + y + 35;
      }
      
      x ++;
    }
  }
  
  position->active = active;

  position->white.k_rook_x = NIL;
  position->white.q_rook_x = NIL;
  position->black.k_rook_x = NIL;
  position->black.q_rook_x = NIL;
  
  if (position->white_king_square == NIL || position->black_king_square == NIL)
  {
    LOG_LINE("ERROR Missing king (%s, line %d)\n", __FILE__, __LINE__)
    return;
  }
  
  /*
  king_x = NIL;
  x = 1; while (x < 9 && king_x == NIL) { i = xy2sqr(x, 0); if (position->board[i] == 'K') { king_x = x; } else { x ++; } }
  */
  king_x = (sqr2y(position->white_king_square) == 0) ? sqr2x(position->white_king_square) : NIL;
  if (king_x != NIL)
  {
    for (i = 0; i < strlen(castling); i ++)
    {
      ptr = strchr("ABCDEFGHIJ", castling[i]);
      if (ptr != NULL) { x = ptr[0] - 'A'; if (x < king_x) { position->white.q_rook_x = x; } else { position->white.k_rook_x = x; } }
      else
      {
        if (castling[i] == 'K') { x = 9; ii = xy2sqr(x, 0); while (x > king_x && position->board[ii] != 'R') { x --; ii = xy2sqr(x, 0); } if (x > king_x) { position->white.k_rook_x = x; } else { LOG_LINE("ERROR Cannot find white king's rook (%s, line %d)\n", __FILE__, __LINE__) } } else
        if (castling[i] == 'Q') { x = 0; ii = xy2sqr(x, 0); while (x < king_x && position->board[ii] != 'R') { x ++; ii = xy2sqr(x, 0); } if (x < king_x) { position->white.q_rook_x = x; } else { LOG_LINE("ERROR Cannot find white queen's rook (%s, line %d)\n", __FILE__, __LINE__) } }
      }
    }
  }
  
  /*
  king_x = NIL;
  x = 1; while (x < 9 && king_x == NIL) { i = xy2sqr(x, 7); if (position->board[i] == 'k') { king_x = x; } else { x ++; } }
  */
  king_x = (sqr2y(position->black_king_square) == 7) ? sqr2x(position->black_king_square) : NIL;
  if (king_x != NIL)
  {
    for (i = 0; i < strlen(castling); i ++)
    {
      ptr = strchr("abcdefghij", castling[i]);
      if (ptr != NULL) { x = ptr[0] - 'a'; if (x < king_x) { position->black.q_rook_x = x; } else { position->black.k_rook_x = x; } }
      else
      {
        if (castling[i] == 'k') { x = 9; ii = xy2sqr(x, 7); while (x > king_x && position->board[ii] != 'r') { x --; ii = xy2sqr(x, 7); } if (x > king_x) { position->black.k_rook_x = x; } else { LOG_LINE("ERROR Cannot find black king's rook (%s, line %d)\n", __FILE__, __LINE__) } } else
        if (castling[i] == 'q') { x = 0; ii = xy2sqr(x, 7); while (x < king_x && position->board[ii] != 'r') { x ++; ii = xy2sqr(x, 7); } if (x < king_x) { position->black.q_rook_x = x; } else { LOG_LINE("ERROR Cannot find black queen's rook (%s, line %d)\n", __FILE__, __LINE__) } }
      }
    }
  }
  
  position->passant = str2sqr(passant);
  
  position->halfmove = atoi(halfmove);
  position->fullmove = atoi(fullmove);
}

void load_position_from_fen(struct t_position* position, const char fen[])
{
  const char sep[2] = " ";
  char* token;
  char* tokens[128];
  char aux[128];
  int i = 0;
  
  strcpy(aux, fen);
  token = strtok(aux, sep);
  while (token != NULL)
  {
    tokens[i++] = token;
    LOG_LINE("DEBUG FEN(%d) = [%s] (%s, line %d)\n", i, token, __FILE__, __LINE__)
    token = strtok(NULL, sep);
  }
  
  if (i == 6)
  {
    load_position(position, tokens[0], tokens[1][0], tokens[2], tokens[3], tokens[4], tokens[5]);
  }
  else if (i == 4)
  {
    load_position(position, tokens[0], tokens[1][0], tokens[2], tokens[3], NULL, NULL);
  }
  else
  {
    LOG_LINE("ERROR Cannot load FEN [%s] (%s, line %d)\n", fen, __FILE__, __LINE__)
  }
}

void get_fen(const struct t_position position, char* str, const size_t str_size)
{
  char placement_str[128];
  char castling_str[5];
  char passant_str[3];
  int x, y, i, n;
  char c;
  char c2[2];
  
  n = 0;
  c2[1] = '\0';
  memset(placement_str, '\0', sizeof(placement_str));
  memset(str, '\0', str_size);
  
  for (y = 7; y >= 0; y --)
  {
    for (x = 0; x <= 9; x ++)
    {
      i = xy2sqr(x, y);
      c = position.board[i];
      if (c == EMPTY_SQUARE)
      {
        n ++;
        if (position.board[xy2sqr(x + 1, y)] != EMPTY_SQUARE)
        {
          if (n == 10)
          {
            strcat(placement_str, "10");
          }
          else
          {
            c2[0] = n + '0';
            strcat(placement_str, c2);
          }
          n = 0;
        }
      }
      else if (c != BORDER)
      {
        c2[0] = c;
        strcat(placement_str, c2);
      }
    }
    
    if (y > 0)
    {
      strcat(placement_str, "/");
    }
  }
  
  castling_to_str(position, castling_str, sizeof(castling_str));
  
  if (position.passant == NIL)
  {
    strcpy(passant_str, "-");
  }
  else
  {
    sqr2str(position.passant, passant_str, sizeof(passant_str));
  }
  
  sprintf(str, "%s %c %s %s %d %d", placement_str, position.active, castling_str, passant_str, position.halfmove, position.fullmove);
}

struct t_move encode_move(const unsigned char from, const unsigned char to, const unsigned char capture, const char promotion)
{
  struct t_move move;
  move.from = from;
  move.to = to;
  move.capture = capture;
  move.promotion = promotion;
  return move;
}

void move2str(const struct t_move move, char* str, const size_t str_size)
{
  char sqr[3];
  char c2[2];
  
  sqr2str(move.from, sqr, sizeof(sqr));
  strcpy(str, sqr);
  sqr2str(move.to, sqr, sizeof(sqr));
  strcat(str, sqr);
  
  if (move.promotion != '\0')
  {
    c2[0] = move.promotion;
    c2[1] = '\0';
    strcat(str, c2);
  }
}

#define APPEND_MOVE(A, B, C, D) move = encode_move(A, B, C, D); moves[movecount ++] = move; if (movecount == array_size) { return movecount; }

#define GENERATE_BISHOP_MOVES \
for (vi = 0; vi <= 3; vi ++)\
{\
  v = vector_bishop[vi]; dest = i + v;\
  while (position.board[dest] != BORDER && (position.board[dest] == EMPTY_SQUARE || (*color2)(position.board[dest]))) {\
    if (options == ALL_MOVES || ((options & CHECK_DETECTION) == CHECK_DETECTION && toupper(position.board[dest]) == 'K') || ((options & CASTLING_CHECK_DETECTION) == CASTLING_CHECK_DETECTION && ((position.active == 'w' && sqr2y(dest) == 7) || (position.active == 'b' && sqr2y(dest) == 0))))\
    {\
      APPEND_MOVE(i, dest, (position.board[dest] != EMPTY_SQUARE), '\0')\
    }\
    if (position.board[dest] != EMPTY_SQUARE)\
      break;\
    else\
      dest += v;\
  }\
}

#define GENERATE_ROOK_MOVES \
for (vi = 0; vi <= 3; vi ++)\
{\
  v = vector_rook[vi]; dest = i + v;\
  while (position.board[dest] != BORDER && (position.board[dest] == EMPTY_SQUARE || (*color2)(position.board[dest]))) {\
    if (options == ALL_MOVES || ((options & CHECK_DETECTION) == CHECK_DETECTION && toupper(position.board[dest]) == 'K') || ((options & CASTLING_CHECK_DETECTION) == CASTLING_CHECK_DETECTION && ((position.active == 'w' && sqr2y(dest) == 7) || (position.active == 'b' && sqr2y(dest) == 0))))\
    {\
      APPEND_MOVE(i, dest, (position.board[dest] != EMPTY_SQUARE), '\0')\
    }\
    if (position.board[dest] != EMPTY_SQUARE)\
      break;\
    else\
      dest += v;\
  }\
}

#define GENERATE_KNIGHT_MOVES \
for (vi = 0; vi <= 7; vi ++)\
{\
  v = vector_knight[vi]; dest = i + v;\
  if (position.board[dest] != BORDER && (position.board[dest] == EMPTY_SQUARE || (*color2)(position.board[dest]))) {\
    if (options == ALL_MOVES || ((options & CHECK_DETECTION) == CHECK_DETECTION && toupper(position.board[dest]) == 'K') || ((options & CASTLING_CHECK_DETECTION) == CASTLING_CHECK_DETECTION && ((position.active == 'w' && sqr2y(dest) == 7) || (position.active == 'b' && sqr2y(dest) == 0))))\
    {\
      APPEND_MOVE(i, dest, (position.board[dest] != EMPTY_SQUARE), '\0')\
    }\
  }\
}

int generate_moves(const struct t_position position, const int options, struct t_move* moves, size_t array_size)
{
  int i, x, y;
  char c;
  int (*color1)(int);
  int (*color2)(int);
  int vi, v, dest, dest_y, capt;
  struct t_move move;
  int movecount = 0;
  
  color1 = (position.active == 'w') ? &isupper : &islower;
  color2 = (position.active == 'b') ? &isupper : &islower;
  
  i = 35;
  
  for (x = 0; x <= 9; x ++)
  {
    for (y = 0; y <= 7; y ++)
    {
      if (position.board[i] != EMPTY_SQUARE)
      {
        c = position.board[i];
        
        if ((*color1)(c))
        {
          switch (toupper(c))
          {
            case 'A': /* archbishop = bishop + knight */
              GENERATE_BISHOP_MOVES
              GENERATE_KNIGHT_MOVES
              break;
            case 'B':
              GENERATE_BISHOP_MOVES
              break;
            case 'C': /* chancellor = rook + knight */
              GENERATE_ROOK_MOVES
              GENERATE_KNIGHT_MOVES
              break;
            case 'K':
              for (vi = 0; vi <= 3; vi ++)
              {
                v = vector_rook[vi]; dest = i + v;
                if (position.board[dest] != BORDER && (position.board[dest] == EMPTY_SQUARE || (*color2)(position.board[dest])))
                {
                  if (options == ALL_MOVES || ((options & CHECK_DETECTION) == CHECK_DETECTION && toupper(position.board[dest]) == 'K') || ((options & CASTLING_CHECK_DETECTION) == CASTLING_CHECK_DETECTION && ((position.active == 'w' && sqr2y(dest) == 7) || (position.active == 'b' && sqr2y(dest) == 0))))
                  {
                    APPEND_MOVE(i, dest, (position.board[dest] != EMPTY_SQUARE), '\0')
                  }
                }
              }
              for (vi = 0; vi <= 3; vi ++)
              {
                v = vector_bishop[vi]; dest = i + v;
                if (position.board[dest] != BORDER && (position.board[dest] == EMPTY_SQUARE || (*color2)(position.board[dest])))
                {
                  if (options == ALL_MOVES || ((options & CHECK_DETECTION) == CHECK_DETECTION && toupper(position.board[dest]) == 'K') || ((options & CASTLING_CHECK_DETECTION) == CASTLING_CHECK_DETECTION && ((position.active == 'w' && sqr2y(dest) == 7) || (position.active == 'b' && sqr2y(dest) == 0))))
                  {
                    APPEND_MOVE(i, dest, (position.board[dest] != EMPTY_SQUARE), '\0')
                  }
                }
              }
              break;
            case 'N':
              GENERATE_KNIGHT_MOVES
              break;
            case 'P':
              switch (c)
              {
                case 'P':
                  v = vector_white_pawn[0];
                  if (position.board[i + v] == EMPTY_SQUARE && options == ALL_MOVES)
                  {
                    if (sqr2y(i + v) == 7)
                    {
                      APPEND_MOVE(i, i + v, 0, 'n')
                      APPEND_MOVE(i, i + v, 0, 'b')
                      APPEND_MOVE(i, i + v, 0, 'r')
                      APPEND_MOVE(i, i + v, 0, 'q')
                      if (position.board_10x8)
                      {
                        APPEND_MOVE(i, i + v, 0, 'a')
                        APPEND_MOVE(i, i + v, 0, 'c')
                      }
                    }
                    else
                    {
                      APPEND_MOVE(i, i + v, 0, '\0')
                    }
                    if (sqr2y(i) == 1 && position.board[i + v + v] == EMPTY_SQUARE) { APPEND_MOVE(i, i + v + v, 0, '\0'); }
                  }
                  for (vi = 1; vi <= 2; vi ++)
                  {
                    v = vector_white_pawn[vi]; dest = i + v; dest_y = sqr2y(dest); capt = (dest == position.passant || (*color2)(position.board[dest]));
                    if (position.board[dest] != BORDER && (capt || (dest_y == 7 && (options & CASTLING_CHECK_DETECTION) == CASTLING_CHECK_DETECTION)))
                    {
                      if (dest_y == 7)
                      {
                        APPEND_MOVE(i, dest, capt, 'n')
                        APPEND_MOVE(i, dest, capt, 'b')
                        APPEND_MOVE(i, dest, capt, 'r')
                        APPEND_MOVE(i, dest, capt, 'q')
                        if (position.board_10x8)
                        {
                          APPEND_MOVE(i, dest, capt, 'a')
                          APPEND_MOVE(i, dest, capt, 'c')
                        }
                      }
                      else
                      {
                        APPEND_MOVE(i, dest, capt, '\0')
                      }
                    }
                  }
                  break;
                case 'p':
                  v = vector_black_pawn[0];
                  if (position.board[i + v] == EMPTY_SQUARE && options == ALL_MOVES)
                  {
                    if (sqr2y(i + v) == 0)
                    {
                      APPEND_MOVE(i, i + v, 0, 'n')
                      APPEND_MOVE(i, i + v, 0, 'b')
                      APPEND_MOVE(i, i + v, 0, 'r')
                      APPEND_MOVE(i, i + v, 0, 'q')
                      if (position.board_10x8)
                      {
                        APPEND_MOVE(i, i + v, 0, 'a')
                        APPEND_MOVE(i, i + v, 0, 'c')
                      }
                    }
                    else
                    {
                      APPEND_MOVE(i, i + v, 0, '\0')
                    }
                    if (sqr2y(i) == 6 && position.board[i + v + v] == EMPTY_SQUARE) { APPEND_MOVE(i, i + v + v, 0, '\0'); }
                  }
                  for (vi = 1; vi <= 2; vi ++)
                  {
                    v = vector_black_pawn[vi]; dest = i + v; dest_y = sqr2y(dest); capt = (dest == position.passant || (*color2)(position.board[dest]));
                    if (position.board[dest] != BORDER && (capt || (dest_y == 0 && (options & CASTLING_CHECK_DETECTION) == CASTLING_CHECK_DETECTION)))
                    {
                      if (dest_y == 0)
                      {
                        APPEND_MOVE(i, dest, 1, 'n')
                        APPEND_MOVE(i, dest, 1, 'b')
                        APPEND_MOVE(i, dest, 1, 'r')
                        APPEND_MOVE(i, dest, 1, 'q')
                        if (position.board_10x8)
                        {
                          APPEND_MOVE(i, dest, 1, 'a')
                          APPEND_MOVE(i, dest, 1, 'c')
                        }
                      }
                      else
                      {
                        APPEND_MOVE(i, dest, 1, '\0')
                      }
                    }
                  }
                  break;
              }
              break;
            case 'Q':
              GENERATE_ROOK_MOVES
              GENERATE_BISHOP_MOVES
              break;
            case 'R':
              GENERATE_ROOK_MOVES
              break;
            default:
              LOG_LINE("WARN Unknown piece [%c] (%s, line %d)", c, __FILE__, __LINE__)
              break;
          }
        }
      }
      i ++;
    }
    i += 7;
    if (position.board[i] == BORDER)
    {
      break;
    }
  }
  
  return movecount;
}

struct t_position_info get_position_info(struct t_position* position, const int detect_only_check)
{
  struct t_move moves[128];
  int movecount, i;
  //char move_str[6];
  struct t_castling castling;
  int king_x, king_y, x;
  char king_c;
  int king_dest_x;
  int rook_dest_x;
  int path_start_x;
  int path_end_x;
  struct t_position_info result;
  //char sqr_str[3];
  int options;
  
  result.check = 0;
  
  castling = (position->active == 'w') ? position->white : position->black;
  
  result.k_castling_possible = (castling.k_rook_x != NIL);
  result.q_castling_possible = (castling.q_rook_x != NIL);
  
  result.king_square = (position->active == 'w') ? position->white_king_square : position->black_king_square;
  
  king_y = (position->active == 'w') ? 0 : 7;
  king_c = (position->active == 'w') ? 'K' : 'k';
  
  position->active = (position->active == 'w') ? 'b' : 'w';
  
  options = (detect_only_check) ? CHECK_DETECTION : CHECK_DETECTION | CASTLING_CHECK_DETECTION;
  
  movecount = generate_moves(*position, options, moves, sizeof(moves) / sizeof(struct t_move));
  
  //~ if (detect_only_check)
  //~ {
    //~ if (movecount > 0)
    //~ {
      //~ result.check = 1;
    //~ }
  //~ }
  //~ else
  //~ {
    for (i = 0; i < movecount; i ++)
    {
      if (position->board[moves[i].to] == king_c)
      {
        result.check = 1;
        break;
      }
    }
  //~ }
  
  if (detect_only_check)
  {
    position->active = (position->active == 'w') ? 'b' : 'w';
    
    return result;
  }
  
  if (castling.k_rook_x != NIL || castling.q_rook_x != NIL)
  {
    king_x = NIL;
    for (x = 0; x <= 9; x ++)
    {
      if (position->board[xy2sqr(x, king_y)] == king_c) { king_x = x; break; }
    }
    if (king_x == NIL)
    {
      LOG_LINE("ERROR Cannot find king (%s, line %d)\n", __FILE__, __LINE__)
      result.k_castling_possible = 0;
      result.q_castling_possible = 0;
    }
    else
    {
      result.king_square = xy2sqr(king_x, king_y);
      /*
      fprintf(stderr, "DEBUG line %d king_x = %d\n", __LINE__, king_x);
      */
      if (castling.k_rook_x != NIL)
      {
        king_dest_x = (position->board_10x8) ? 8 : 6;
        rook_dest_x = (position->board_10x8) ? 7 : 5;
        
        path_start_x = (king_x < rook_dest_x) ? king_x : rook_dest_x;
        path_end_x = (castling.k_rook_x > king_dest_x) ? castling.k_rook_x : king_dest_x;
        /*
        fprintf(stderr, "DEBUG line %d path_start_x = %d path_end_x = %d\n", __LINE__, path_start_x, path_end_x);
        */
        for (x = path_start_x; x <= path_end_x; x ++)
        {
          if (x != king_x && x != castling.k_rook_x && position->board[xy2sqr(x, king_y)] != EMPTY_SQUARE)
          {
            /*
            sqr2str(xy2sqr(x, king_y), sqr_str, sizeof(sqr_str));
            fprintf(stderr, "DEBUG castling impossible, path occupied (%s)\n", sqr_str);
            */
            result.k_castling_possible = 0;
            break;
          }
        }
        
        path_start_x = king_x;
        path_end_x = king_dest_x;
        /*
        fprintf(stderr, "DEBUG line %d path_start_x = %d path_end_x = %d\n", __LINE__, path_start_x, path_end_x);
        */
        for (i = 0; i < movecount; i ++)
        {
          if (sqr2y(moves[i].to) == king_y)
          {
            x = sqr2x(moves[i].to);
            if (x >= path_start_x && x <= path_end_x)
            {
              /*
              move2str(moves[i], move_str, sizeof(move_str));
              fprintf(stderr, "DEBUG castling impossible, path attacked (%s)\n", move_str);
              */
              result.k_castling_possible = 0;
              break;
            }
          }
        }
      }
      if (castling.q_rook_x != NIL)
      {
        king_dest_x = 2;
        rook_dest_x = 3;
        
        path_start_x = (castling.q_rook_x < king_dest_x) ? castling.q_rook_x : king_dest_x;
        path_end_x = (king_x > rook_dest_x) ? king_x : rook_dest_x;
        /*
        fprintf(stderr, "DEBUG line %d path_start_x = %d path_end_x = %d\n", __LINE__, path_start_x, path_end_x);
        */
        for (x = path_start_x; x <= path_end_x; x ++)
        {
          if (x != king_x && x != castling.q_rook_x && position->board[xy2sqr(x, king_y)] != EMPTY_SQUARE)
          {
            /*
            sqr2str(xy2sqr(x, king_y), sqr_str, sizeof(sqr_str));
            fprintf(stderr, "DEBUG castling impossible, path occupied (%s)\n", sqr_str);
            */
            result.q_castling_possible = 0;
            break;
          }
        }
        
        path_start_x = (king_dest_x < king_x) ? king_dest_x : king_x;
        path_end_x = (king_x > king_dest_x) ? king_x : king_dest_x;
        /*
        fprintf(stderr, "DEBUG line %d path_start_x = %d path_end_x = %d\n", __LINE__, path_start_x, path_end_x);
        */
        for (i = 0; i < movecount; i ++)
        {
          if (sqr2y(moves[i].to) == king_y)
          {
            x = sqr2x(moves[i].to);
            if (x >= path_start_x && x <= path_end_x)
            {
              /*
              move2str(moves[i], move_str, sizeof(move_str));
              fprintf(stderr, "DEBUG castling impossible, path attacked (%s)\n", move_str);
              */
              result.q_castling_possible = 0;
              break;
            }
          }
        }
      }
    }
  }
  
  position->active = (position->active == 'w') ? 'b' : 'w';
  
  return result;
}

int do_move(struct t_move move, struct t_position* position)
{
  const int A8 =  42;
  const int A1 =  35;
  const int E8 = 102;
  const int E1 =  95;
  const int F8 = 117;
  const int F1 = 110; 
  const int H8 = 147;
  const int H1 = 140;
  const int J8 = 177;
  const int J1 = 170;
  
  int x1, y1, x2, y2;
  int kdest, rdest;
  int vect;
  
  vect = move.to - move.from;
  
  if (position->board[move.from] == 'K' || position->board[move.from] == 'k')
  {
    if ((abs(vect) == 30 || abs(vect) == 45) && position->board[move.to] == EMPTY_SQUARE) /* Roque variantes traditionnelle ou Capablanca */
    {
      if (position->active == 'w')
      {
        if (move.from == E1) /* Traditionnelle */
        {
          move.to = (vect > 0) ? H1 : A1;
        } else if (move.from == F1) /* Capablanca */
        {
          move.to = (vect > 0) ? J1 : A1;
        }
      }
      else
      {
        if (move.from == E8)
        {
          move.to = (vect > 0) ? H8 : A8;
        } else if (move.from == F8)
        {
          move.to = (vect > 0) ? J8 : A8;
        }
      }
    }
  }
  
  sqr2xy(move.from, &x1, &y1);
  sqr2xy(move.to, &x2, &y2);
  
  if (position->board[move.from] == 'P' || position->board[move.from] == 'p' || position->board[move.to] != EMPTY_SQUARE)
  {
    position->halfmove = 0;
  }
  else
  {
    position->halfmove ++;
  }
  
  if (position->board[move.from] == 'P' || position->board[move.from] == 'p')
  {
    if (abs(y2 - y1) == 2)
    {
      position->passant = move.from + (y2 - y1) / 2;
    }
    else
    {
      if (x2 != x1 && move.to == position->passant)
      {
        position->board[xy2sqr(x2, y1)] = EMPTY_SQUARE;
      }
      else if (y2 == 7 || y2 == 0)
      {
        if (move.promotion == '\0')
        {
          LOG_LINE("WARN Missing promotion value (%s, line %d)\n", __FILE__, __LINE__)
          position->board[move.from] = (y2 == 7) ? 'Q' : 'q';
        }
        else
        {
          position->board[move.from] = (y2 == 7) ? toupper(move.promotion) : move.promotion;
        }
      }
      
      position->passant = NIL;
    }
  }
  else
  {
    position->passant = NIL;
  }
  
  if ((position->board[move.from] == 'R' && y1 == 0) || (position->board[move.from] == 'r' && y1 == 7))
  {
    if (position->board[move.from] == 'R')
    {
      if (x1 == position->white.k_rook_x)
      {
        position->white.k_rook_x = NIL;
      }
      else if (x1 == position->white.q_rook_x)
      {
        position->white.q_rook_x = NIL;
      }
    }
    else
    {
      if (x1 == position->black.k_rook_x)
      {
        position->black.k_rook_x = NIL;
      }
      else if (x1 == position->black.q_rook_x)
      {
        position->black.q_rook_x = NIL;
      }
    }
  }
  
  if (position->board[move.to] == 'R' && position->board[move.from] != 'K' && y2 == 0)
  {
    if (x2 == position->white.k_rook_x) position->white.k_rook_x = NIL;
    if (x2 == position->white.q_rook_x) position->white.q_rook_x = NIL;
  }
  else if (position->board[move.to] == 'r' && position->board[move.from] != 'k' && y2 == 7)
  {
    if (x2 == position->black.k_rook_x) position->black.k_rook_x = NIL;
    if (x2 == position->black.q_rook_x) position->black.q_rook_x = NIL;
  }
  
  if (position->board[move.from] == 'K' || position->board[move.from] == 'k')
  {
    if (position->board[move.from] == 'K')
    {
      position->white.k_rook_x = NIL;
      position->white.q_rook_x = NIL;
      
      if (position->board[move.to] == 'R')
      {
        position->board[move.from] = EMPTY_SQUARE;
        position->board[move.to] = EMPTY_SQUARE;
        
        if (x2 > x1)
        {
          kdest = xy2sqr((position->board_10x8) ? 8 : 6, y1);
          rdest = xy2sqr((position->board_10x8) ? 7 : 5, y1);
        }
        else
        {
          kdest = xy2sqr(2, y1);
          rdest = xy2sqr(3, y1);
        }
        
        position->board[kdest] = 'K';
        position->board[rdest] = 'R';
        position->white_king_square = kdest;
      }
      else
      {
        position->board[move.to] = position->board[move.from];
        position->board[move.from] = EMPTY_SQUARE;
        position->white_king_square = move.to;
      }
    }
    else
    {
      position->black.k_rook_x = NIL;
      position->black.q_rook_x = NIL;
      
      if (position->board[move.to] == 'r')
      {
        position->board[move.from] = EMPTY_SQUARE;
        position->board[move.to] = EMPTY_SQUARE;
        
        if (x2 > x1)
        {
          kdest = xy2sqr((position->board_10x8) ? 8 : 6, y1);
          rdest = xy2sqr((position->board_10x8) ? 7 : 5, y1);
        }
        else
        {
          kdest = xy2sqr(2, y1);
          rdest = xy2sqr(3, y1);
        }
        
        position->board[kdest] = 'k';
        position->board[rdest] = 'r';
        position->black_king_square = kdest;
      }
      else
      {
        position->board[move.to] = position->board[move.from];
        position->board[move.from] = EMPTY_SQUARE;
        position->black_king_square = move.to;
      }
    }
  }
  else
  {
    position->board[move.to] = position->board[move.from];
    position->board[move.from] = EMPTY_SQUARE;
  }
  
  if (position->active == 'w')
  {
    position->active = 'b';
  }
  else
  {
    position->active = 'w';
    position->fullmove ++;
  }
  
  return 0;
}

int generate_castling(const struct t_position position, const struct t_position_info info, struct t_move* moves, size_t array_size)
{
  struct t_castling castling;
  struct t_move move;
  int movecount = 0;
  
  castling = (position.active == 'w') ? position.white : position.black;
  
  if (info.k_castling_possible) { APPEND_MOVE(info.king_square, xy2sqr(castling.k_rook_x, sqr2y(info.king_square)), CASTLING, '\0') }
  if (info.q_castling_possible) { APPEND_MOVE(info.king_square, xy2sqr(castling.q_rook_x, sqr2y(info.king_square)), CASTLING, '\0') }
    
  return movecount;
}

int generate_legal_moves(const struct t_position position, const struct t_position_info info, struct t_move* moves, size_t array_size, const int one_move)
{
  struct t_move moves1[256];
  struct t_position position1;
  int movecount, movecount1, i;
  //char move_str[6];
  struct t_position_info info1;
  
  movecount = 0;
  movecount1 = generate_castling(position, info, moves1, sizeof(moves1) / sizeof(struct t_move));
  //printf("movecount1 %d\n", movecount1);
  
  for (i = 0; i < movecount1; i ++)
  {
    moves[movecount ++] = moves1[i];
    if (movecount == array_size)
    {
      return movecount;
    }
  }
  
  movecount1 = generate_moves(position, ALL_MOVES, moves1, sizeof(moves1) / sizeof(struct t_move));
  
  for (i = 0; i < movecount1; i ++)
  {
    position1 = position;
    
    do_move(moves1[i], &position1);
    
    position1.active = (position1.active == 'w') ? 'b' : 'w';
    
    info1 = get_position_info(&position1, 1);
    
    if (info1.check)
    {
      /*
      move2str(moves1[i], move_str, sizeof(move_str));
      fprintf(stderr, "DEBUG %s is not legal\n", move_str);
      */
    }
    else
    {
      moves[movecount ++] = moves1[i];
      if (movecount == array_size || one_move)
      {
        return movecount;
      }
    }
  }
  
  return movecount;
}

struct t_move str2move(const char str[])
{
  struct t_move result;
  result.from = xy2sqr(str[0] - 'a', str[1] - '1');
  result.to   = xy2sqr(str[2] - 'a', str[3] - '1');
  result.capture = 0;
  result.promotion = (strlen(str) > 4) ? str[4] : '\0';
  return result;
}

void print_info(const struct t_position_info info)
{
  char sqr_str[4];
  if (info.king_square == NIL)
  {
    sqr_str[0] = 'n';
    sqr_str[1] = 'i';
    sqr_str[2] = 'l';
    sqr_str[3] = '\0';
  }
  else
  {
    sqr2str(info.king_square, sqr_str, sizeof(sqr_str));
  }
  printf("check %d k_castling_possible %d q_castling_possible %d king_square %s\n", info.check, info.k_castling_possible, info.q_castling_possible, sqr_str);
}

void castling2str(const struct t_move move, char* str, const size_t str_size, const enum chess_variant variant)
{
  char sqr[3];
  int king_x, rook_x, king_x2;
  char str1[5];
  
  king_x = sqr2x(move.from);
  rook_x = sqr2x(move.to);
  
  sqr2str(move.from, sqr, sizeof(sqr));
  strcpy(str, sqr);
  strcpy(str1, sqr);
  
  if (variant == cv_normal || variant == cv_undefined)
  {
    king_x2 = rook_x > king_x ? 6 : 2;
  }
  else if (variant == cv_capablanca)
  {
    king_x2 = rook_x > king_x ? 8 : 2;
  }
  else
  {
    king_x2 = rook_x;
  }
  
  sqr2str(move.to, sqr, sizeof(sqr));
  strcat(str1, sqr);
  
  sqr2str(xy2sqr(king_x2, sqr2y(move.from)), sqr, sizeof(sqr));
  strcat(str, sqr);
  
  LOG_LINE("DEBUG castling2str %s -> %s (variant %d)\n", str1, str, variant)
}
