/******************************************
* Needle - a bitboard based chess engine. *
* (C) 2004 Mikko Nummelin                 *
******************************************/

#include "needle.h"
#include "version.h"
#include <signal.h>

#define _XBOARDCLIENT_C

char *n_strtok_r (char *s, const char *delim, char **save_ptr);

Board brd;
Movelist all_moves_list,legal_moves_list;
char move[6];
PVlist principal_variation;

void initialize_game(void){
	clear_hashtable();
	all_moves_list.size=0;
	legal_moves_list.size=0;
	reset_engine_variables();
	reset_board(&brd);
	write_position(&brd,&(evars.ntb));
	generate_moves(&brd,&all_moves_list);
	construct_legal_movelist(&brd,&all_moves_list,&legal_moves_list);
}

void xboardclient(void){
	char command[128],waste[16],base_str[16],*minutes_str,*seconds_str,*ptrptr,move[6];
	int i,ping_value,value,time_arg;
	PVlist principal_variation;

	signal(SIGINT, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	fgets(command,127,stdin);
	if((memcmp(command,"protover",8))||atoi(&(command[9]))<2){
		return; /* Protocol failure */
	}
	printf("feature done=0\n");fflush(stdout);

	/* Initialization */
	alloc_hashtable(14,3);
	init_opening_book();
	initialize_game();
	printf("feature ping=1 setboard=1 usermove=1 draw=0 sigint=0 sigterm=0");
	printf(" analyze=0 myname=\"%s %s%s\" variants=\"normal\" colors=0 pause=0\n",
		BASENAME,VERSION,EXTRAVERSION);
	printf("feature done=1\n");fflush(stdout);

	/* Command loop */
	while(1) {
		fgets(command,127,stdin);
		if((!memcmp(command,"new",3))||
			(evars.flags & DISPOSE)) {
			initialize_game();
		}
		if((!memcmp(command,"quit",4))||
			(evars.flags & TERMINATE)) {
			break;
		}
		if(!memcmp(command,"force",5)){
			evars.flags &= ~COMP_WHITE;
			evars.flags &= ~COMP_BLACK;
			stop_clock(&(evars.computers_clock));
			stop_clock(&(evars.opponents_clock));
			stop_clock(&(evars.this_move_clock));
		}
		if(!memcmp(command,"go",2)){
			if(brd.whosmove=='w'){
				evars.flags |= COMP_WHITE;
				evars.flags &= ~COMP_BLACK;
			} else {
				evars.flags &= ~COMP_WHITE;
				evars.flags |= COMP_BLACK;
			}
			start_clock(&(evars.computers_clock));
			stop_clock(&(evars.opponents_clock));
		}
		if(!memcmp(command,"playother",9)){
			if(brd.whosmove=='w'){
				evars.flags &= ~COMP_WHITE;
				evars.flags |= COMP_BLACK;
			} else {
				evars.flags |= COMP_WHITE;
				evars.flags &= ~COMP_BLACK;
			}
			start_clock(&(evars.opponents_clock));
			stop_clock(&(evars.computers_clock));
		}
		if(!memcmp(command,"level",5)){
			sscanf(command,"%s %d %s %d",waste,&(evars.mps),base_str,&(evars.inc));
			minutes_str=n_strtok_r(base_str,":",&ptrptr);
			seconds_str=n_strtok_r(NULL," ",&ptrptr);
			if(!seconds_str){
				evars.base=60*atoi(minutes_str);
			} else {
				evars.base=60*atoi(minutes_str)+atoi(seconds_str);
			}
			if(evars.mps!=0){
				evars.flags |= CONVENTIONAL;
				evars.flags &= ~INCREMENTAL;
				evars.flags &= ~STMOVE;
			} else {
				evars.flags &= ~CONVENTIONAL;
				evars.flags |= INCREMENTAL;
				evars.flags &= ~STMOVE;
			}
		}
		if(!memcmp(command,"st",2)){
			sscanf(command,"%s %d",waste,&(evars.st));
			evars.flags &= ~CONVENTIONAL;
			evars.flags &= ~INCREMENTAL;
			evars.flags |= STMOVE;
		}
		if(!memcmp(command,"sd",2)){
			sscanf(command,"%s %d",waste,&(evars.sd));
		}
		if(!memcmp(command,"time",4)){
			sscanf(command,"%s %d",waste,&time_arg);
			set_clock(&(evars.computers_clock),(double)time_arg/100);
		}
		if(!memcmp(command,"otim",4)){
			sscanf(command,"%s %d",waste,&time_arg);
			set_clock(&(evars.opponents_clock),(double)time_arg/100);
		}
		if(!memcmp(command,"usermove",8)){

			sscanf(command,"%s %s",waste,move);
			i=make_move_from_list(&brd,&legal_moves_list,move);
			if(((evars.flags & COMP_WHITE) && (brd.whosmove=='b'))||
				((evars.flags & COMP_BLACK) && (brd.whosmove=='w'))){
				printf("Illegal move (Not your move) %s\n",move);fflush(stdout);
			} else if(i!=0){
				printf("Illegal move: %s\n",move);fflush(stdout);
			} else {
				write_move(&(evars.ntb),move);
				write_position(&brd,&(evars.ntb));
				if(is_check(&brd)){
					generate_check_evasions(&brd,&all_moves_list);
				} else {
					generate_moves(&brd,&all_moves_list);
				}
				construct_legal_movelist(&brd,
					&all_moves_list,&legal_moves_list);
				if(legal_moves_list.size==0){
					if(is_check(&brd)){
						if(brd.whosmove=='b'){
							printf("1-0 {White mates}\n");
						} else {
							printf("0-1 {Black mates}\n");
						}
					} else {
						printf("1/2-1/2 {Stalemate}\n");
					}
					fflush(stdout);
				}
				if(is_threefold_repetition(&(evars.ntb),
					NULL,brd.half_moves)){
					printf("1/2-1/2 {Threefold repetition}\n");
					fflush(stdout);
				}
				if(brd.half_moves>=100){
					printf("1/2-1/2 {50 moves without progress}\n");
					fflush(stdout);
				}
				if((evars.flags & COMP_WHITE)||(evars.flags & COMP_BLACK)){
					stop_clock(&(evars.opponents_clock));
					start_clock(&(evars.computers_clock));
				}
				if((evars.flags & CONVENTIONAL) && ((brd.full_moves % (2*evars.mps))==1)){
					addtime(&(evars.opponents_clock),(double)evars.base);
					addtime(&(evars.computers_clock),(double)evars.base);
				} else if(evars.flags & INCREMENTAL){
					addtime(&(evars.opponents_clock),(double)evars.inc);
				}
			}
		}
		if(!memcmp(command,"ping",4)){
			sscanf(command,"%s %d",waste,&ping_value);
			printf("pong %d\n",ping_value);fflush(stdout);
		}
		if(!memcmp(command,"setboard",8)){
			import_fen(&brd,&(command[9]));
			init_notebook(&(evars.ntb));
			write_position(&brd,&(evars.ntb));
			if(is_check(&brd)){
				generate_check_evasions(&brd,&all_moves_list);
			} else {
				generate_moves(&brd,&all_moves_list);
			}
			construct_legal_movelist(&brd,
				&all_moves_list,&legal_moves_list);
			evars.flags &= ~COMP_WHITE;
			evars.flags &= ~COMP_BLACK;
			stop_clock(&(evars.computers_clock));
			stop_clock(&(evars.opponents_clock));
			stop_clock(&(evars.this_move_clock));
		}
		if(!memcmp(command,"undo",4)){
			reverse(&brd,&(evars.ntb),1);
			if(is_check(&brd)){
				generate_check_evasions(&brd,&all_moves_list);
			} else {
				generate_moves(&brd,&all_moves_list);
			}
			construct_legal_movelist(&brd,
				&all_moves_list,&legal_moves_list);
		}
		if(!memcmp(command,"remove",6)){
			reverse(&brd,&(evars.ntb),2);
			if(is_check(&brd)){
				generate_check_evasions(&brd,&all_moves_list);
			} else {
				generate_moves(&brd,&all_moves_list);
			}
			construct_legal_movelist(&brd,
				&all_moves_list,&legal_moves_list);
		}
		if(!memcmp(command,"hard",4)){
			evars.flags |= PONDER;
		}
		if(!memcmp(command,"easy",4)){
			evars.flags &= ~PONDER;
		}
		if(((brd.whosmove=='w')&&(evars.flags & COMP_WHITE)) ||
			((brd.whosmove=='b')&&(evars.flags & COMP_BLACK))){
			move[0]=0;
			value=iterativedeep(&brd,32,move,&principal_variation);
			if(value<=-600) {
				printf("resign\n");
				fflush(stdout);
			}
			i=make_move_from_list(&brd,&legal_moves_list,move);
			printf("move %s\n",move);fflush(stdout);
			if(value>=29900) {
				clear_hashtable();
			}
			write_move(&(evars.ntb),move);
			write_position(&brd,&(evars.ntb));
			if(is_check(&brd)){
				generate_check_evasions(&brd,&all_moves_list);
			} else {
				generate_moves(&brd,&all_moves_list);
			}
			construct_legal_movelist(&brd,
				&all_moves_list,&legal_moves_list);
			if(legal_moves_list.size==0){
				if(is_check(&brd)){
					if(brd.whosmove=='b'){
						printf("1-0 {White mates}\n");
					} else {
						printf("0-1 {Black mates}\n");
					}
				} else {
					printf("1/2-1/2 {Stalemate}\n");
				}
				fflush(stdout);
			}
			if(is_threefold_repetition(&(evars.ntb),
				NULL,brd.half_moves)){
				printf("1/2-1/2 {Threefold repetition}\n");
				fflush(stdout);
			}
			if(brd.half_moves>=100){
				printf("1/2-1/2 {50 moves without progress}\n");
				fflush(stdout);
			}
			if((evars.flags & COMP_WHITE)||(evars.flags & COMP_BLACK)){
				stop_clock(&(evars.opponents_clock));
				start_clock(&(evars.computers_clock));
			}
			if((evars.flags & CONVENTIONAL) &&
				((brd.full_moves % (2*evars.mps))==1)) {
				addtime(&(evars.opponents_clock),(double)evars.base);
				addtime(&(evars.computers_clock),(double)evars.base);
			} else if(evars.flags & INCREMENTAL){
				addtime(&(evars.computers_clock),(double)evars.inc);
			}
		}
	}

	/* Program finished */
	free_opening_book();
	free_hashtable();

	return;
}
