# include <XBoard/XBoardBase.h>
# include <iostream>
using namespace Alice;
using namespace Alice::XBoard;

/* The following commands need to be implemented:
   playother: not sent unless requested -- won't be implemented
   ?: is optional. Only for "move now" menu 
   draw
   setboard
   edit: not necessary if the host program implements setboard
   hint: can be ignored
   bk
   undo
   remove
   post
   nopost
   analyze: optional
   pause: optional
   resume: optional
*/


XBoardBase::XBoardBase(std::istream& in, std::ostream& out)
  : input( in ), 
    output( out )
{
  protocolVersion = 1;
  initialize();
# define DECLARE_COMMAND(name) commands[#name] = &XBoardBase::name
  DECLARE_COMMAND( accepted );
  DECLARE_COMMAND( rejected );
  DECLARE_COMMAND( protover );
  DECLARE_COMMAND( ping );
  DECLARE_COMMAND( hard );
  DECLARE_COMMAND( easy );
  DECLARE_COMMAND( force );
  DECLARE_COMMAND( go );
  DECLARE_COMMAND( computer );
  DECLARE_COMMAND( time );
  DECLARE_COMMAND( otim );
  DECLARE_COMMAND( quit );
  DECLARE_COMMAND( level );
  DECLARE_COMMAND( name );
  DECLARE_COMMAND( usermove );
  DECLARE_COMMAND( result );
  DECLARE_COMMAND( black );
  DECLARE_COMMAND( white );
  DECLARE_COMMAND( random );
  DECLARE_COMMAND( xboard );
  DECLARE_COMMAND( sd );
  DECLARE_COMMAND( st );
  DECLARE_COMMAND( rating );
  DECLARE_COMMAND( ics );
  DECLARE_COMMAND( setboard );
  DECLARE_COMMAND( undo );
  DECLARE_COMMAND( remove );
  DECLARE_COMMAND( board );
# undef DECLARE_COMMAND
  // this goes extra since "new" is reserved
  commands["new"] = &XBoardBase::newGame;
  setUpFeatures();
  timeControl.limitType = TimeControl::NORMAL;
  alarm(30);
};

void
XBoardBase::initialize()
{
  pondering = true;
  oppComputer = false;
  setOwnTime( 0 );
  setOtherTime( 0 );
  shouldQuit_ = false;
  timeControl.movesPerSession = 0;
  timeControl.setSecondsPerSession( 0 );
  timeControl.setTimeIncrement( 0);
  opponentName = "";
  mode = GO;
  resultString = "*";
  resultComment = "";
};
XBoardBase::~XBoardBase()
{

};

int
XBoardBase::getProtocolVersion() const
{
  return protocolVersion;
};

XBoardFeature*
XBoardBase::getFeature(const std::string& name)
{
  return features[name];
};

void
XBoardBase::acceptFeature(const std::string& name )
{
  XBoardFeature* feature = features[name];
  if (feature == 0)
    return;
  feature->accept();
};

void
XBoardBase::sendFeatureRequest(  )
{
  output << "feature done=0"<<std::endl;
  output << "feature variants=\"normal\"" << std::endl;
  for (std::map<std::string, XBoardFeature*>::iterator it = features.begin();
       it != features.end(); ++ it)
    {
      if (it->second && (!it->second->isAccepted()))
	output << "feature " << it->first << "=" 
	    << it->second->getWanted() << std::endl;
    }
  output << "feature done=1"<<std::endl;
};

void
XBoardBase::setUpFeatures()
{
  //                                       default, wanted
  features["ping"]      = new XBoardFeature( false, true );
  features["setboard"]  = new XBoardFeature( false, true );
  features["playother"] = new XBoardFeature( false, true );
  // we disable SAN since FICS sends ambiguous moves
  // Actually, xboard does the same. They appear to interpret
  // the SAN standard differently.
  features["san"]       = new XBoardFeature( false, false);
  features["usermove"]  = new XBoardFeature( false, true );
  features["time"]      = new XBoardFeature( true,  true );
  features["draw"]      = new XBoardFeature( true,  true );
  features["sigint"]    = new XBoardFeature( true,  false ); // for now
  features["sigterm"]   = new XBoardFeature( true,  false );
  features["reuse"]     = new XBoardFeature( true,  true );
  features["analyze"]   = new XBoardFeature( true,  false );
  features["colors"]    = new XBoardFeature( true,  false );
  features["ics"]       = new XBoardFeature( false, true );
  features["name"]      = new XBoardFeature( false, true );
  features["pause"]     = new XBoardFeature( false, false );
};

int
XBoardBase::getOwnTime() const
{
  return timeControl.ownTime;
};

int
XBoardBase::getOtherTime() const
{
  return timeControl.otherTime;
};

bool
XBoardBase::shouldPonder() const
{
  return pondering;
};


XBoardBase::Mode
XBoardBase::getMode() const
{
  return mode;
};

bool
XBoardBase::opponentIsComputer() const
{
  return oppComputer;
};

bool
XBoardBase::shouldQuit() const
{
  return shouldQuit_;
};

int
XBoardBase::getMovesPerSession() const
{
  return timeControl.movesPerSession;;
};

int
XBoardBase::getTimePerSession() const
{
  return timeControl.getSecondsPerSession();
};

int
XBoardBase::getTimeIncrement() const
{
  return timeControl.getTimeIncrement();
};

std::string
XBoardBase::getOpponentName() const
{
  return opponentName;
};

std::string
XBoardBase::getResult() const
{
  return resultString;
};

std::string
XBoardBase::getResultComment() const
{
  return resultComment;
};

void
XBoardBase::processCommand()
{
  std::string command;
  input >> command;
  if (! input )
    return;
  Command c = commands[command];
  if (c)
    (this->*c)();

  else
    output << "Error (unknown command): " << command <<std::endl;
};

void
XBoardBase::black()
{

};

void
XBoardBase::white()
{

};

void
XBoardBase::random()
{

};

void
XBoardBase::accepted()
{

  std::string name;
  input >> name;
  XBoardFeature* feature = getFeature(name);
  if (! feature )
    return;
  feature->accept();
};

void
XBoardBase::rejected()
{

  std::string unused;
  input >> unused;
};

void
XBoardBase::computer()
{

  oppComputer = true;
};

void
XBoardBase::quit()
{
  shouldQuit_ = true;

};

void
XBoardBase::easy()
{

  pondering = false;
};

void
XBoardBase::name()
{
  char buffer[256];
  input.getline(buffer, 256);
  opponentName = buffer+1;

};

void
XBoardBase::newGame()
{

};

void
XBoardBase::ping()
{

  int identifier;
  input >> identifier;
  output << "pong "<<identifier<<std::endl;
};

void
XBoardBase::otim()
{
  int t;
  input >> t;
  setOtherTime( t );
};

void
XBoardBase::protover()
{

  input >> protocolVersion;
  if (getProtocolVersion() >= 2)
    sendFeatureRequest( );
};

void
XBoardBase::force()
{
  mode = FORCE;
};

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

void
XBoardBase::hard()
{

  pondering = true;
};

void
XBoardBase::level()
{
  int minutes;
  int increment;
  input >> timeControl.movesPerSession >> minutes >> increment;
  timeControl.setTimeControl( minutes, increment );
};

void
XBoardBase::result()
{
  //std::cout<<"XBoardBase::result()"<<std::endl;
  input >> resultString;
  char buffer[256];
  input.getline(buffer, 256);
  resultComment = buffer+1;
  //std::cout<<"XBoardBase::result(): done"<<std::endl;
  
};

void
XBoardBase::time()
{

  int t;
  input >> t;
  setOwnTime( t );
};

void
XBoardBase::usermove()
{

};

void
XBoardBase::xboard()
{

};

void
XBoardBase::draw()
{

};

void
XBoardBase::setboard()
{

};

void
XBoardBase::edit()
{

};

void
XBoardBase::hint()
{

};

void
XBoardBase::bk()
{

};

void
XBoardBase::undo()
{

};

void
XBoardBase::remove()
{

};

void
XBoardBase::post()
{

};

void
XBoardBase::nopost()
{

};

void
XBoardBase::analyze()
{

};

void
XBoardBase::rating()
{
  input >> myRating >> opponentRating;
};

void
XBoardBase::ics()
{
  input >> icsHostName;
};

void
XBoardBase::pause()
{

};

void
XBoardBase::resume()
{

};

void
XBoardBase::sd()
{
  timeControl.limitType = TimeControl::FIXED_DEPTH;
  input >> maxDepth;
};

void
XBoardBase::st()
{
  timeControl.limitType = timeControl.FIXED_TIME;
  int secs;
  input >> secs;
  timeControl.setSecondsPerSession( secs );
};

XBoardBase::LimitType
XBoardBase::getLimitType() const
{
  return timeControl.limitType;
};

int
XBoardBase::getMaxDepth() const
{
  return maxDepth;
};

int
XBoardBase::getMyRating() const
{
  return myRating;
};

int 
XBoardBase::getOpponentRating() const
{
  return opponentRating;
};

const std::string&
XBoardBase::getIcsHostName() const
{
  return icsHostName;
};

void
XBoardBase::setOwnTime( int t )
{
  timeControl.ownTime  = t;
};

void
XBoardBase::setOtherTime( int t )
{
  timeControl.otherTime  = t;
};

void
XBoardBase::board()
{

};
