//////////////////////////////////////////////////
// (c) MMIII Sven Reichard
// 

# include <XBoard/XBoardInterface.h>
# include <XBoard/XBoardFeature.h>
# include <iostream>
# include <Game/Game.h>
# include <Game/GameRecord.h>
# include <Algorithms/HistoryAlphaBeta.h>
# include <Game/PositionalEvaluation.h>
# include <Moves/SanNotation.h>
# include <sstream>
# include <TestData/TestPositionSuite.h>
# include <signal.h>
using namespace Alice;
using namespace Alice::XBoard;
bool
XBoardInterface::idle;
XBoardInterface::XBoardInterface()
  : XBoardBase( std::cin, std::cout )
{
  initialize();
};
XBoardInterface::XBoardInterface(std::istream& in, std::ostream& out)
  : XBoardBase( in, out ),
    logFile("Alice.log")
{
  initialize();
};
void
XBoardInterface::initialize()
{
  game = 0;
  algorithm = 0;
  evaluation = 0;
  //  record = new GameRecord;
  XBoardBase::initialize();
  idle = true;
  int flags = SA_RESTART;
  struct sigaction action;
  action.sa_handler = signalHandler;
  action.sa_flags = flags;
  sigaction(SIGALRM, &action, 0);
  alarm(600);
  
};

XBoardInterface::~XBoardInterface()
{
  delete game;
  delete algorithm;
  delete evaluation;
  //delete record;
  for (std::map<std::string, XBoardFeature*>::iterator it = features.begin();
       it != features.end(); ++ it)
    delete it->second;
};


Game*
XBoardInterface::getGame()
{
  return game;
};

SmartPointer<Move>
XBoardInterface::readMove( const std::string& buffer )
{
  if (*getFeature("san"))
    {
      SanNotation notation(buffer, *game);
      return notation.getMove();
    }
  MoveList list;
  game->pseudoLegalMoves(list);
  MoveList::const_iterator it;
  for ( it = list.begin();
	it != list.end(); ++ it)
    if ((*it)->fitsDescription(buffer))
      {
	return *it;
      }
  return 0;
};

std::string
XBoardInterface::moveToString(SmartPointer<Move> move)
{
  if (*getFeature("san"))
    {
      SanNotation san(move, *game);
      return san.getString();
    };
  std::ostringstream out;
  move->printOn(out);
  return out.str();
};

void
XBoardInterface::noOperation( )
{

};

void
XBoardInterface::go()
{
  mode = GO;
  search();
};

void
XBoardInterface::newGame()
{
  idle = false;
  delete game;
  game = new Game;
  delete algorithm;
  algorithm = new HistoryAlphaBeta(*game);
  delete evaluation;
  evaluation = new PositionalEvaluation;
  algorithm -> setEvaluation( evaluation );
  //record = new GameRecord;
  oppComputer = false;
  mode = GO;
  movesSoFar = 0;
  learningFile.open("position.epd");
  lastDepth = 0;
  epdStrings.clear();
};

void
XBoardInterface::usermove()
{
  std::string moveString;
  input >> moveString;
  SmartPointer<Move> move = readMove(moveString);
  if (!move)
    {
      game->board().displayOn(std::cerr);
      if (game->colorToMove()->isWhite())
	std::cerr<<"White to move"<<std::endl;
      else
	std::cerr<<"Black to move"<<std::endl;
    }
  //if (record)
  //record->addMove(move);

  makeMove(move);
  
  if (getMode() == GO)
    search();
};

void
XBoardInterface::processCommand()
{
  std::string command;
  input >> command;
  if (! input )
    return;
  //  std::cerr<<"processing command \""<<command<<"\""<<std::endl;
  Command c = commands[command];
  if (c)
    (this->*c)();

  else if (game && !*getFeature("usermove"))
    {
      SmartPointer<Move> move = readMove(command);
      if (! move)
	{
	  output << "Error (unknown command): " << command <<std::endl;
	    return;
	}
      //if (record)
      //record->addMove(move);
      makeMove(move);
      if (getMode() == GO)
	search();
    }
  else
    output << "Error (unknown command): " << command <<std::endl;
};

double
XBoardInterface::timeToSearch() const
{
  return timeControl.timeToSearch( movesSoFar );
};

void
XBoardInterface::reportResult()
{
  assert(game->isStalemate());
  if (game->isInCheck(game->colorToMove()))
    {
      if (game->colorToMove() == Color::white())
	output<<"result 0-1 {Black mates}\n";
      else
	output<<"result 1-0 {White mates}\n";
      return;
    }
  output<<"result 1/2-1/2 {Stalemate}\n";
  return;
    
};

void
XBoardInterface::checkTactics()
{

  if (epdStrings.size() > 1)
    {
      if (algorithm->score() < lastScore - 2000)
	{
	  std::ofstream tactics("tactics.epd", std::ios::app);
	  tactics<<epdStrings[epdStrings.size()-2];
	  tactics<<epdStrings[epdStrings.size()-1];
	}
    }
  lastScore = algorithm->score();
};

void
XBoardInterface::writeLeafPosition( std::vector<SmartPointer<Move> >&
				    pv)
{

  for (std::vector<SmartPointer<Move> >::iterator move = pv.begin();
       move != pv.end(); ++ move)
    (*move)->makeOn(*game);
  TestPosition leafPosition(*game);
  leafPosition.writeEPD(learningFile);

  for (std::vector<SmartPointer<Move> >::reverse_iterator move = 
	 pv.rbegin();
       move != pv.rend(); ++ move)
    (*move)->takeBackOn(*game);
};

void
XBoardInterface::sendMove()
{

  SmartPointer<Move> move = algorithm->bestMove();
  if (move)
    {
      output<<"move "<<moveToString(move)<<std::endl;
      makeMove(move);
    }
  else
    output <<"resign"<<std::endl;
};

void
XBoardInterface::search()
{
  if (! game)
    return;
  if (game->isStalemate())
    {
      reportResult();
      return;
    }
  logFile << game->getEPDString()<<std::endl;
  TestPosition position( *game );
  myColor = game->colorToMove();
  algorithm->setGame(*game);
  algorithm->clearTable();
  double time = timeToSearch();
  algorithm->searchTime(time, logFile);

  std::ostringstream pvStream;
  algorithm->printCompletePV(algorithm->getSearchDepth(), pvStream);

  logFile<<"PV: " << pvStream.str() << std::endl;
  position.setOperand("pv", pvStream.str());
  std::ostringstream scoreStream;
  scoreStream << round(algorithm->score()/10);
  position.setOperand("ce", scoreStream.str());
  std::ostringstream epdStream;
  position.writeEPD(epdStream);
  epdStrings.push_back(epdStream.str());
  logFile << epdStream.str();
  checkTactics();
  std::vector<SmartPointer<Move> > pv =
    algorithm->completePV(algorithm->getSearchDepth());
  writeLeafPosition( pv );
  sendMove();
  movesSoFar ++;
  if (movesSoFar >= getMovesPerSession())
    movesSoFar -= getMovesPerSession();
};

void
XBoardInterface::saveRecord()
{
# if 0
  if (! record)
    return;
  record->setTag("Result", getResult());
  std::ofstream output("alice.pgn", std::ios_base::app);
  if (myColor->isWhite())
    {
      record->setTag("White", "Alice");
      record->setTag("Black", getOpponentName());
      /*
	if (getMyRating())
	record->setTag("WhiteELO", getMyRating() );
      if (getOpponentRating())
	record->setTag("BlackELO", getOpponentRating() );
      */
    }
  else
    {
      record->setTag("Black", "Alice");
      record->setTag("White", getOpponentName());
    }
  time_t t = std::time(0);
  struct tm* l = localtime(&t);
  std::ostringstream date;
  date << l->tm_year+1900 << "." 
       << l->tm_mon+1 << "." 
       << l->tm_mday;
  record->setTag("Date", date.str());
  record->write(output);
  output.close();
# endif // 0
};

void
XBoardInterface::learn()
{
  learningFile.close();
  std::ofstream learnLog("learn.log", std::ios_base::app);
  TestPositionSuite suite("position.epd");
  learnLog << "found " << suite.numberOfPositions()
	   << " positions" << std::endl;
  if (suite.numberOfPositions() == 0)
    return;
  PositionalEvaluation eval;
  std::vector<Evaluation::Score> scores(suite.numberOfPositions());
  eval.computeScores( suite, scores );
  Evaluation::Score error = eval.totalError( scores );
  learnLog<<"total error: "<<error<<std::endl;
  eval.adjustWeights(suite,scores);
  eval.computeScores(suite, scores);
  learnLog<<"total error now: "<<eval.totalError(scores)<<std::endl;
};

void
XBoardInterface::result()
{
  XBoardBase::result();
  saveRecord();
  learn();
  idle = true;
};

void
XBoardInterface::setboard()
{
  learningFile.open("position.epd");
  TestPosition pos;
  pos.readEPD(input);
  delete game;
  game = new Game;
  //  std::cout<<"forsythe: "<<pos.forsythe()<<std::endl;
  game->forsytheString(pos.forsythe());
  if (pos.getColorToMove() == "b")
    game->doNullMove();
  //game->board().displayOn(std::cout);
  //delete record;
  //record = 0;
};

void
XBoardInterface::undo()
{
  //std::cout<<"stack size: "<<moves.size()<<std::endl;
  moves.top()->takeBackOn(*getGame());
  moves.pop();
};

void
XBoardInterface::makeMove(SmartPointer<Move> move)
{
  moves.push(move);
  move->makeOn(*getGame());
};

void
XBoardInterface::remove()
{
  undo();
  undo();
};

void
XBoardInterface::board()
{
  getGame()->board().displayOn(output);
};


void
XBoardInterface::signalHandler(int )
{
  alarm(600);
  if (!idle)
    return;
  std::cout<<"resume"<<std::endl;
  
};
