/***************************************************************************
                          SanNotation.cpp  -  description
                             -------------------
    begin                : Sat Sep 1 2001
    copyright            : (C) 2001 by Sven Reichard
    email                : reichard@math.udel.edu
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "Moves/SanNotation.h"
# include "Moves/Move.h"
# include "Game/Game.h"
# include "Color.h"
# include <sstream>
# include <iostream>
using namespace Alice;
SanNotation::SanNotation(SmartPointer<Move> m, Game& g)
  : _move(m),
    game(g),
    _hasSourceFile(false),
    _hasSourceRank(false),
    promotion(false),
    isCastling(false),
    flagsCalculated(false)
{
  if (!_move->isLegal(game))
    {
      std::cerr<<std::endl;
      game.board().displayOn(std::cerr);
      _move->printOn(std::cerr);
    };
  assert(_move->isLegal(game));
    calculateFlags();
};
SanNotation::SanNotation(std::string str, Game& g)
  : _string(str),
    game(g)
{
  findMove();
};

SanNotation::~SanNotation()
{
};
void
SanNotation::calculateFlags()
{
  if (flagsCalculated)
    return;
  flagsCalculated = true;
  const Square src(_move->source());

  _move->makeOn(game);
  isMate = false;
  isCheck = false;
  if (game.isInCheck(game.colorToMove()))
    {
      
      if (game.isStalemate())
	 isMate = true;
	else
	  isCheck = true;
     
    }
  //out<<(game.colorToMove()->isWhite()? 'w':'b') << "->";
  _move->takeBackOn(game);
  const Piece* piece = game.board().at(src);
  assert(piece != 0);
  assert(!piece->isNull());
  char pieceLetter = piece->forsytheLetter();
  displayLetter = "";
  if (! piece->isPawn())
    displayLetter = displayLetter + piece->displayLetter();
  //std::cout<<"San"<<endl;
  _move->modifySan(*this);
  MoveList allMoves;
  game.pseudoLegalMoves(allMoves);
  MoveList fittingMoves;
  for (MoveList::iterator moveIter = allMoves.begin();
       moveIter != allMoves.end(); moveIter++)
    {
      if ((*moveIter)->destination() != _move->destination())
	continue;
      Square src((*moveIter)->source());
      const Piece* p = game.board().at(src);
      if (p->forsytheLetter() != pieceLetter)
	continue;
      if (! fitsDescription(**moveIter))
	continue;
      fittingMoves.push_back(*moveIter);
    }
  if (fittingMoves.size() == 0)
    {
      std::cerr<<"error"<<std::endl;
      _move->printOn(std::cerr);
      game.board().displayOn(std::cerr);
      // exit(1);
    }
  //assert(fittingMoves.size() > 0);
  if (fittingMoves.size() == 1)
    return;
  int fileMatches = 0;
  setFile();
  for (MoveList::iterator moveIter = fittingMoves.begin();
       moveIter != fittingMoves.end(); ++ moveIter)
    if (fitsDescription(**moveIter))
      fileMatches ++;
  if (promotion)
    fileMatches /= 4;
  if (fileMatches == 1)
    return;
  _hasSourceFile = false;
  setRank();
  int rankMatches = 0;
  
  for (MoveList::iterator moveIter = fittingMoves.begin();
       moveIter != fittingMoves.end(); ++ moveIter)
    if (fitsDescription(**moveIter))
      rankMatches ++;
  if (promotion)
    rankMatches /= 4;
  if (rankMatches == 1)
    return;
  setFile();
};
void
SanNotation::setPromotion( char c)
{
  promotion = true;
  promotionChar = c;
};

void
SanNotation::setCastling(bool isLong)
{
  isCastling = true;
  isLongCastling = isLong;
}

void
SanNotation::printOn(std::ostream& out) 
{
  calculateFlags();
  //out<<(game.colorToMove()->isWhite()? 'w':'b') << "->";
  if (isCastling) {
    out<<"O-O";
    if (isLongCastling)
      out<<"-O";
    //return;
  }
  else {
    const Piece* p = game.board().at(_move->source());
    Square source(_move->source());
    out << displayLetter;
    if (_hasSourceFile)
      out<<char(source.file() + 'a');
    if (_hasSourceRank)
      out<<char(source.rank() + '1');
    if (_move->isCapture()){
      if (p->isPawn()&& !_hasSourceFile)
	out<<"abcdefgh"[_move->source().file()];
      out<<'x';
    }
    
    Square dest(_move->destination());
    //dest.printOn(out);
    out<<char('a'+dest.file())<<char('1'+dest.rank());
    
    if (promotion)
      out<<'='<<promotionChar;
  }
  if (isMate)
    out<<"#";
  else if (isCheck)
    out<<"+";
  
};

std::string
SanNotation::getString()
{
  std::ostringstream out;
  printOn(out);
  return out.str();
}

SmartPointer<Move>
SanNotation::getMove() const
{
  return _move;
};

void
SanNotation::setFile()
{
  _hasSourceFile = true;
};

void
SanNotation::setRank()
{
  _hasSourceRank = true;
};

bool
SanNotation::fitsDescription(const Move& mv) const
{
  return mv.fitsDescription(*this);
};

int
SanNotation::getSourceFile() const
{
  return getMove()->source().file();
};

int
SanNotation::getSourceRank() const
{
  return getMove()->source().rank();
};

bool
SanNotation::isValid() const
{
  
  return _isValid;
};

void
SanNotation::findMove()
{
  if (_move)
    return;
  //  if (_string[_string.size()-1] == '#')
  //_string[_string.size() -1] = '+';
  MoveList moves;
  game.pseudoLegalMoves(moves);
  MoveList fittingMoves;
  for (MoveList::iterator it = moves.begin();
       it != moves.end(); ++it)
    {
      SanNotation san(*it, game);
      if (san.getString() == _string)
	fittingMoves.push_back(*it);
    }
  //cout<<fittingMoves.size()<<" fitting moves"<<endl;
  if (fittingMoves.size() == 1)
    {
      _move = fittingMoves.front();
      _isValid = true;
    }
  else
    _isValid = false;
};
