/***************************************************************************
                          Game.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/Game.h>
# include <Moves/Move.h>
# include <Color.h>
# include <Pieces/Piece.h>
# include <Square.h>
# include <Directions/Direction.h>
# include <Directions/DirectionDown.h>
# include <Directions/DirectionUp.h>
# include <Game/CheckSentry.h>
# include <Game/EnPassantSentry.h>
# include <Game/CastlingSentry.h>
# include <TranspositionTable/RepetitionSentry.h>
# include <Moves/EnPassantMove.h>
# include <SmartPointer.h>
# include <algorithm>
# ifdef NO_SSTREAMS
#  include <strstream>
# else
#  include <sstream>
# endif
# include <iostream>
# include <Moves/SanNotation.h>
# include <Bitboard.h>
using namespace Alice;

Game::Game():
  mColorToMove(Color::white()),
  checkSentry(new CheckSentry),
  epSentry(new EnPassantSentry),
  castlingSentry(new CastlingSentry),
  repetitionSentry(new RepetitionSentry)
{
  mBoard.initialPosition();
  checkSentry->startObserving(this);
  epSentry->startObserving(this);
  castlingSentry->startObserving(this);
  repetitionSentry->startObserving(this);
  _recapture.push(false);
  _capturingSquare.push(Square::invalid());
  _capturingSquare.push(Square::invalid());
};
Game::~Game()
{
  delete repetitionSentry;
  delete castlingSentry;
  delete epSentry;
  delete checkSentry;

  while (!mObservers.empty())
    mObservers.front()->stopObserving();
};
void 
Game::basicDoMove(Move& move)
{
  //GameBroadcaster::basicDoMove(move);
  if (!(move.isLegal(*this))){
    std::cerr<<"illegal move ";
    move.printOn(std::cerr);
    std::cerr<<std::endl;
    mBoard.displayOn(std::cerr);
    if (mColorToMove == Color::white())
      std::cout<<"White";
    else
      std::cout<<"Black";
    std::cout<<" to move"<<std::endl;
    assert(false);
  }
  for (std::list<GameObserver*>::iterator it = mObservers.begin();
       it != mObservers.end(); ++ it)
    move.makeOn(**it);
  move.makeOn(mBoard);
  mColorToMove = mColorToMove->otherColor();
  for (std::list<GameObserver*>::iterator it = mLateObservers.begin();
       it != mLateObservers.end(); ++ it)
    move.makeOn(**it);
  _recapture.push(false);
  _capturingSquare.push(Square::invalid());
  /*
  std::cout<<"making move ";
  move.printOn(std::cout);
  std::cout<<endl;
  board().displayOn(std::cout);
  return;
  */
};
void 
Game::basicTakeBack(Move& move)
{
  move.takeBackOn(mBoard);
  mColorToMove = mColorToMove->otherColor();
  GameBroadcaster::basicTakeBack(move);
  for (std::list<GameObserver*>::iterator it = mLateObservers.begin();
  	it != mLateObservers.end(); ++ it)
  	move.takeBackOn(**it);
  _recapture.pop();
  _capturingSquare.pop();
  
  /*std::cout<<"unmaking move ";
  move.printOn(std::cout);
  std::cout<<endl;
  board().displayOn(std::cout);
  return;
*/
};

void 
Game::doNullMove()
{
  mColorToMove = mColorToMove->otherColor();
  GameBroadcaster::doNullMove();
  _recapture.push(false);
  _capturingSquare.push(Square::invalid());
  /*
  std::cout<<"making move null";

  std::cout<<endl;
  board().displayOn(std::cout);
  */
};

void 
Game::takeBackNullMove()
{
  mColorToMove = mColorToMove->otherColor();
  GameBroadcaster::takeBackNullMove();
  _recapture.pop();
  _capturingSquare.pop();
  /*
  std::cout<<"unmaking move null";

  std::cout<<endl;
  board().displayOn(std::cout);
  */
};


void 
Game::doCapturingMove(CapturingMove& move)
{
  basicDoMove(move);
  _capturingSquare.pop();
  _recapture.top() = (move.destination() == _capturingSquare.top());
  _capturingSquare.push(move.destination());
};

std::string 
Game::forsytheString() const
{
# ifdef NO_SSTREAMS
	std::ostrstream out;
# else
  std::ostringstream out;
# endif
  mBoard.writeForsythe(out);
  std::string result =  out.str();
  result += "/";
  if (colorToMove() == Color::white())
    result += 'w';
  else
    result += 'b';
  return result;
};

void
Game::forsytheString(const std::string& str)
{
# ifdef NO_SSTREAMS
  istrstream in(str.c_str());
# else

  std::istringstream in(str);
# endif
  mBoard.readForsythe(in);
  if (!in.eof()){
    char c;
    if (in>>c)
      {
	
	if (c != '/' || 
	    in>>c)
	  {
    switch(c) {
    case 'w': mColorToMove = Color::white(); break;
    case 'b': mColorToMove = Color::black(); break;
    }
	  }
      }
  }
  checkSentry->stopObserving();
  checkSentry->startObserving(this);
};

Color*
Game::colorToMove() const
{
  return mColorToMove;
};

void
Game::pseudoLegalMoves(MoveList& moves, bool capturesOnly)
{
  BitboardIterator myPieces( colorToMove()==Color::white()?
		     board().getWhitePieces() :
		     board().getBlackPieces());
  // while( myPieces )
  for (int i = 0; i < 64; i++)
    {
      Square sq(i);
      const Piece* piece=mBoard.at(sq);
      if (piece->color() != colorToMove())
	continue;
      //++myPieces;
      piece->pseudolegalMoves(moves, sq, mBoard, capturesOnly);
    }
  if (! capturesOnly)
    castlingSentry->generateMoves(moves);
  // e.p. moves
  if (! epSquare().isValid())
    return;
  const Direction* backward;
  if (colorToMove()->isWhite())
    backward = &Direction::down();
  else
    backward =  &Direction::up();
  Square capture(backward->appliedTo(epSquare()));
  Square source(capture.right());
  if (source.isValid()) {
    const Piece* p = board().at(source);
    if (p->color() == colorToMove() && p->isPawn())
      moves.push_back(MoveList::value_type(new EnPassantMove(source, epSquare(), capture)));
  }
  source = (capture.left());
  if (source.isValid()) {
    const Piece* p = board().at(source);
    if (p->color() == colorToMove() && p->isPawn())
      moves.push_back(MoveList::value_type(new EnPassantMove(source, epSquare(), capture)));
  }
};
bool 
Game::isAttacked(const Square& sq, Color* color)
{
  
  const std::list<Direction*>& directions=Direction::allInstances();
  for (std::list<Direction*>::const_iterator dir(directions.begin());
       dir != directions.end(); ++ dir)
    if (isAttackedFromDirection(**dir, sq, color)){
      return true;}
  return false;
  
};
bool 
Game::isAttackedFromDirection(const Direction& dir, const Square& sq,
			       Color* color)
{
  Square attacker(dir.blockedSquareFrom(sq, board()));
  if (!attacker.isValid()){
    return false;
  }
  const Piece* p=board().at(attacker);
  if(p->color() != color){
    return false;
  }
  bool result =  p->attacks(&dir, attacker, sq);
  return result;
};

bool
Game::isInCheck(Color* color)
{
  return checkSentry->isInCheck(color);
};

bool
Game::isStalemate()
{
  if (isInCheck(colorToMove()->otherColor()))
    return false;;
  MoveList moves;
  Color* defender(colorToMove());
  pseudoLegalMoves(moves);
  bool isMate = true;
  for (MoveList::iterator it = moves.begin();
       it != moves.end();
       ++ it)
    {
      (*it)->makeOn(*this);
      isMate &= isInCheck(defender);
      (*it)->takeBackOn(*this);
    };
  return isMate;
};

bool
Game::isRepeated()
{
  return repetitionSentry->isRepeatedPosition();
};

Square
Game::epSquare() const
{
	return epSentry->square();
};

void
Game::addObserver(GameObserver& observer)
{
  if (&observer == checkSentry)
    assert(observer.isLateObserver());
  if (observer.isLateObserver())
    mLateObservers.push_front(&observer);
  else
    mObservers.push_front(&observer);
  //std::cerr<<"added "<<&observer<<endl;
};

void
Game::removeObserver(GameObserver& observer)
{
  if (observer.isLateObserver()) {
    std::list<GameObserver*>::iterator it= find(mLateObservers.begin(), mLateObservers.end(), &observer);
    assert(it != mLateObservers.end());
    mObservers.erase(it);
  }
  else{
    std::list<GameObserver*>::iterator it= find(mObservers.begin(), 
						mObservers.end(), &observer);
    assert(it != mObservers.end());
    mObservers.erase(it);
  }
  //std::cerr<<"removed "<<&observer<<std::endl;
};

bool
Game::isRecapture() const
{
  return _recapture.top();
};

bool
Game::hasCastled(const Color* color) const
{
  return castlingSentry->hasCastled(color);
};

bool
Game::canCastle( const Color* color ) const
{
  return castlingSentry->canCastle( color );
};


std::string
Game::getEPDString() const
{
  std::ostringstream output;
  board().writeForsythe( output );
  output << " ";
  if (colorToMove()->isWhite())
    output<<"w";
  else
    output<<"b";
  output<<" ";
  output<<castlingSentry->getEPDString()<<" ";
  output<<epSentry->getEPDString();
  return output.str();
};

Square 
Game::findKing( const Color* color ) const
{
  return checkSentry->findKing(color);
};
