// Copyright 1994, 1995 by Jon Dart.  All Rights Reserved.

#include "stdafx.h"
#include "notation.h"
#include "board.h"
#include "emove.h"
#include "bearing.h"
#include "movegen.h"
#include "globals.h"
#include <ctype.h>
#include <string.h>

// This module handles I/O of standard algebraic notation (SAN).
// Note that we can't put this into the Move class, because a
// move doesn't contain enough info by itself to produce SAN -
// we need the board position, too.

void Notation::
Image(const Board & b, const Move & m, char *image)
{
   if (m.IsNull())
   {
       strcpy(image,"(null)");
       return;
   }

   ExtendedMove emove(b, m);
   Piece p = emove.PieceMoved();
   ASSERT(!p.IsEmpty());
   int k;
   if (emove.Special() == ExtendedMove::KCastle)
   {
	 strcpy(image, "O-O");
	 k = 3;
   }
   else if (emove.Special() == ExtendedMove::QCastle)
   {
	 strcpy(image, "O-O-O");
	 k = 5;
   }
   else
   {
   k = 0;
   if (p.Type() == Piece::Pawn)
   {
      if (emove.Capture().IsEmpty())
      {
	 image[k] = m.DestSquare().FileImage();
	 k++;
	 image[k] = m.DestSquare().RankImage();
	 k++;
      }
      else
      {
	 image[k] = m.StartSquare().FileImage();
	 k++;
	 image[k] = 'x';
	 k++;
	 image[k] = m.DestSquare().FileImage();
	 k++;
	 image[k] = m.DestSquare().RankImage();
	 k++;
      }
      if (emove.Special() == ExtendedMove::Promotion)
      {
	 image[k] = '=';
	 k++;
         image[k] = Piece::Image(m.PromoteTo());
	 k++;
      }
   } 
   else
   {
      image[k] = Piece::Image(p.Type());
      k++;
      Square squares[Bearing::MaxBearSq];
      unsigned n = Bearing::Attack(b, m.DestSquare(), b.Side(), squares);
      int dups = 0;
      int filedups = 0;
      int rankdups = 0;
      int files[9];
      int ranks[9];

      if (n > 1)
      {
	 for (unsigned i = 0; i < n; i++)
	 {
	    if (b[squares[i]].Type() == p.Type())
	    {
	       files[dups] = squares[i].File();
	       if (files[dups] == m.StartSquare().File())
	          filedups++;
	       ranks[dups] = squares[i].Rank(White);
	       if (ranks[dups] == m.StartSquare().Rank(White))
	          rankdups++;
	       ++dups;
	    }
	 }
      }
      if (dups > 1)
      {
	 // need to disambiguate move.
	 if (filedups == 1)
	 {
	    image[k] = m.StartSquare().FileImage();
	    k++;
	 }
         else if (rankdups == 1)
	 {
	    image[k] = m.StartSquare().RankImage();
	    k++;
	 }
	 else
	 {
	    // need both rank and file to disambiguate
	    image[k] = m.StartSquare().FileImage();
	    k++;
	    image[k] = m.StartSquare().RankImage();
	    k++;
	 }
      }
      if (!emove.Capture().IsEmpty())
      {
	 image[k] = 'x';
	 k++;
      }
      image[k] = m.DestSquare().FileImage();
      k++;
      image[k] = m.DestSquare().RankImage();
      k++;
   }
   }
   Square kp = b.KingPos(b.OppositeSide());
   Board board_copy = b;
   board_copy.MakeMove(emove);
   if (board_copy.CheckStatus() == Board::InCheck)
   {
      Move last_move;
      Move_Generator mg(board_copy, 0, last_move);
      static Move moves[Move_Generator::MaxMoves];
      if (mg.Check_Evasions(moves,FALSE))
         image[k++] = '+';
      else
         image[k++] = '#'; // mate
   }
   image[k] = '\0';
}

Move Notation::
Value(const Board & board,
      const ColorType side, char *image)
{
   char *p;
   int rank = 0;
   int file = 0;

   Piece::PieceType piece;
   Piece:: PieceType promotion = Piece::Invalid;
   Square dest, start;
   BOOL capture = FALSE;

   for (p = image; isspace(*p); ++p);
   if (!isalpha(*p) && *p != '0')
      return Move::NullMove();
   if (toupper(*p) == 'O' || *p == '0')
   {
      // castling, we presume
      char tmp[10];

      strncpy(tmp, p, 9);
      // look for brain-dead variants of castling like "O-O-0"
      char *q = tmp;
      while ((q = strchr(q,'0')) != NULL)
      {
          *q++ = 'O';
      }
      char *check;
      if ((check = strchr(tmp, '+')) != NULL)
         *check = '\0';
      else if ((check = strchr(tmp, '#')) != NULL)
         *check = '\0';
      for (q = tmp; *q; q++)
	 *q = toupper(*q);
      return Move::Value(tmp, side);
   }
   int have_start = 0;
   if (isupper(*p))
   {
      piece = Piece::CharValue(*p);
      ++p;
   }
   else
   {
      piece = Piece::Pawn;
      file = *p - 'a' + 1;
      // allow "dc4" as in Informant, instead of dxc4
      if (*(p+1) == 'x' || isalpha(*(p+1)))
      {
	 capture = TRUE;
	 ++p;
      }
      else if (isdigit(*(p+1)))
      {
         char *q = p+2;
	 if (*q == 'x' || *q == '-')
	 {
	     // long algebraic notation
	     have_start++;
	     start = Square::Value(p);
	     p = q;
	 }
      }
   }
   if (piece == Piece::Invalid)
      return Move::NullMove();
   if (piece != Piece::Pawn)
   {
      // look for disambiguating rank or file, e.g. 'b' in "Nbd7".
      
      if (isalpha(*p) && isdigit(*(p+1)) &&
          (strchr(p,'-') || strchr(p,'x')))
      {
         // looks like long algebraic notation
	 start = Square::Value(p);
	 p+=2;
	 have_start++;
      }
      else if (isdigit(*p))
      {
	 rank = *p - '0';
	 ++p;
      }
      else if (*p != 'x' && isalpha(*p) && isalpha(*(p + 1)))
      {
	 file = *p - 'a' + 1;
	 ++p;
      }
   }

   if (*p == 'x')
   {
      capture = TRUE;
      ++p;
   }
   else if (*p == '-')
      ++p;
   // remainder of move should be a square identifier, e.g. "g7"
   dest = Square::Value(p);
   if (dest == Square::Invalid())
      return Move::NullMove();
   p += 2;
   if (*p == '=')
   {
      promotion = Piece::CharValue(*(p + 1));
      if (piece != Piece:: Pawn || promotion == Piece::Invalid)
         return Move::NullMove();
   }
   else if (piece == Piece::Pawn && isupper(*p))
   {
      // Quite a few "PGN" files have a8Q instead of a8=Q.
      promotion = Piece::CharValue(*p);
      if (promotion == Piece::Invalid)
         return Move::NullMove();
   }

   // Informant does not use "x" for captures.  Assume that if the destination
   // is occupied, this is a capture move.
   if (!board[dest].IsEmpty())
      capture = TRUE;
   // Do a sanity check on capture moves:
   if (capture && !board[dest].IsEmpty() && board[dest].Color() == board.Side())
      return Move::NullMove();

   // ok, now we need to figure out where the start square is.

   int dups = 0;

   if (!have_start)
   {

   if (capture && piece == Piece::Pawn && board[dest].IsEmpty() &&
       dest.Rank(board.Side()) != 8)
   {
       // en passant capture, special case
       int start_rank = (board.Side() == White) ?
       dest.Rank(White) - 1 :
       dest.Rank(White) + 1;

       start = Square(file, start_rank, White);
       dups = 1;
   }
   else
   {
      static Square squares[36];
      int n = Bearing::Attack(board, dest, side, squares);

      for (int i = 0; i < n; i++)
      {
	 Square maybe(squares[i]);

	 if (board[maybe].Type() == piece && board[maybe].Color() == board.Side())
	 {
	    if (file && maybe.File() != file)
	       continue;
	    else if (rank && maybe.Rank(White) != rank)
	       continue;
	    else if (board[maybe].Color() == board.Side())
	    {
               // Possible move to this square.  Make sure it is legal.
	       
	       Board board_copy(board);
	       ExtendedMove emove(board_copy,maybe,dest,promotion);
	       board_copy.MakeMove(emove);
	       if (!board_copy.any_attacks(
	          board_copy.KingPos(board_copy.OppositeSide()),
		  board_copy.Side()))
               {			   
	          ++dups;
	          start = maybe;
               }
	    }
	 }
      }
   }
   }
   if (dups == 1 || have_start)
      return Move(start, dest, promotion);
   else				// ambiguous move
      return Move::NullMove();
}
