# include <TranspositionTable/HashTable.h>
# include <Square.h>
# include <Game/Game.h>
# include <Pieces/Piece.h>
# include <Moves/Move.h>
# include <Moves/CapturingMove.h>
# include <Moves/PromotingMove.h>
# include <Moves/EnPassantMove.h>
# include <Moves/DoubleStep.h>
# include <Moves/PromotingCapture.h>
using namespace Alice;
HashTable::HashTable(int bits)
  : mCurrentCode(bits),
    colorCode(bits),
   whiteKingCode(bits),
   blackKingCode(bits),
   whiteQueenCode(bits),
   blackQueenCode(bits),
   whiteRookCode(bits),
   blackRookCode(bits),
   whiteBishopCode(bits),
   blackBishopCode(bits),
   whiteKnightCode(bits),
   blackKnightCode(bits),
   whitePawnCode(bits),
   blackPawnCode(bits),
   enPassantCode(bits)
{

  codeStack.push(mCurrentCode);
};

HashTable::~HashTable()
{

};


void
HashTable::setEnPassantSquare(Square ep)
{
  currentCode() ^= enPassantCode[ep];
};

void
HashTable::clearEnPassantSquare(Square ep)
{
  currentCode() ^= enPassantCode[ep];
};
const HashCode&
HashTable::hashCode(const Piece* piece, 
			     const Square& square) const
{
  assert(piece);
  if (piece->isNull())
    {
      observedGame->board().displayOn(std::cout);
    }
  assert(!piece->isNull());
  switch (piece->forsytheLetter()) {
  case 'K': return whiteKingCode[square];
  case 'k': return blackKingCode[square];
      
  case 'Q': return whiteQueenCode[square];
  case 'q': return blackQueenCode[square];
      
  case 'R': return whiteRookCode[square];
  case 'r': return blackRookCode[square];
      
  case 'B': return whiteBishopCode[square];
  case 'b': return blackBishopCode[square];
      
  case 'N': return whiteKnightCode[square];
  case 'n': return blackKnightCode[square];
      
  case 'P': return whitePawnCode[square];
  case 'p': return blackPawnCode[square];
  default: std::cerr<<"hashCode: wrong piece"<<std::endl;
  } // switch (forsytheLetter)
  return whitePawnCode[square];
};
void 
HashTable::basicTakeBack(Move& move)
{
  downdate();
  codeStack.pop();
};


void
HashTable::doNullMove()
{
  currentCode() ^= colorCode;
};

void
HashTable::takeBackNullMove()
{
  currentCode() ^= colorCode;
};

void 
HashTable::basicDoMove(Move& move)
{
  Game* game = (Game*) observedGame;
	
  codeStack.push(currentCode());
  assert(codeStack.size() < 10000);

  const Square source(move.source());
  const Square destination(move.destination());
  const Piece* p = game->board().at(source);
  HashCode& c = currentCode();
  c ^= hashCode(p, source);
  c ^= hashCode(p, destination);
  c ^= colorCode;

  Square sq(    ((Game*)observedGame)  ->epSquare());
  if (sq.isValid())
  	clearEnPassantSquare(sq);
};

void
HashTable::doSimpleMove(SimpleMove& move)
{
  basicDoMove(move);
  update();
};
void  
HashTable::doCapturingMove(CapturingMove& move)
{
  const Square destination(move.destination());
  const Piece* p = ((Game*)observedGame)->board().at(destination);
  basicDoMove(move);
  currentCode() ^= hashCode(p, destination);
  update();
};

void  
HashTable::doPromotingMove(PromotingMove& move)
{
  
  const Square destination(move.destination());
  const Piece* p = move.promotedPiece();
  basicDoMove(move);
  currentCode() ^= hashCode(p, destination);
  update();
};

void  
HashTable::doPromotingCapture(PromotingCapture& move)
{
  
  const Square destination(move.destination());
  const Piece* p = move.promotedPiece();
  const Piece* victim = ((Game*)observedGame)->board().at(destination);
  basicDoMove(move);
  currentCode() ^= hashCode(p, destination);
  currentCode() ^= hashCode(victim, destination);
  update();
};

void
HashTable::doEnPassantMove(EnPassantMove& move)
{
  basicDoMove(move);
  const Square sq(move.capturedSquare());
  const Piece* pawn = ((Game*)observedGame)->board().at(sq);
  assert(pawn->isPawn());
  currentCode() ^= hashCode(pawn, sq);
  update();
};

/** updates the hash code to reflect the e.p. square */
void 
HashTable::doDoubleStep(DoubleStep& move){
  basicDoMove(move);
  setEnPassantSquare(move.passedSquare());
  update();
}

void
HashTable::update()
{};

void HashTable::downdate()
{};
