#include "chess.h"
#include "data.h"
/* last modified 01/17/09 */
/*
 *******************************************************************************
 *                                                                             *
 *   InputMove() is responsible for converting a move from a text string to    *
 *   the internal move format.  It allows the so-called "reduced algebraic     *
 *   move format" which makes the origin square optional unless required for   *
 *   clarity.  It also accepts as little as required to remove ambiguity from  *
 *   the move, by using GenerateMoves() to produce a set of legal moves        *
 *   that the text can be applied against to eliminate those moves not         *
 *   intended.  Hopefully, only one move will remain after the elimination     *
 *   and legality checks.                                                      *
 *                                                                             *
 *******************************************************************************
 */
int InputMove(TREE * RESTRICT tree, char *text, int ply, int wtm, int silent,
    int ponder_list) {
  int moves[220], *mv, *mvp, *goodmove = 0;
  int piece = -1, capture, promote, give_check;
  int ffile, frank, tfile, trank;
  int current, i, nleft;
  char *goodchar, *tc;
  char movetext[128];
  static const char pieces[15] =
      { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
    'Q', 'q', 'K', 'k', '\0'
  };
  static const char pro_pieces[15] =
      { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r', 'Q', 'q',
    'K', 'k', '\0'
  };
/*
 ************************************************************
 *                                                          *
 *   First, we need to strip off the special characters for *
 *   check, mate, bad move, good move, and such that might  *
 *   come from a PGN input file.                            *
 *                                                          *
 ************************************************************
 */
  if ((tc = strchr(text, '!')))
    *tc = 0;
  if ((tc = strchr(text, '?')))
    *tc = 0;
/*
 ************************************************************
 *                                                          *
 *   Check for full coordinate input (f1e1) and handle that *
 *   if needed.                                             *
 *                                                          *
 ************************************************************
 */
  if (strlen(text) == 0)
    return (0);
  if ((text[0] >= 'a') && (text[0] <= 'h') && (text[1] >= '1') &&
      (text[1] <= '8') && (text[2] >= 'a') && (text[2] <= 'h') &&
      (text[3] >= '1') && (text[3] <= '8'))
    return (InputMoveICS(tree, text, ply, wtm, silent, ponder_list));
/*
 ************************************************************
 *                                                          *
 *   Initialize move structure.  If we discover a parsing   *
 *   error, this will cause us to return a move of "0" to   *
 *   indicate some sort of error was detected.              *
 *                                                          *
 ************************************************************
 */
  tree->position[MAXPLY] = tree->position[ply];
  strcpy(movetext, text);
  moves[0] = 0;
  piece = 0;
  capture = 0;
  promote = 0;
  give_check = 0;
  frank = -1;
  ffile = -1;
  trank = -1;
  tfile = -1;
  goodchar = strchr(movetext, '#');
  if (goodchar)
    *goodchar = 0;
/*
 ************************************************************
 *                                                          *
 *   First we look for castling moves which are a special   *
 *   case with an unusual syntax compared to normal moves.  *
 *                                                          *
 ************************************************************
 */
  if (!strcmp(movetext, "o-o") || !strcmp(movetext, "o-o+") ||
      !strcmp(movetext, "O-O") || !strcmp(movetext, "O-O+") ||
      !strcmp(movetext, "0-0") || !strcmp(movetext, "0-0+")) {
    piece = king;
    if (wtm) {
      ffile = 4;
      frank = 0;
      tfile = 6;
      trank = 0;
    } else {
      ffile = 4;
      frank = 7;
      tfile = 6;
      trank = 7;
    }
  } else if (!strcmp(movetext, "o-o-o") || !strcmp(movetext, "o-o-o+") ||
      !strcmp(movetext, "O-O-O") || !strcmp(movetext, "O-O-O+") ||
      !strcmp(movetext, "0-0-0") || !strcmp(movetext, "0-0-0+")) {
    piece = king;
    if (wtm) {
      ffile = 4;
      frank = 0;
      tfile = 2;
      trank = 0;
    } else {
      ffile = 4;
      frank = 7;
      tfile = 2;
      trank = 7;
    }
  } else {
/*
 ************************************************************
 *                                                          *
 *   OK, it is not a castling move.  Check for two "b"      *
 *   characters which might be a piece (bishop) and a file  *
 *   (b-file).  The first "b" should be "B" but we allow    *
 *   this to make typing input simpler.                     *
 *                                                          *
 ************************************************************
 */
    if ((movetext[0] == 'b') && (movetext[1] == 'b'))
      movetext[0] = 'B';
/*
 ************************************************************
 *                                                          *
 *   Check to see if there is a "+" character which means   *
 *   that this move is a check.  We can use this to later   *
 *   eliminate all non-checking moves as possibilities.     *
 *                                                          *
 ************************************************************
 */
    if (strchr(movetext, '+')) {
      *strchr(movetext, '+') = 0;
      give_check = 1;
    }
/*
 ************************************************************
 *                                                          *
 *   If this is a promotion, indicated by an "=" in the     *
 *   text, we can pick up the promote-to piece and save it  *
 *   to use later when eliminating moves.                   *
 *                                                          *
 ************************************************************
 */
    if (strchr(movetext, '=')) {
      goodchar = strchr(movetext, '=');
      goodchar++;
      promote = (strchr(pro_pieces, *goodchar) - pro_pieces) >> 1;
      *strchr(movetext, '=') = 0;
    }
/*
 ************************************************************
 *                                                          *
 *   Now for a kludge.  ChessBase (and others) can't follow *
 *   the PGN standard of bxc8=Q for promotion, and instead  *
 *   will produce "bxc8Q" omitting the PGN-standard "="     *
 *   character.  We handle that here so that we can read    *
 *   their non-standard moves.                              *
 *                                                          *
 ************************************************************
 */
    else {
      char *prom = strchr(pro_pieces, movetext[strlen(movetext) - 1]);

      if (prom) {
        promote = (prom - pro_pieces) >> 1;
        movetext[strlen(movetext) - 1] = 0;
      }
    }
/*
 ************************************************************
 *                                                          *
 *   Next we extract the last rank/file designators from    *
 *   the text, since the destination is required for all    *
 *   valid non-castling moves.  Note that we might not have *
 *   both a rank and file but we must have at least one.    *
 *                                                          *
 ************************************************************
 */
    current = strlen(movetext) - 1;
    trank = movetext[current] - '1';
    if ((trank >= 0) && (trank <= 7))
      movetext[current] = 0;
    else
      trank = -1;
    current = strlen(movetext) - 1;
    tfile = movetext[current] - 'a';
    if ((tfile >= 0) && (tfile <= 7))
      movetext[current] = 0;
    else
      tfile = -1;
    if (strlen(movetext)) {
/*
 ************************************************************
 *                                                          *
 *   The first character is the moving piece, unless it is  *
 *   a pawn.  In this case, the moving piece is omitted and *
 *   we know what it has to be.                             *
 *                                                          *
 ************************************************************
 */
      if (strchr("  PpNnBBRrQqKk", *movetext)) {
        piece = (strchr(pieces, movetext[0]) - pieces) >> 1;
        for (i = 0; i < (int) strlen(movetext); i++)
          movetext[i] = movetext[i + 1];
      }
/*
 ************************************************************
 *                                                          *
 *   It is also possible that this move is a capture, which *
 *   is indicated by a "x" between either the source and    *
 *   destination squares, or between the moving piece and   *
 *   the destination.                                       *
 *                                                          *
 ************************************************************
 */
      if ((strlen(movetext)) && (movetext[strlen(movetext) - 1] == 'x')) {
        capture = 1;
        movetext[strlen(movetext) - 1] = 0;
      } else
        capture = 0;
/*
 ************************************************************
 *                                                          *
 *   It is possible to have no source square, but we could  *
 *   have a complete algebraic square designation, or just  *
 *   rank or file, needed to disambiguate the move.         *
 *                                                          *
 ************************************************************
 */
      if (strlen(movetext)) {
        ffile = movetext[0] - 'a';
        if ((ffile < 0) || (ffile > 7)) {
          ffile = -1;
          frank = movetext[0] - '1';
          if ((frank < 0) || (frank > 7))
            piece = -1;
        } else {
          if (strlen(movetext) == 2) {
            frank = movetext[1] - '1';
            if ((frank < 0) || (frank > 7))
              piece = -1;
          }
        }
      }
    }
  }
/*
 ************************************************************
 *                                                          *
 *   Now for the easy part.  We first generate all moves    *
 *   if not pondering, or else use a pre-computed list of   *
 *   moves (if pondering) since the board position is not   *
 *   correct for move input analysis.  We then loop through *
 *   the list of moves, using the information we extracted  *
 *   previously, and eliminate all moves that are (a) the   *
 *   wrong piece type;  (b) wrong source or destination     *
 *   square;  (c) wrong promotion type;  (d) should be a    *
 *   capture/check/promotion but is not or vice-versa.      *
 *                                                          *
 ************************************************************
 */
  if (!piece)
    piece = 1;
  if (!ponder_list) {
    mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
    mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
  } else {
    for (i = 0; i < num_ponder_moves; i++)
      moves[i] = ponder_moves[i];
    mvp = moves + num_ponder_moves;
  }
  for (mv = &moves[0]; mv < mvp; mv++) {
    if (piece && (Piece(*mv) != piece))
      *mv = 0;
    if ((ffile >= 0) && (File(From(*mv)) != ffile))
      *mv = 0;
    if (capture && (!Captured(*mv)))
      *mv = 0;
    if (promote && (Promote(*mv) != promote))
      *mv = 0;
    if ((frank >= 0) && (Rank(From(*mv)) != frank))
      *mv = 0;
    if ((tfile >= 0) && (File(To(*mv)) != tfile))
      *mv = 0;
    if ((trank >= 0) && (Rank(To(*mv)) != trank))
      *mv = 0;
    if (!ponder_list && *mv) {
      MakeMove(tree, MAXPLY, *mv, wtm);
      if (Check(wtm) || (give_check && !Check(Flip(wtm)))) {
        UnmakeMove(tree, MAXPLY, *mv, wtm);
        *mv = 0;
      } else
        UnmakeMove(tree, MAXPLY, *mv, wtm);
    }
  }
/*
 ************************************************************
 *                                                          *
 *   Once we have completed eliminating incorrect moves, we *
 *   hope to have exactly one move left.  If none or left,  *
 *   the entered move is illegal.  If more than one is      *
 *   left, the move entered is ambiguous.  If appropriate,  *
 *   we output some sort of diagnostic message and then     *
 *   return.                                                *
 *                                                          *
 ************************************************************
 */
  nleft = 0;
  for (mv = &moves[0]; mv < mvp; mv++) {
    if (*mv) {
      nleft++;
      goodmove = mv;
    }
  }
  if (nleft == 1)
    return (*goodmove);
  if (!silent) {
    if (nleft == 0)
      Print(4095, "Illegal move: %s\n", text);
    else if (piece < 0)
      Print(4095, "Illegal move (unrecognizable): %s\n", text);
    else
      Print(4095, "Illegal move (ambiguous): %s\n", text);
  }
  return (0);
}

/* last modified 01/17/09 */
/*
 *******************************************************************************
 *                                                                             *
 *   InputMoveICS() is responsible for converting a move from the ics format   *
 *   [from][to][promote] to the program's internal format.                     *
 *                                                                             *
 *******************************************************************************
 */
int InputMoveICS(TREE * RESTRICT tree, char *text, int ply, int wtm,
    int silent, int ponder_list) {
  int moves[220], *mv, *mvp, *goodmove = 0;
  int piece = -1, promote;
  int ffile, frank, tfile, trank;
  int i, nleft;
  char movetext[128];
  static const char pieces[15] =
      { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
    'Q', 'q', 'K', 'k', '\0'
  };
/*
 ************************************************************
 *                                                          *
 *   Initialize move structure.  If we discover a parsing   *
 *   error, this will cause us to return a move of "0" to   *
 *   indicate some sort of error was detected.              *
 *                                                          *
 ************************************************************
 */
  if (strlen(text) == 0)
    return (0);
  tree->position[MAXPLY] = tree->position[ply];
  strcpy(movetext, text);
  moves[0] = 0;
  promote = 0;
/*
 ************************************************************
 *                                                          *
 *   First we look for castling moves which are a special   *
 *   case with an unusual syntax compared to normal moves.  *
 *                                                          *
 ************************************************************
 */
  if (!strcmp(movetext, "o-o") || !strcmp(movetext, "O-O") ||
      !strcmp(movetext, "0-0")) {
    piece = king;
    if (wtm) {
      ffile = 4;
      frank = 0;
      tfile = 6;
      trank = 0;
    } else {
      ffile = 4;
      frank = 7;
      tfile = 6;
      trank = 7;
    }
  } else if (!strcmp(movetext, "o-o-o") || !strcmp(movetext, "O-O-O") ||
      !strcmp(movetext, "0-0-0")) {
    piece = king;
    if (wtm) {
      ffile = 4;
      frank = 0;
      tfile = 2;
      trank = 0;
    } else {
      ffile = 4;
      frank = 7;
      tfile = 2;
      trank = 7;
    }
  } else {
/*
 ************************************************************
 *                                                          *
 *   Next we extract both rank/file designators from the    *
 *   text, since the destination is required for all valid  *
 *   non-castling moves.                                    *
 *                                                          *
 ************************************************************
 */
    ffile = movetext[0] - 'a';
    frank = movetext[1] - '1';
    tfile = movetext[2] - 'a';
    trank = movetext[3] - '1';
/*
 ************************************************************
 *                                                          *
 *   If this is a promotion, indicated by an "=" in the     *
 *   text, we can pick up the promote-to piece and save it  *
 *   to use later when eliminating moves.                   *
 *                                                          *
 ************************************************************
 */
    if (movetext[4] == '=')
      promote = (strchr(pieces, movetext[5]) - pieces) >> 1;
    else if ((movetext[4] != 0) && (movetext[4] != ' '))
      promote = (strchr(pieces, movetext[4]) - pieces) >> 1;
  }
/*
 ************************************************************
 *                                                          *
 *   Now for the easy part.  We first generate all moves    *
 *   if not pondering, or else use a pre-computed list of   *
 *   moves (if pondering) since the board position is not   *
 *   correct for move input analysis.  We then loop through *
 *   the list of moves, using the information we extracted  *
 *   previously, and eliminate all moves that are (a) the   *
 *   wrong piece type;  (b) wrong source or destination     *
 *   square;  (c) wrong promotion type;  (d) should be a    *
 *   capture/check/promotion but is not or vice-versa.      *
 *                                                          *
 ************************************************************
 */
  if (!ponder_list) {
    mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
    mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
  } else {
    for (i = 0; i < num_ponder_moves; i++)
      moves[i] = ponder_moves[i];
    mvp = moves + num_ponder_moves;
  }
  for (mv = &moves[0]; mv < mvp; mv++) {
    if (Promote(*mv) != promote)
      *mv = 0;
    if (Rank(From(*mv)) != frank)
      *mv = 0;
    if (File(From(*mv)) != ffile)
      *mv = 0;
    if (Rank(To(*mv)) != trank)
      *mv = 0;
    if (File(To(*mv)) != tfile)
      *mv = 0;
    if (!ponder_list && *mv) {
      MakeMove(tree, MAXPLY, *mv, wtm);
      if (Check(wtm)) {
        UnmakeMove(tree, MAXPLY, *mv, wtm);
        *mv = 0;
      } else
        UnmakeMove(tree, MAXPLY, *mv, wtm);
    }
  }
/*
 ************************************************************
 *                                                          *
 *   Once we have completed eliminating incorrect moves, we *
 *   hope to have exactly one move left.  If none or left,  *
 *   the entered move is illegal.  If more than one is      *
 *   left, the move entered is ambiguous.  If appropriate,  *
 *   we output some sort of diagnostic message and then     *
 *   return.                                                *
 *                                                          *
 ************************************************************
 */
  nleft = 0;
  for (mv = &moves[0]; mv < mvp; mv++) {
    if (*mv) {
      nleft++;
      goodmove = mv;
    }
  }
  if (nleft == 1)
    return (*goodmove);
  if (!silent) {
    if (nleft == 0)
      Print(4095, "Illegal move: %s\n", text);
    else if (piece < 0)
      Print(4095, "Illegal move (unrecognizable): %s\n", text);
    else
      Print(4095, "Illegal move (ambiguous): %s\n", text);
  }
  return (0);
}
