/***************************************************************************
                          CheckSentry.cpp  -  description
                             -------------------
    begin                : Tue May 22 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/CheckSentry.h"

# include "Color.h"
# include "Game/Game.h"
# include "Pieces/Piece.h"
# include "Moves/Move.h"
# include "Directions/Direction.h"
# include "Moves/CastlingMove.h"
# include "Moves/EnPassantMove.h"
# define DYNAMIC_CHECK_UPDATE
using namespace Alice;

CheckSentry::CheckSentry():
  whiteKing(0,0),
  blackKing(0,0)
{};

CheckSentry::~CheckSentry()
{
  stopObserving();
};

void
CheckSentry::startObserving(Game* game)
{
  GameObserver::startObserving(game);
  for (int i = 0; i < 8; i++)
    for (int j = 0; j < 8; j++) {
      Square sq(Square(i,j));
      const Piece* p = ((Game*)observedGame)->board().at(sq);
      if (p->isKing()){
	if (p->color()->isWhite())
	  whiteKing = sq;
	else
	  blackKing = sq;
      }
    }
  while(!whiteInCheck.empty()) whiteInCheck.pop();
  whiteInCheck.push(((Game*)observedGame)->isAttacked(whiteKing, 
						      Color::black()));
  while(!blackInCheck.empty()) blackInCheck.pop();
  blackInCheck.push(((Game*)observedGame)->isAttacked(blackKing, 
						      Color::white()));
};

void CheckSentry::stopObserving()
{
  GameObserver::stopObserving();
};

void
CheckSentry::basicDoMove(Move& mv)
{
  if (mv.source() == whiteKing)
    whiteKing = mv.destination();
  else if (mv.destination() == whiteKing)
    {
      std::cerr<<"white king captured!"<<std::endl;
      observedGame->board().displayOn(std::cerr);
      mv.printOn(std::cerr);
      mv.takeBackOn(*observedGame);
      observedGame->board().displayOn(std::cerr);
      throw(this);
    }
  if (mv.source() == blackKing)
    blackKing = mv.destination();
  else if (mv.destination() == blackKing)
    {
      std::cerr<<"black king captured!"<<std::endl;
      observedGame->board().displayOn(std::cerr);
      mv.printOn(std::cerr);
      mv.takeBackOn(*observedGame);
      observedGame->board().displayOn(std::cerr);
      throw(this);
    }
# ifndef DYNAMIC_CHECK_UPDATE
	whiteInCheck.push(whiteInCheck.top());
	blackInCheck.push(blackInCheck.top());
# else // DYNAMIC_CHECK_UPDATE

  if (whiteInCheck.top() || mv.destination() == whiteKing) 
    {
      whiteInCheck.push(observedGame->isAttacked(whiteKing,
					 Color::black()));
    }
  else 
    {
      bool inCheck = false;
      const Direction& srcDir = whiteKing.directionTo(mv.source());
      if (! srcDir.isNull())
	inCheck =observedGame->isAttackedFromDirection(srcDir, whiteKing, 
					       Color::black());
      //delete srcDir;
      if (inCheck == false && observedGame->colorToMove()->isWhite())
	{
	  const Direction& destDir = whiteKing.directionTo(mv.destination());
	  if (! destDir.isNull())
	    inCheck = observedGame->isAttackedFromDirection(destDir, 
						    whiteKing, Color::black());
	  //delete destDir;
	}
      whiteInCheck.push(inCheck);
    }
  if (blackInCheck.top() || mv.destination() == blackKing) 
    {
      blackInCheck.push(observedGame->isAttacked(blackKing,Color::white()));
    }
  else 
    {
      
      bool inCheck = false;
      const Direction& srcDir = blackKing.directionTo(mv.source());
      if (! srcDir.isNull())
	inCheck =observedGame->isAttackedFromDirection(srcDir, blackKing, Color::white());
      if (inCheck == false && observedGame->colorToMove()->isBlack()){
	const Direction& destDir = blackKing.directionTo(mv.destination());
	if (! destDir.isNull())
	  inCheck = observedGame->isAttackedFromDirection(destDir, blackKing, Color::white());
      }
      blackInCheck.push(inCheck);
    }
# endif // def DYNAMIC_CHECK_UPDATE
};

void
CheckSentry::doCastlingMove(CastlingMove& move)
{
  basicDoMove(move);
  {
    const Direction& d = whiteKing.directionTo(move.getRookDestination());
    if (!d.isNull())
      whiteInCheck.top() |= observedGame->isAttackedFromDirection(d, whiteKing, Color::black());
  }{
    const Direction& d = blackKing.directionTo(move.getRookDestination());
    if (!d.isNull())
      blackInCheck.top() |= observedGame->isAttackedFromDirection(d, blackKing, Color::white());
  }
};
void
CheckSentry::doEnPassantMove(EnPassantMove& move)
{
  basicDoMove(move);
  {
    const Direction& d = whiteKing.directionTo(move.capturedSquare());
    if (!d.isNull())
      whiteInCheck.top() |= observedGame->isAttackedFromDirection(d, whiteKing, Color::black());
  }{
    const Direction& d = blackKing.directionTo(move.capturedSquare());
    if (!d.isNull())
      blackInCheck.top() |= observedGame->isAttackedFromDirection(d, blackKing, Color::white());
  }
};

void CheckSentry::basicTakeBack(Move& mv)
{
  if (mv.destination() == whiteKing)
    whiteKing = mv.source();
  if (mv.destination() == blackKing)
    blackKing = mv.source();
  whiteInCheck.pop();
  blackInCheck.pop();
};

void
CheckSentry::doNullMove()
{
};

void 
CheckSentry::takeBackNullMove()
{

};

Square
CheckSentry::findKing(const Color* color) const
{
  if (color->isWhite()){
    assert(observedGame->board().at(whiteKing)->isKing());
    return whiteKing;
    
  }
  assert(observedGame->board().at(blackKing)->isKing());
  return blackKing;
};

bool
CheckSentry::isInCheck(Color* color) const
{
# ifdef DYNAMIC_CHECK_UPDATE
  const std::stack<bool>* checkStack =
    color->isWhite()? &whiteInCheck: &blackInCheck;
  //assert(isConsistent());
  //if (((Game*)observedGame)->isAttacked(findKing(color),color->otherColor()) == checkStack->top())
    return checkStack->top();
  std::cerr<<"isInCheck: error"<<std::endl;
  ((Game*)observedGame)->board().displayOn(std::cerr);
  exit(0);
# else // DYNAMIC_CHECK_UPDATE
  return ((Game*)observedGame)->isAttacked(findKing(color),color->otherColor());
# endif // DYNAMIC_CHECK_UPDATE
};

bool
CheckSentry::isConsistent() const
{
  bool result = true;
  Game* game = (Game*) observedGame;
  const Piece* pWhiteKing = ((Game*)observedGame)->board().at(whiteKing);
  if (pWhiteKing->isKing() == false)
    result =  false;
  if (pWhiteKing->color()->isWhite() == false)
    result =  false;
  
  const Piece* pBlackKing = ((Game*)observedGame)->board().at(blackKing);
  if (pBlackKing->isKing() == false)
    result =  false;
  if (pBlackKing->color()->isBlack() == false)
    result =  false;
  if (result == false)
    {
      game->board().displayOn(std::cerr);
      std::cerr<<"white: "<<"abcdefgh"[whiteKing.file()]
	  <<"12345678"[whiteKing.rank()]<<std::endl;
      std::cerr<<"black: "<<"abcdefgh"[blackKing.file()]
	  <<"12345678"[blackKing.rank()]<<std::endl;
    }
  return result;
};

bool
CheckSentry::isLateObserver() const
{
  return true;
};
