#include <stdlib.h>
#include <string.h>
#include <stdio.h>


#include "version.h"
#include "config.h"
#include "uci.h"
#include "move.h"
#include "protocol.h"
#include "input.h"
#include "option.h"
#include "searchparams.h"
#include "searchstats.h"
#include "board.h"
#include "ttable.h"
#include "ptable.h"
#include "log.h"
#include "mainloop.h"

void UciInit(uci_t *uci){
    BoardFromFen(uci->board,StartFen);
    uci->type=UCI_NOTYPE;
    uci->name=NULL;
    my_string_set(&uci->name,"<empty>");
    uci->value=NULL;
    uci->wtime=0;
    uci->winc=0;
    uci->btime=0;
    uci->binc=0;
    uci->ponder=false;
    uci->depth=0;
    uci->nodes=0;
    uci->movetime=0;
    uci->infinite=false;
    uci->movestogo=0;
}

void UciClear(uci_t *uci){
    my_string_clear(&(uci->name));
    my_string_clear(&(uci->value));
    UciInit(uci);
}

void UciShow(uci_t *uci){
    switch(uci->type){
    case UCI_NOTYPE:
	OutputConsole("%s\n","uci_type=UCI_NOTYPE");
	break;
    case UCI_ISREADY:
	OutputConsole("%s\n","uci_type=UCI_ISREADY");
	break;
    case UCI_STOP:
	OutputConsole("%s\n","uci_type=UCI_STOP");
	break;
    case UCI_PONDERHIT:
	OutputConsole("%s\n","uci_type=UCI_PONDERHIT");
	break;
    case UCI_NEWGAME:
	OutputConsole("%s\n","uci_type=UCI_NEWGAME");
	break;
    case UCI_UCI:
	OutputConsole("%s\n","uci_type=UCI_UCI");
	break;
    case UCI_QUIT:
	OutputConsole("%s\n","uci_type=UCI_QUIT");
	break;
    case UCI_POSITION:
	OutputConsole("%s\n","uci_type=UCI_POSITION");
	BoardShow(uci->board);
	break;
    case UCI_SETOPTION:
	OutputConsole("%s\n","uci_type=UCI_SETOPTION");
	if(uci->value){
	    OutputConsole("[%s]=[%s]\n",uci->name,uci->value);
	}else{
	    OutputConsole("Button: [%s]\n",uci->name);
	}
	break;
    case UCI_GO:
	OutputConsole("%s\n","uci_type=UCI_GO");
	OutputConsole("wtime=%d winc=%d btime=%d binc=%d depth=%d nodes=%d " \
		      "movetime=%d ponder=%s infinite=%s\n",uci->wtime,uci->winc,
		      uci->btime,uci->binc,uci->depth,uci->nodes,uci->movetime,
		      uci->ponder?"true":"false",uci->infinite?"true":"false");
	break;
    }
}


void UciFromString(uci_t *uci, char *s){
    char *ss, *sss, *t;
    char *fen=NULL;
    int move;
    int first_time;
    uci->type=UCI_NOTYPE;
    ss=strdup(s);
    sss=strdup(s);
    t=strtok(ss," ");
    if(t==NULL){
	goto done;
    }
    if(!strcmp(t,"position")){
	uci->type=UCI_POSITION;
	t=strtok(NULL," ");
	if(t==NULL){
	    goto done;
	}else if(!strcmp(t,"startpos")){
	    BoardFromFen(uci->board,StartFen);
	    t=strtok(NULL," ");
	    if(t==NULL){
		goto done;
	    }
	}else if(!strcmp(t,"fen")){
	    first_time=true;
	    while(1){
		t=strtok(NULL," ");
		if(t==NULL){
		    break;
		}
		if(!strcmp(t,"moves")){
		    break;
		}
		if(!first_time){
		    my_string_append(&fen," ");
		}
		first_time=false;
		my_string_append(&fen,t);
	    }
	    BoardFromFen(uci->board,fen);
	    if(fen){
		free(fen);
	    }
	    
	}
	if(t==NULL){
	    goto done;
	}
	if(!strcmp(t,"moves")){
	    while(1){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		MoveFromString(uci->board,&move,t);
		if(move==NOMOVE){
		    goto done;
		}
		MoveMake(uci->board,move);
	    }
	}
    }else if(!strcmp(t,"go")){
	uci->type=UCI_GO;
	uci->wtime=0;
	uci->winc=0;
	uci->btime=0;
	uci->binc=0;
	uci->depth=0;
	uci->nodes=0;
	uci->movetime=0;
	uci->movestogo=0;
	uci->ponder=false;
	uci->infinite=false;
	while(1){
	    t=strtok(NULL," ");
	    if(t==NULL){
		goto done;
	    }
	    if(false){
	    }else if(!strcmp(t,"wtime")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->wtime=atoi(t);
	    }else if(!strcmp(t,"wtime")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->wtime=atoi(t);
	    }else if(!strcmp(t,"winc")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->winc=atoi(t);
	    }else if(!strcmp(t,"btime")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->btime=atoi(t);
	    }else if(!strcmp(t,"binc")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->binc=atoi(t);
	    }else if(!strcmp(t,"depth")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->depth=atoi(t);
	    }else if(!strcmp(t,"nodes")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->nodes=atoi(t);
	    }else if(!strcmp(t,"movetime")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->movetime=atoi(t);
	    }else if(!strcmp(t,"movestogo")){
		t=strtok(NULL," ");
		if(t==NULL){
		    goto done;
		}
		uci->movestogo=atoi(t);
	    }else if(!strcmp(t,"ponder")){
		uci->ponder=true;
	    }else if(!strcmp(t,"infinite")){
		uci->infinite=true;
	    }
	}
    }else if(!strcmp(t,"setoption")){
	char *t1,*t2,*t3, *t4;
	int l;
	my_string_set(&uci->name,"<empty>");
	my_string_clear(&uci->value);
	l=strlen(sss);
	uci->type=UCI_SETOPTION;
	t1=strstr(sss,"name");
	t2=strstr(sss,"value");
	if(t1==NULL){
	    goto done;
	}
	if(t2==NULL){           // button
	    t3=t1+4;            // char after name
	    t4=sss+l-1;          // last char
	    while(*t3==' '){    // first non space after name
		t3++;
	    }
	    while(*t4==' '){    // last non space after name
		t4--;
	    }
	    *(t4+1)='\0';
	    my_string_set(&uci->name,t3);
	}else{
	    t3=t1+4;            // char after name
	    t4=t2-1;            // last char of nme
	    while(*t3==' '){    // first non space after name
		t3++;
	    }
	    while(*t4==' '){    // last non space after name
		t4--;
	    }
	    *(t4+1)='\0';
	    my_string_set(&uci->name,t3);

	    t3=t2+5;            // char after value
	    t4=sss+l-1;         // last char
	    while(*t3==' '){    // first non space after value
		t3++;
	    }
	    while(*t4==' '){    // last non space after value
		t4--;
	    }
	    *(t4+1)='\0';
	    my_string_set(&uci->value,t3);
	}
    }else if(!strcmp(t,"isready")){
	uci->type=UCI_ISREADY;
    }else if(!strcmp(t,"ucinewgame")){
	uci->type=UCI_NEWGAME;
    }else if(!strcmp(t,"stop")){
	uci->type=UCI_STOP;
    }else if(!strcmp(t,"quit")){
	uci->type=UCI_QUIT;
    }else if(!strcmp(t,"uci")){
	uci->type=UCI_UCI;
    }
 done:
    free(ss);
    free(sss);
	
}

// taken from polyglot but written by me

static void UciNormalizeType(char *dst, const char* src){
    if(IS_STRING(src)){
	strcpy(dst,"string");
	return;
    }else if(IS_SPIN(src)){
	strcpy(dst,"spin");
    }else if(IS_BUTTON(src)){
	strcpy(dst,"button");
    }else{
	strcpy(dst,src);
    }
}


static void UciOptionLineFormat(char * option_line,option_t *opt){
    char option_string[MAXSTR];
    char type[MAXSTR];
    char value[MAXSTR];
    int j;
    strcpy(option_line,"");
        // buffer overflow alert
    strcat(option_line,"option name");
    sprintf(option_string," %s",opt->name);
    strcat(option_line,option_string);
    UciNormalizeType(type,opt->type);
    sprintf(option_string," type %s",type);
    strcat(option_line,option_string);
    strcpy(value,opt->value);
    if(my_string_case_equal(opt->type,"check")){
	if(my_string_case_equal(opt->value,"0")){
	    strcpy(value,"false");
	}else if(my_string_case_equal(opt->value,"1")){
	    strcpy(value,"true");
	}
    }
    if(!IS_BUTTON(opt->type)){
        sprintf(option_string," default %s",value);
        strcat(option_line,option_string);
    }
    if(IS_SPIN(opt->type)){
        sprintf(option_string," min %s",opt->min);
        strcat(option_line,option_string);
    }
    if(IS_SPIN(opt->type)){
        sprintf(option_string," max %s",opt->max);
        strcat(option_line,option_string);
    }
    for(j=0;j<opt->var_nb;j++){
        sprintf(option_string," var %s",opt->var[j]);
        strcat(option_line,option_string);
    }
}

static void UciSendOptions() {
#ifdef DEBUG
    char *debug_s="(debug)";
#else
    char *debug_s="";
#endif
#ifdef __i386__
    char *mode_s="-32";
#else
    char *mode_s="-64";
#endif
    option_t * opt;
    char option_line[MAXSTR]="";
    Output("id name %s %s%s%s\n", PROGRAM, VERSION,mode_s,debug_s);
    Output("id author %s\n", "Chua Kong Sian, Stuart Cracraft <cracraft@ai.mit.edu>, Lukas Geyer <lukas@debian.org>, Simon Waters <simon@wretched.demon.co.uk>, Michel Van den Bergh <michel.vandenbergh@uhasselt.be>");
    OptionStartIter(Option);
    while((opt=OptionNext(Option))){
	if(opt->mode & MODE_UCI){
	    UciOptionLineFormat(option_line,opt);
	    Output("%s\n",option_line);
	}
    }
    Output("%s\n","uciok");
}

void UciCmd(uci_t *uci){
    switch(uci->type){
    case UCI_NOTYPE:
	break;
    case UCI_QUIT:
	SET(Protocol->state_flags,QUIT);
	break;
    case UCI_UCI:
	UciSendOptions();		
	Protocol->console=0;
	Protocol->protocol=PROTOCOL_UCI;
	break;
    case UCI_ISREADY:
	Output("readyok\n");
	break;
    case UCI_SETOPTION:
	gnuchess_set_option(uci->name,uci->value);
	break;
    case UCI_STOP:
	break;
    case UCI_PONDERHIT:
	break;
    case UCI_POSITION:  
	break;          
    case UCI_NEWGAME:
	TTableZero(TTable);
	PTableZero(PTable);
	break;
    case UCI_GO:
	DoComputerMove(uci);
	//SearchParamsCreateUci(searchparams,uci);
	//Iterate(uci->board,searchparams,searchstats);
	//MoveToLAN(searchstats->RootPVSeq->moves[0],LANmv);
	//Output("bestmove %s", LANmv);
	//if(searchstats->RootPVSeq->gencnt>=2){
	//    MoveToLAN(searchstats->RootPVSeq->moves[1],LANmv);
	//    Output(" ponder %s\n", LANmv);
	//}else{
	//    Output("\n", LANmv);
	//}
	break;
    }
}

void UciMainLoop(){
    uci_t uci[1];
    char *cmd;
    UciInit(uci);
    while(!(Protocol->state_flags & QUIT)){
	InputWait();
	cmd=strdup(InputLook());
	InputWakeup(); 
	UciFromString(uci,cmd);
	free(cmd);
	UciCmd(uci);
   }
    InputCleanup();
}




