/* GNU Chess 5.0 - cmd.c - command parser
   Copyright (c) 1999-2002 Free Software Foundation, Inc.

   GNU Chess is based on the two research programs 
   Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.

   GNU Chess 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, or (at your option)
   any later version.

   GNU Chess is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Chess; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Contact Info: 
     bug-gnu-chess@gnu.org
     cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>

#include "version.h"
#include "util.h"
#include "movelist.h"
#include "eval.h"
#include "book.h"
#include "game.h"
#include "move.h"
#include "movelist.h"
#include "option.h"
#include "ttable.h"
#include "piece.h"
#include "pgn.h"
#include "protocol.h"
#include "epd.h"
#include "input.h"
#include "cmd.h"
#include "config.h"
#include "searchparams.h"
#include "searchdata.h"
#include "searchstats.h"
#include "log.h"
#include "players.h"
#include "solve.h"
#include "genmove.h"
#include "atak.h"
#include "hung.h"
#include "quiesce.h"
#include "test.h"


char *input_cmd;


// [VdB] Borrowed from Polyglot but written by me!

void format_xboard_option_line(char * option_line, option_t *opt){
    int j;
    char option_string[MAXSTR];
    strcpy(option_line,"");
        // buffer overflow alert
    strcat(option_line,"feature option=\"");
    sprintf(option_string,"%s",opt->name);
    strcat(option_line,option_string);
    sprintf(option_string," -%s",opt->type);
    strcat(option_line,option_string);
    if(!IS_BUTTON(opt->type) && strcmp(opt->type,"combo")){
        if(strcmp(opt->type,"check")){
            sprintf(option_string," %s",opt->value);
        }else{
            sprintf(option_string," %d",
                    my_string_case_equal(opt->value,"true")||
                    my_string_equal(opt->value,"1")
                    ?1:0);
        }
        strcat(option_line,option_string);
    }
    if(IS_SPIN(opt->type)){
        sprintf(option_string," %s",opt->min);
            strcat(option_line,option_string);
    }
    if(IS_SPIN(opt->type)){
        sprintf(option_string," %s",opt->max);
        strcat(option_line,option_string);
    }
    for(j=0;j<opt->var_nb;j++){
        if(!strcmp(opt->var[j],opt->value)){
            sprintf(option_string," *%s",opt->var[j]);
        }else{
            sprintf(option_string," %s",opt->var[j]);
        }
        strcat(option_line,option_string);
        if(j!=opt->var_nb-1){
            strcat(option_line," ///");
        }
    }
    strcat(option_line,"\"");

}

void xboard_send_options(){
  char option_line[MAXSTR]="";
  option_t *opt;
  
  
  OptionStartIter(Option);
  while((opt=OptionNext(Option))){
    format_xboard_option_line(option_line,opt);
    if(opt->mode & MODE_XBOARD){
      Output("%s\n",option_line);
    }
  }       
  
}

void DoHumanMove(){

  board_t board[1];
  int move;
  int state;

  BoardFromGame(board,Protocol->game,BOARD_LAST);
  MoveFromString (board, &move, token[0]);
  if (move != NOMOVE) {
    MoveMake(board, move);
    Protocol->game->RealGameCnt = board->GameCnt;
    if(Protocol->game->RealGameCnt>=Protocol->NextPeriod){
      Protocol->NextPeriod+=2*Protocol->TCMove;
    }
    Protocol->game->boards[Protocol->game->RealGameCnt-Protocol->game->InitialGameCnt-1].move=move;
    OutputConsole("%d. ",board->GameCnt/2+1);
    OutputConsole("%s",token[0]);
    OutputConsole("\n");
    if(Protocol->console){
      BoardShow (board); 
    }
    
    state=BoardState(board);
    if(state==BOARD_WHITE_WINS){
      Output("1-0 {White Mates}\n");
      Protocol->game->result=R_WHITE_WINS;
    }else if(state==BOARD_BLACK_WINS){
      Output("0-1 {Black Mates}\n");
      Protocol->game->result=R_BLACK_WINS;
    }else if(state==BOARD_STALEMATE){
      Output("1/2-1/2 {stalemate}\n");
      Protocol->game->result=R_DRAW;
    }else if(state==BOARD_50MOVERULE){
      Output("1/2-1/2 {Draw by 50 move rule}\n");
      Protocol->game->result=R_DRAW;
    }else if(state==BOARD_3FOLD_REPETITION){
      Output("1/2-1/2 {Draw by 3-fold repetition}\n");
      Protocol->game->result=R_DRAW;
    }
    
    SET (Protocol->state_flags, THINK);
  }
  else {
    if(MoveLookAlike(token[0])){
      Output("Illegal move: %s\n",token[0]);
    }else{
      Output("Error (unknown command): %s\n",token[0]);
    }
    CLEAR(Protocol->state_flags, THINK);
  }  
}


/*
 * Splitting input is actually not neccessary, but we find
 * tokens separated by whitespace and put pointers on them.
 * How many do we need? We just take 3 for now. Check if the
 * fact that tokens are not 0-terminated but whitespace-terminated
 * generates bugs. (Already killed one bug in move.c)
 * We also kill trailing whitespace. (The trailing '\n' might
 * be really annoying otherwise.)
 */

#define TOKENS 3

char *token[TOKENS];

void cmd_accepted(void){
    // ignore for now
}

void cmd_analyze(void)
{
  /*
   * "analyze" mode is similar to force, hard and post together
   * in that it produces a text output like post, but must
   * think indefinitely like ponder.
   *
   * Some additional output is expected in command ".\n" but if ignored
   * this should not be sent any more
   */

   if (!(Protocol->state_flags & ANALYZE)){
     Protocol->preanalyze_flags=Protocol->state_flags; /* save these flags for exit */
     SET (Protocol->state_flags, ANALYZE);
     cmd_post();
     cmd_force();
     cmd_hard();
   }
}

void cmd_bk(void)
{
  /* Print moves from Open Book for Xboard/WinBoard */
  /* Lines must start with " " and end with blank line */
  /* No need to test for xboard as it is generally useful */

  board_t board[1];
  movelist_t movelist[1];
  BoardFromGame(board,Protocol->game,BOARD_LAST);
  if(OptionGetBool(Option,"OwnBook")){
    BookQuery(Book,board,movelist);
  }
  MoveListShow(board,movelist);
  Output("\n");
}

// [VdB] Rewritten to use new option system
void cmd_book(void)
{
    if (tokeneq(token[1], "add")){
	char from[MAXSTR], to[MAXSTR];
	if(sscanf(token[2],"%128s %128s",from,to)==2){
	    BookAdd(from,to);
	}else{
	    Output("The syntax to add a new book is:\n  book add file.pgn book.dat\n");  
	}
    } else if (tokeneq(token[1],"convert")){
	char from[MAXSTR], to[MAXSTR];
	if(sscanf(token[2],"%128s %128s",from,to)==2){
	    BookConvert(to,from);
	}else{
	    Output("The syntax to convert a book to polyglot format is: \n  book convert book.dat book.bin\n");
	}
    } else if (tokeneq (token[1], "on")) {
    OptionSet(Option,"OwnBook","true");
    Output("book now on.\n");
  } else if (tokeneq (token[1], "off")) {
    OptionSet(Option,"OwnBook","off");
    Output("book now off.\n");
  } else if (tokeneq (token[1], "best")) {
    OptionSet(Option,"BookRandom","false");
    Output("book now best.\n");
  } else if (tokeneq (token[1], "random")) {
    OptionSet(Option,"BookRandom","true");
    Output("book now random.\n");
  }
}

/* Our opponent is a computer */
void cmd_computer(void) {}

/* Ignore draw offers */
void cmd_draw(void) {}

void cmd_dump(void)
{
    if (tokeneq (token[1], "off")){
      gnuchess_set_option("DumpTree","0");
      Output ("Stop dumping tree\n");
    }else if (tokeneq (token[1], "on")){
      gnuchess_set_option("DumpTree","1");
      Output ("Dumping tree to %s\n",OptionGetString(Option,"DumpFile"));
    }
}



void cmd_easy(void) { CLEAR (Protocol->state_flags, HARD); }

/* Predecessor to setboard */
void cmd_edit(void) 
{
  Output("tellusererror command 'edit' not implemented\n");
}

void cmd_exit(void) 
{ 
  /*
   * "exit" is a synonym for quit except in engine mode
   * when it means leave analyze mode
   */

  if ( Protocol->state_flags & ANALYZE ){
	Protocol->state_flags = Protocol->preanalyze_flags ; /* this implicitly clears ANALYZE flag */
  } else {
    cmd_quit(); 
  }
   

}

void cmd_force(void) { SET (Protocol->state_flags, MANUAL); }

void cmd_go(void)
{
  SET (Protocol->state_flags, THINK);
  CLEAR (Protocol->state_flags, MANUAL);

  Protocol->computer=SideToMove(Protocol->game->RealGameCnt);
}

void cmd_hard(void) { SET (Protocol->state_flags, HARD); }

void cmd_hash(void)
{
    if (tokeneq (token[1], "off")){
      gnuchess_set_option("UseHash","0");
      Output ("Hashing %s\n", "off");
    }else if (tokeneq (token[1], "on")){
      gnuchess_set_option("UseHash","1");
      Output ("Hashing %s\n", "on");
    }
}

void cmd_hashsize(void){
    unsigned int i;
    if (token[1][0] == 0) {
	Output("Current HashSize is %d slots\n", TTable->Size);
    } else {
	i = atoi(token[1]);
	TTableSetSize(TTable,i);
    }
}

/* Give a possible move for the player to play */
void cmd_hint(void)
{
    int move, tt_move,score;
    uint8_t flag;
    char SANmv[SANSZ];
    board_t board[1];
    uint8_t tt_depth;
    movelist_t movelist[1];
    leaf *l=NULL;
    
    move=NOMOVE;
    tt_move=NOMOVE;
    BoardFromGame(board,Protocol->game,BOARD_LAST);
    GenMoves (board,movelist);
    if (movelist->gencnt == 1){
	l= movelist->moves;
	move=l->move;
    }
    if((move==NOMOVE) && OptionGetBool(Option,"OwnBook")){
	BookQuery(Book,board,movelist);
	l=MoveListPickBest(movelist);
	if(l){
	    move=l->move;
	}
    }
    if(move==NOMOVE){
	TTableGet(TTable,board, 0,&tt_depth, &flag, &score,&tt_move);
	if(flag && tt_move!=NOMOVE){
	    move=tt_move;
	}
    }
    if(move!=NOMOVE){
	MoveToSAN(board,move,SANmv);
	Output("Hint: %s\n", SANmv);
    }
    return;
}

void cmd_level(void)
{
  float TCTime1, TCTime2;
  Protocol->SearchDepth=0;
  Protocol->ST=0;
  if(sscanf (token[1],
             "%d %f:%f %d",
             &Protocol->TCMove,
             &TCTime1,
             &TCTime2,
             &Protocol->TCinc)==4){
      Protocol->TCTime=TCTime1+TCTime2/60;
  }else if(sscanf (token[1], "%d %f %d", 
		   &Protocol->TCMove, 
		   &Protocol->TCTime, 
		   &Protocol->TCinc)!=3){
      Output("Error (incorrect number of parameters):%s %s\n",token[0],token[1]);
  }
  if(Protocol->TCMove){
    Protocol->NextPeriod=Protocol->game->RealGameCnt+2*Protocol->TCMove;
  }
  Protocol->TimeLimit[white] = Protocol->TimeLimit[black] 
    = Protocol->TCTime * 60;

  if(Protocol->TCMove){
    OutputConsole ("Time Control: %d moves in %.2f secs\n", 
	     Protocol->TCMove, 60*Protocol->TCTime);
  }else if(!Protocol->TCinc){
    OutputConsole ("Time Control: sudden death in %.2f secs\n", 
	     60*Protocol->TCTime);
  }else if(Protocol->TCinc){
    OutputConsole("Fischer increment of %d seconds\n",Protocol->TCinc);
  }
}

void cmd_list(void)
{
  if (token[1][0] == '?') {
    Output("name    - list known players alphabetically\n");
    Output("score   - list by GNU best result first \n");
    Output("reverse - list by GNU worst result first\n");
  } else {
    if (token[1][0] == '\0') DBListPlayer("rscore");
    else DBListPlayer(token[1]);
  }
}

void cmd_load(void)
{
  board_t board[1];
  char fen[MAXFEN];
  int index;
  char *space;
  int doit=false;

  space=strchr(token[1],' ');
  if(space){
    index=atoi(space+1);
    *space='\0';
  }else{
    index=0;
  }
  if(tokeneq(token[1],"next")){
    if(EPDIterNext(EPDIter,board,NULL,NULL)){
      doit=true;
    }else{
      Output ("File position exceeded\n");
    }
  }else if(tokeneq(token[1],"close")){
    EPDIterClose(EPDIter);
  }else{
    if(!EPDIterStart(EPDIter,token[1])){
      Output ("Error opening file %s\n", token[1]);
    }
    while (index--){
      if(!EPDIterNext(EPDIter,board,NULL,NULL)){
	  Output ("File position exceeded\n");
	  return; 
      }
    }
    if(EPDIterNext(EPDIter,board,NULL,NULL)){
      doit=true;
    }else{
      Output ("File position exceeded\n");
    }
  }
  if(doit){
      BoardToFen(board,fen);
      BoardShow(board);
      GameSetBoard(Protocol->game,fen);
      if(Protocol->TCMove){
	Protocol->NextPeriod=
	  ((Protocol->game->RealGameCnt/(2*Protocol->TCMove))+2)*(2*Protocol->TCMove);
      }
  }
}


void cmd_manual(void) { 
    SET (Protocol->state_flags, MANUAL); 
}

void cmd_memory(void) {

    gnuchess_set_option("Hash",token[1]);
}

/* Move now, not applicable */
void cmd_movenow(void) {}
  
void cmd_name(void)
{
  my_string_set(&Protocol->name,token[1]);
}

void cmd_new(void)
{
    board_t board[1];
    GameNew(Protocol->game);
    
    /* Protocol specification for ANALYZE says "new" does not end analysis */
    if (!(Protocol->state_flags & ANALYZE)){
	CLEAR (Protocol->state_flags, MANUAL);
    }
    CLEAR (Protocol->state_flags, THINK);
    if (Protocol->console) {
	BoardFromGame(board,Protocol->game,BOARD_LAST);
	BoardShow (board);
    }
}

void cmd_nopost(void) {	
    CLEAR (Protocol->state_flags, POST); 
}

void cmd_nps(void) {
    Protocol->NPS=atoi(token[1]);
}

void cmd_null(void)
{
  if (tokeneq (token[1], "off")){
    gnuchess_set_option("NullMovePruning","0");
    Output ("Null moves %s\n", "off");
  }
  else if (tokeneq (token[1], "on")){
    gnuchess_set_option("NullMovePruning","1");
    Output ("Null moves %s\n", "off");
  }
}

void cmd_option(void){
    char *p, *q, *r, *name, *value;
    char *input_cmd_c;
    option_t *opt;
    
    input_cmd_c=strdup(input_cmd);
  
    p=strchr(input_cmd,' ');
    if(!p){
	Output("Error (incorrect syntax):%s\n",input_cmd);
	return;
    }
    while(*p==' ') p++;
    q=strchr(input_cmd,'=');
    r=q;
    if(!q){
	q=input_cmd_c+strlen(input_cmd_c)-1;
	while(isspace(*q)) q--;
	q++;
	*q='\0';
	name=p;
	value="<empty>";
	goto done;
    }
    q--;
    while(*q==' ')q--;
    q++;
    *q='\0';
    name=p;
    r++;
    while(*r==' ')r++;
    q=input_cmd_c+strlen(input_cmd_c)-1;
    while(isspace(*q)) q--;
    q++;
    *q='\0';
    value=r;
 done:
    opt=OptionFind(Option,name);
    if(!opt){
	Output("Error (unknown option): %s\n",name);
	return;
    }
    if(!IS_BUTTON(opt->type) && my_string_equal(value,"<empty>")){
	Output("Error (not a button): %s\n",name);
	return;
    }
    if(IS_BUTTON(opt->type) && !my_string_equal(value,"<empty>")){
	Output("Error (option is button): %s\n",name);
	return;
    }  
    gnuchess_set_option(name,value);
    free(input_cmd_c);
}

void cmd_otim(void) {
    Protocol->TimeLimit[1^Protocol->computer] = atoi(token[1]) / 100.0f ;
}


void cmd_pgnload(void) { 
  int index;
  char *space;
  board_t board[1];
  int doit=false;

  space=strchr(token[1],' ');
  if(space){
    index=atoi(space+1);
    *space='\0';
  }else{
    index=0;
  }
  if(tokeneq(token[1],"next")){
    if(PGNIterNext(PGNIter,Protocol->game)){
      doit=true;
    }else{
      Output ("File position exceeded\n");
    }
  }else if(tokeneq(token[1],"close")){
    PGNIterClose(PGNIter);
  }else{
    if(!PGNIterStart(PGNIter,token[1])){
      Output ("Error opening file %s\n", token[1]);
    }
    while (index--){
      if(!PGNIterNext(PGNIter,Protocol->game)){
	  Output ("File position exceeded\n");
	  return; 
      }
    }
    if(PGNIterNext(PGNIter,Protocol->game)){
      doit=true;
    }else{
      Output ("File position exceeded\n");
    }
  }
  if(doit){
    BoardFromGame(board,Protocol->game,BOARD_LAST);
    BoardShow(board);
    if(Protocol->TCMove){
      Protocol->NextPeriod=
	((Protocol->game->RealGameCnt/(2*Protocol->TCMove))+2)*(2*Protocol->TCMove);
    }
  }
}

/*
 * XXX - Filenames with spaces will break here,
 * do we want to support them? I vote for "no" 
 *   - Lukas
 */
void cmd_pgnsave(void)
{
  if ( strlen(token[1]) > 0 )
    GameSave(Protocol->game,token[1]);
  else
    Output("Invalid filename.\n");
}

void cmd_playother(void){
    Protocol->computer=1^SideToMove(Protocol->game->RealGameCnt);
    CLEAR(Protocol->state_flags,MANUAL);
    CLEAR(Protocol->state_flags,THINK);
}
 
void cmd_ping(void)
{
  Output("pong %s\n", token[1]);
}

void cmd_isready(void)
{
  Output("readyok\n");
}
 
void cmd_post(void) { SET (Protocol->state_flags, POST); }

void cmd_protover(void)
{
#ifdef DEBUG
    char *debug_s="(debug)";
#else
    char *debug_s="";
#endif
#ifdef __i386__
    char *mode_s="-32";
#else
    char *mode_s="-64";
#endif
    Output("feature done=0\n");
    Output("feature myname=\"%s %s%s%s\"\n",PROGRAM, VERSION,mode_s,debug_s);
    Output("feature setboard=1\n"
	   "feature analyze=1\n"
	   "feature ping=1\n"
	   "feature draw=0\n"
	   "feature sigint=0\n"
	   "feature memory=1\n"
	   "feature nps=1\n"
	   "feature colors=0\n"
	   "feature playother=1\n"
	   "feature name=1\n"
	   "feature variants=\"normal\"\n");
    
    xboard_send_options();
    
    Output("feature done=1\n"); 

}

void cmd_quit(void) { 
    SET (Protocol->state_flags, QUIT); 
}

void cmd_random(void) {}

void cmd_rating(void)
{
  if(Protocol->computer==white){
    Protocol->game->white_rating = atoi(token[1]);
    Protocol->game->black_rating = atoi(token[2]);
  }else{
    Protocol->game->white_rating = atoi(token[2]);
    Protocol->game->black_rating = atoi(token[1]);
  }
  OutputConsole("my rating = %d, opponent rating = %d\n",token[1],token[2]); 
}

void cmd_remove(void)
{
  board_t board[1];
  if(Protocol->game->RealGameCnt-Protocol->game->InitialGameCnt>=2){
    Protocol->game->result=R_NORESULT;
    my_string_clear(&Protocol->game->result_str);
    Protocol->game->RealGameCnt-=2;
    Protocol->TimeLimit[SideToMove(Protocol->game->RealGameCnt)] += 
	Protocol->game->boards[Protocol->game->RealGameCnt-Protocol->game->InitialGameCnt].et;
    Protocol->TimeLimit[1^(SideToMove(Protocol->game->RealGameCnt))] += 
	Protocol->game->boards[(Protocol->game->RealGameCnt-Protocol->game->InitialGameCnt)+1].et;
    if(Protocol->TCMove){
      if(Protocol->NextPeriod-Protocol->game->RealGameCnt>2*Protocol->TCMove){
	Protocol->NextPeriod-=2*Protocol->TCMove;
      }
    }
    if (Protocol->console){
      BoardFromGame(board,Protocol->game,BOARD_LAST);
      BoardShow (board);
    }
  }else{
    OutputConsole("No moves to undo! \n");
  }
}


void cmd_result(void)
{
    const char *gamefile;
    gamefile=(const char *)OptionGetString(Option, "GameFile");
    Log("result: %s\n",token[1]);
    OutputConsole("Save to %s\n",gamefile);
    my_string_set(&Protocol->game->result_str,token[1]);
    if (strstr(Protocol->game->result_str,"1-0")) {
	Protocol->game->result= R_WHITE_WINS;
    }else if (strstr(Protocol->game->result_str,"0-1")){
	Protocol->game->result= R_BLACK_WINS;
    }else if (strstr(Protocol->game->result_str,"1/2-1/2")){
	Protocol->game->result= R_DRAW;
    }
    GameSave (Protocol->game,gamefile);
    DBUpdatePlayer ((char *)Protocol->name, token[1]);
}
	
void cmd_save(void)
{  
  board_t board[1];
  BoardFromGame(board,Protocol->game,BOARD_LAST);
  if ( strlen(token[1]) > 0 )
    SaveEPD (board,token[1]);
  else
    Output("Invalid filename.\n");
}

void cmd_sd(void)
{
    Protocol->SearchDepth = atoi (token[1]);
    OutputConsole("Search to a depth of %d\n",Protocol->SearchDepth);
}

void cmd_setboard(void)
{
  board_t board[1];
  if(BoardFromFen(board,token[1])==EPD_SUCCESS){
    GameSetBoard(Protocol->game,token[1]);
    if (Protocol->console) {
	BoardShow (board);
    }
    if(Protocol->TCMove){
      Protocol->NextPeriod=
	((Protocol->game->RealGameCnt/(2*Protocol->TCMove))+1)*(2*Protocol->TCMove);
    }
  }else{
    Output("tellusererror Illegal FEN\n");
  }
  
}

void cmd_solve(void) { 
    searchparams_t searchparams[1];
    SearchParamsCreate(searchparams,Protocol);
    Solve (token[1],searchparams); 
}

/* Set total time for move to be N seconds is "st N" */
void cmd_st(void) 
{
    Protocol->ST=atoi(token[1]);
}

void cmd_time(void)
{
    Protocol->TimeLimit[Protocol->computer] = atoi(token[1]) / 100.0f ;
}

void cmd_undo(void)
{
  board_t board[1];
  if(Protocol->game->RealGameCnt-Protocol->game->InitialGameCnt>=1){
    Protocol->game->result=R_NORESULT;
    my_string_clear(&Protocol->game->result_str);

    Protocol->game->RealGameCnt--;

    // XXX Is this correct?
    Protocol->computer=1^SideToMove(Protocol->game->RealGameCnt);

    Protocol->TimeLimit[SideToMove(Protocol->game->RealGameCnt)] += 
      Protocol->game->boards[Protocol->game->RealGameCnt-Protocol->game->InitialGameCnt].et;
    if(Protocol->TCMove){
      if(Protocol->NextPeriod-Protocol->game->RealGameCnt>2*Protocol->TCMove){
	Protocol->NextPeriod-=2*Protocol->TCMove;
      }
    }
    if (Protocol->console) {
      BoardFromGame(board,Protocol->game,BOARD_LAST);
      BoardShow (board);
    }
  }else{
    OutputConsole("No moves to undo! \n");
  }
}

void cmd_usage(void) 
{
      Output (
     "\n"
     " Usage: %s [OPTION]\n"
     "\n"
     " -h, --help                 display this help and exit\n"
     " -v, --version              display version information and exit\n" 
     "\n"
     " -x, --xboard               start in xboard mode\n"
     " -p, --post   	          start up showing thinking\n"
     " -e, --easy   	          disable thinking in opponents time\n"
     " -m, --manual  	          enable manual mode\n"
     " -s size, --hashsize=size   specify hashtable size in slots\n"
     " -c file, --config=file     load settings from file\n"
     "\n"
     " Options xboard and post are accepted without leading dashes\n"
     " for backward compatibility\n"
     "\n"
     "Report bugs to <bug-gnu-chess@gnu.org>.\n"
     "\n", Protocol->progname);
     }


/* Play variant, we instruct interface in protover we play normal */
void cmd_variant(void) {}

void cmd_version(void)
{
     Output("%s %s\n", PROGRAM, VERSION);
}

void cmd_xboard(void)
{
    Protocol->console=0;
}

/*
 * Command with subcommands, could write secondary method
 * tables here
 */

void cmd_show (void)
/************************************************************************
 *
 *  The show command driver section.
 *
 ************************************************************************/
{
  searchstats_t searchstats[1];
  searchdata_t searchdata[1];
  searchparams_t searchparams[1];
  movelist_t movelist[1];
  int hunged[2];
  board_t board[1];
  BoardFromGame(board,Protocol->game,BOARD_LAST);
  SearchStatsInit(searchstats);
  SearchDataInit(searchdata,board);
  SearchParamsCreate(searchparams,Protocol);
  BitBoard pieces[2];
  if (tokeneq (token[1], "board"))
      BoardShow (board);
   else if (tokeneq (token[1], "rating"))
   {
     Output("My rating = %d\n",Protocol->computer==white?Protocol->game->white_rating:Protocol->game->black_rating);
      Output("Opponent rating = %d\n",Protocol->computer==white?Protocol->game->black_rating:Protocol->game->white_rating);
   } 
   else if (tokeneq (token[1], "moves")) {
      GenMoves (board,movelist);      
      MoveListShow (board,movelist);
      Output ("No. of moves generated = %ld\n", movelist->gencnt);
   }
   else if (tokeneq (token[1], "escape")) {
     GenPseudoCheckEscapes (board,movelist);      
     MoveListShow (board,movelist);
     Output ("No. of moves generated = %ld\n", movelist->gencnt);
   }
   else if (tokeneq (token[1], "noncapture"))
   {
     GenPseudoNonCaptures (board,movelist);      
     MoveListFilter (board,movelist);
     MoveListShow (board,movelist);
     Output ("No. of moves generated = %ld\n", movelist->gencnt);
   }
   else if (tokeneq (token[1], "capture"))
   {
      GenPseudoCaptures (board,movelist);      
      MoveListFilter (board,movelist);
      MoveListShow (board,movelist);
      Output ("No. of moves generated = %ld\n", movelist->gencnt);
   }
   else if (tokeneq (token[1], "quietchecks"))
   {
      GenPseudoQuietChecks (board,movelist);      
      MoveListFilter (board,movelist);
      MoveListShow (board,movelist);
      Output ("No. of moves generated = %ld\n", movelist->gencnt);
   }
     else if (tokeneq (token[1], "eval") || tokeneq (token[1], "score"))
   {
      int s, wp, bp, wk, bk;
      BitBoard *b;
      int alpha, beta;
      GenAtaks (board);
      FindPins (board,&pinned);
      hunged[white] = EvalHung(board,white);
      hunged[black] = EvalHung(board,black);
      b = board->b[white];
      pieces[white] = b[knight] | b[bishop] | b[rook] | b[queen]; 
      b = board->b[black];
      pieces[black] = b[knight] | b[bishop] | b[rook] | b[queen]; 
      wp = ScoreP (board,searchparams,white);
      bp = ScoreP (board,searchparams,black);
      wk = ScoreK (board,searchparams,white);
      bk = ScoreK (board,searchparams,black);
      Output ("White:  Mat:%4d/%4d  P:%d  N:%d  B:%d  R:%d  Q:%d  K:%d  Dev:%d  h:%d \n",
	      board->pmaterial[white], 
	      board->material[white], 
	      wp, 
	      ScoreN(board,searchparams,white), 
	      ScoreB(board,searchparams,white), 
	      ScoreR(board,searchparams,white), 
	      ScoreQ(board,searchparams,white), 
	      wk, 
	      ScoreDev(board,searchparams,white), 
	      hunged[white]);
      Output ("Black:  Mat:%4d/%4d  P:%d  N:%d  B:%d  R:%d  Q:%d  K:%d  Dev:%d  h:%d \n",
	      board->pmaterial[black], 
	      board->material[black], 
	      bp, 
	      ScoreN(board,searchparams,black), 
	      ScoreB(board,searchparams,black), 
	      ScoreR(board,searchparams,black), 
	      ScoreQ(board,searchparams,black), 
	      bk,
	      ScoreDev(board,searchparams,black), 
	      hunged[black]);
      Output ("Phase: %d\t", board->phase);
      if(sscanf(token[2],"%d %d",&alpha,&beta)!=2){
	  alpha=-INFIN;
	  beta=INFIN;
      }
      if(EvaluateDraw(board)){
	  s=DRAWSCORE;
      }else{
	  s = Evaluate (board, 
			searchparams, 
			searchdata,
			searchstats, 
			alpha, 
			beta);
      }
      Output ("score = %d\n", s);
      Output ("\n");
   }else if (tokeneq (token[1], "game")){
     GameShow (Protocol->game);
   }else if (tokeneq (token[1], "hash")){
       TTShow(TTable,board);
   }else if (tokeneq (token[1], "pin")) {
      BitBoard b;
      GenAtaks (board);
      FindPins (board, &b);
      BitBoardShow (b);
   }else if (tokeneq(token[1], "quiesce")){
       int s,alpha, beta, best_move;
       if(sscanf(token[2],"%d %d",&alpha,&beta)!=2){
	   alpha=-INFIN;
	   beta=INFIN;
       }
       s=Quiesce(board,
		 searchparams,
		 searchdata,
		 searchstats,
		 1,
		 alpha,
		 beta,
		 0,
		 &best_move);
       Output ("score = %d best_move=%s\n", s,AlgbrMove(best_move));
       Output ("\n");
   }
}

void cmd_test (void)
/*************************************************************************
 *
 *  The test command driver section.
 *
 *************************************************************************/
{
  if (tokeneq (token[1], "movelist"))
    TestMoveList ();
  else if (tokeneq (token[1], "capture"))
    TestCaptureList ();
  else if (tokeneq (token[1], "movegenspeed"))
    TestMoveGenSpeed ();
  else if (tokeneq (token[1], "capturespeed"))
    TestCaptureGenSpeed ();
  else if (tokeneq (token[1], "eval"))
    TestEval ();
  else if (tokeneq (token[1], "evalspeed"))
    TestEvalSpeed ();
}

/*
 * This is more or less copied from the readme, and the
 * parser is not very clever, so the lines containing
 * command names should not be indented, the lines with
 * explanations following them should be indented. Do not
 * use tabs for indentation, only spaces. CAPITALS are
 * reserved for parameters in the command names. The
 * array must be terminated by two NULLs.
 * 
 * This one should be integrated in the method table.
 * (Very much like docstrings in Lisp.)
 */

static const char * const helpstr[] = {
   "stop",
   " move now",
   "?",
   " move now",
   "+",
   " display current line during thinking",
   ".",
   " diplay current move with time and nodecount during thinking",
   "quit",
   " quit the program.",
   "exit",
   " In analysis mode this stops analysis, otherwise it quits the program.",
   "help",
   " Produces a help blurb corresponding to this list of commands.",
   "book",
   " add - adds a pgn-file (1st argument) to a gnuchess (.dat) book (2nd argument)",
   " convert - convert a gnuchess (.dat) book (1st argument) to a polyglot (.bin) book (2nd argument)",
   " on - enables use of the book",
   " off - disables the use of the book",
   " best - play the best move from the book",
   " random - play a move from the book with probability given by its weight",
   "dump",
   " on - start dumping tree",
   " off - stop dumping tree",
   "version",
   " prints out the version of this program",
   "pgnsave FILENAME",
   " saves the game so far to the file from memory",
   "pgnload FILENAME",
   " loads the game in the file into memory",
   "force",
   "manual",
   " Makes the program stop moving. You may now enter moves",
   " to reach some position in the future.",
   " ",
   "go",
   " Computer takes whichever side is on move and begins its",
   " thinking immediately",
   "post",
   " Arranges for verbose thinking output showing variation, score,",
   " time, depth, etc.",
   "nopost",
   " Turns off verbose thinking output",
   "name NAME",
   " Lets you input your name. Also writes the log.nnn and a",
   " corresponding game.nnn file. For details please see",
   " auxillary file format sections.",
   "result",
   " Mostly used by Internet Chess server.",
   " add time to the clock with level or time in that case.",
   "rating COMPUTERRATING OPPONENTRATING",
   " Inputs the estimated rating for computer and for its opponent",
   "new",
   " Sets up new game (i.e. positions in original positions)",
   "time",
   " Inputs time left in game for computer in hundredths of a second.",
   " Mostly used by Internet Chess server.",
   "hash",
   " on - enables using the memory hash table to speed search",
   " off - disables the memory hash table",
   "hashsize N",
   " Sets the hash table to permit storage of N positions",
   "memory N",
   " Sets the amount of memory used in MegaBytes.",
   "nps N",
   " Sets the node rate.",
   "option NAME [= VALUE]",
   " If VALUE is not present pushes the button NAME",
   " Otherwise sets option NAME equal to VALUE"
   "null",
   " on - enables using the null move heuristic to speed search",
   " off - disables using the null move heuristic",
   "playother",
   " Leave force mode and play other side.",
   "xboard",
   " on - enables use of xboard/winboard",
   " off - disables use of xboard/winboard",
   "level MOVES MINUTES:SECONDS INCREMENT",
   " Sets time control to be MOVES in MINUTES:SECONDS with each move giving",
   " an INCREMENT (in seconds, i.e. Fischer-style clock).",
   "load",
   "epdload",
   " Loads a position in EPD format from disk into memory.",
   "save",
   "epdsave",
   " Saves game position into EPD format from memory to disk.",
   "sd N",
   " Sets the program to look N ply (half-moves) deep for every",
   " search it performs. If there is a checkmate or other condition",
   " that does not allow that depth, then it will not be ",
   "solve FILENAME",
   "solveepd FILENAME",
   " Solves the positions in FILENAME",
   "remove",
   " Backs up two moves in game history",
   "undo",
   " Backs up one move in game history",
   "usage",
   " Display command line syntax",
   "show",
   " board - displays the current board",
   " time - displays the time settings",
   " moves - shows all moves using one call to routine",
   " escape - shows moves that escape from check using one call to routine",
   " noncapture - shows non-capture moves",
   " quietchecks - shows non-capturing, not discovered checks",
   " capture - shows capture moves",
   " eval [or score] - shows the evaluation per piece and overall; alpha beta may be given as arguments",
   " game - shows moves in game history",
   " pin - shows pinned pieces",
   " hash - shows current hash table entry",
   " quiesce - shows quiescent evaluation of current position; alpha beta may be given as arguments",
   "test",
   " movelist - reads in an epd file and shows legal moves for its entries",
   " capture - reads in an epd file and shows legal captures for its entries",
   " movegenspeed - tests speed of move generator",
   " capturespeed - tests speed of capture move generator",
   " eval - reads in an epd file and shows evaluation for its entries",
   " evalspeed tests speed of the evaluator",
   "bk",
   " show moves from opening book.",
   NULL,
   NULL
};

void cmd_help (void)
/**************************************************************************
 *
 *  Display all the help commands.
 *
 **************************************************************************/
{
   const char * const *p;
   int count, len;

   if (strlen(token[1])>0) {
      for (p=helpstr, count=0; *p; p++) {
	 if  (strncmp(*p, token[1], strlen(token[1])) == 0) {
	    puts(*p);
	    while (*++p && **p != ' ') /* Skip aliases */ ;
	    for (; *p && **p == ' '; p++) {
	       puts(*p);
	    }
	    return;
	 }
      }
      Output("Help for command %s not found\n\n", token[1]);
   }
   Output("List of commands: (help COMMAND to get more help)\n");
   for (p=helpstr, count=0; *p; p++) {
      len = strcspn(*p, " ");
      if (len > 0) {
	 count += printf("%.*s  ", len, *p);
	 if (count > 60) {
	    count = 0;
	    Output("\n");
	 }
      }
   }
   Output("\n");
}

/*
 * Try a method table, one could also include the documentation
 * strings here
 */

struct methodtable {
  const char *name;
  void (*method) (void);
};

/* Last entry contains to NULL pointers */

/* List commands we don't implement to avoid illegal moving them */

const struct methodtable commands[] = {
  { "?", cmd_movenow },
  { "accepted", cmd_accepted },
  { "analyze", cmd_analyze },
  { "bk", cmd_bk },
  { "book", cmd_book },
  { "computer", cmd_computer },
  { "draw", cmd_draw },
  { "dump", cmd_dump },
  { "easy", cmd_easy },
  { "edit", cmd_edit },
  //  { "epd", cmd_epd },
  { "epdload", cmd_load },
  { "epdsave", cmd_save },
  { "exit", cmd_exit },
  { "force", cmd_force },
  { "go", cmd_go },
  { "hard", cmd_hard },
  { "hash", cmd_hash },
  { "hashsize", cmd_hashsize },
  { "help", cmd_help },
  { "hint", cmd_hint },
  { "level", cmd_level },
  { "list", cmd_list },
  { "load", cmd_load },
  { "manual", cmd_manual },
  { "memory", cmd_memory },
  { "name", cmd_name },
  { "new", cmd_new },
  { "nopost", cmd_nopost },
  { "nps", cmd_nps },
  { "null", cmd_null },
  { "option", cmd_option },
  { "otim", cmd_otim },
  { "playother", cmd_playother },
  { "pgnload", cmd_pgnload },
  { "pgnsave", cmd_pgnsave },
  { "ping", cmd_ping },
  { "post", cmd_post },
  { "protover", cmd_protover },
  { "quit", cmd_quit },
  { "random", cmd_random },
  { "rating", cmd_rating },
  { "remove", cmd_remove },
  { "result", cmd_result },
  { "save", cmd_save },
  { "sd", cmd_sd },
  { "setboard", cmd_setboard },
  { "show", cmd_show },
  { "solve", cmd_solve },
  { "solveepd", cmd_solve },
  { "st", cmd_st },
  { "stop", cmd_movenow },
  { "test", cmd_test },
  { "time", cmd_time },
  { "undo", cmd_undo },
  { "usage", cmd_usage },
  { "variant", cmd_variant },
  { "version", cmd_version },
  { "xboard", cmd_xboard },
  { NULL, NULL }
};

void parse_input(void)
/*************************************************************************
 *
 *  This is the main user command interface driver.
 *
 *************************************************************************/
{
   const struct methodtable * meth;
   
   input_cmd=InputLook();

   Log("parse_input() called, input_cmd = *%s*\n", input_cmd);
   
   split_input(input_cmd);

   for (meth = commands; meth->name != NULL; meth++) {
     if (tokeneq(token[0], meth->name)) {
       meth->method();
       goto done;
     }
   }
   /* OK, no known command, this should be a move */
   DoHumanMove();
 done:
   InputWakeup();
}

