/***************************************************************************
                          CastlingSentry.cpp  -  description
                             -------------------
    begin                : Sun Sep 2 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 "Game/CastlingSentry.h"
# include "Game/Game.h"
# include "Color.h"
# include "Moves/Move.h"
# include "Moves/CastlingMove.h"
using namespace Alice;
CastlingSentry::CastlingSentry()
  : whiteHasCastled(false),
    blackHasCastled(false)
{
	flagStack.push(ALL_RIGHTS);
}
CastlingSentry::~CastlingSentry(){
}

void
CastlingSentry::basicDoMove(Move& move)
{
	static Square e1(4,0);
	static Square e8(4,7);
	static Square a1(0, 0);
	static Square h1(7,0);
	static Square a8(0,7);
	static Square h8(7, 7);
	//std::cerr<<__FUNCTION__<<flagStack.top()<<"  ";
	flagStack.push(flagStack.top());
	if (move.source() ==/* Square(0, 4)) // */e1)
		flagStack.top() &= ~(WHITE_SHORT | WHITE_LONG);
	if (move.source() ==/* Square(7, 4)) // */e8)
		flagStack.top() &= ~(BLACK_SHORT | BLACK_LONG);
	if (move.source() == Square(0, 0) ||
			move.destination() ==/* Square(0, 0)) //*/ a1)
		flagStack.top() &= ~WHITE_LONG;
	if (move.source() == /*Square(0, 7)*/h1 ||
			move.destination() == /*Square(0, 7)) //*/ h1)
		flagStack.top() &= ~WHITE_SHORT;
	if (move.source() == /*Square(7, 0)*/a8 ||
			move.destination() == /*Square(7, 0)) //*/ a8)
		flagStack.top() &= ~BLACK_LONG;
	if (move.source() == /*Square(7, 7) */h8||
			move.destination() == /*Square(7, 7)) // */h8)
		flagStack.top() &= ~BLACK_SHORT;
	//std::cerr<<flagStack.top()<<std::endl;
};

void
CastlingSentry::basicTakeBack(Move& move)
{
	flagStack.pop();
};

bool
CastlingSentry::canCastleShort()
{
	Game* game((Game*) observedGame);
	if (game->isInCheck(game->colorToMove()))
		return false;
	int flag = (game->colorToMove()->isWhite()? WHITE_SHORT : BLACK_SHORT);
	if ((flagStack.top() & flag) == 0)
		return false;
	int baseRank = (game->colorToMove()->isWhite() ? 0 : 7);
	Square kingSource(4, baseRank);
	Square kingDestination(6, baseRank);
	Square rookSource(7, baseRank);
	Square rookDestination(5, baseRank);
	
	const Piece* king(game->board().at(kingSource));
	if (! king->isKing()){
		return false;
	}
	if (  king->color() != game->colorToMove()){
		return false;
	}
	const Piece* rook(game->board().at(rookSource));
	if (! rook->isRook()){
		return false;
	}
	if (  rook->color() != game->colorToMove()){
		return false;
	}
	if (!game->board().at(kingDestination)->isNull()){
		return false;
	}
	if (!game->board().at(rookDestination)->isNull()){
		return false;
	}
	if (game->isAttacked(rookDestination, game->colorToMove()->otherColor()))
		return false;
	return true;
};


bool
CastlingSentry::canCastleLong()
{
	Game* game((Game*) observedGame);
	if (game->isInCheck(game->colorToMove()))
		return false;
	int flag = (game->colorToMove()->isWhite()? WHITE_LONG : BLACK_LONG);
	if ((flagStack.top() & flag) == 0)
		return false;
	int baseRank = (game->colorToMove()->isWhite() ? 0 : 7);
	Square kingSource(4, baseRank);
	Square kingDestination(2, baseRank);
	Square rookSource(0, baseRank);
	Square rookDestination(3, baseRank);
	Square bFile(1, baseRank);
	const Piece* king(game->board().at(kingSource));
	if (! king->isKing()){
		return false;
	}
	if (  king->color() != game->colorToMove()){
		return false;
	}
	const Piece* rook(game->board().at(rookSource));
	if (! rook->isRook()){
		return false;
	}
	if (  rook->color() != game->colorToMove()){
		return false;
	}
	if (!game->board().at(kingDestination)->isNull()){
		return false;
	}
	if (!game->board().at(rookDestination)->isNull()){
		return false;
	}
	if (!game->board().at(bFile)->isNull()){
		return false;
	}
	if (game->isAttacked(rookDestination, game->colorToMove()->otherColor()))
		return false;
	return true;
};

void
CastlingSentry::generateMoves(MoveList& moves)
{
	Game* game((Game*) observedGame);
	int baseRank;
	if (game->colorToMove()->isWhite()) {
		baseRank = 0;
	}
	else
		baseRank = 7;
	if (canCastleShort())
		moves.push_back(MoveList::value_type(new
			CastlingMove(Square(4, baseRank),Square(6, baseRank),
								Square(7, baseRank),Square(5, baseRank))));
	if (canCastleLong())
		moves.push_back(MoveList::value_type(new
			CastlingMove(Square(4, baseRank),Square(2, baseRank),
								Square(0, baseRank),Square(3, baseRank))));
		
				
};

bool
CastlingSentry::hasCastled(const Color* color) const
{
  if (color->isWhite())
    return whiteHasCastled;
  else
    return blackHasCastled;
  
};

void
CastlingSentry::doCastlingMove(CastlingMove& move)
{
  Game* g = (Game*) observedGame;
  if (g->colorToMove()->isWhite())
    whiteHasCastled = true;
  else
    blackHasCastled = true;
  basicDoMove(move);
  
};

void
CastlingSentry::takeBackCastlingMove(CastlingMove& move)
{
  Game* g = (Game*) observedGame;
  if (g->colorToMove()->isWhite())
    whiteHasCastled = false;
  else
    blackHasCastled = false;
  basicTakeBack(move);
};

bool
CastlingSentry::canCastle( const Color* color ) const
{
  if (color->isWhite())
    return flagStack.top() & 3;
  else
    return flagStack.top() & 12;
};

std::string
CastlingSentry::getEPDString() const
{
  std::string result("");
  int flags = flagStack.top();
  if (flags & WHITE_SHORT)
    result += 'K';
  if (flags & WHITE_LONG)
    result += 'Q';
  if (flags & BLACK_SHORT)
    result += 'k';
  if (flags & BLACK_LONG)
    result += 'q';
  if (result == "")
    return "-";
  return result;

};
