/*--------------------------------------------------------------------------
    Pawny 0.3, chess engine (source code).
    Copyright (C) 2009 - 2011 by Mincho Georgiev.
    
    This program 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 3 of the License, or
    (at your option) any later version.

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
    
    contact: pawnychess@gmail.com 
    web: http://www.pawny.netii.net/
----------------------------------------------------------------------------*/

#include "data.h"
#include <math.h>

void init()
{
	suppress_buffering();
	//initialize:
	board_init();
	tt_init(DEFAULT_TT_SIZE);
	pnt_init(DEFAULT_PNT_SIZE);
	opt->analyze = false;
	opt->time_high = 50000;//50 sec. default max time/move
	opt->time_low  = 30000;
	opt->aspiration = DEFAULT_ASPIRATION_WINDOW;
	opt->rtp = false;
	init_distance();
	init_direction();
	init_attack_vector();
	memset(si,0,sizeof(searchinfo_t));
	book_open();
}

int main(int argc, char *argv[])
{///program entry point
	int i;
	char sInput[512];
	char *p;
	move_t move;

	init();
	//parsing the cmd line:
	for(i = 1; i < argc; i++) parse_input(argv[i]);
	while(true)
	{	p = fgets(sInput,sizeof(sInput),stdin);
		//null terminating:
		while(*p != '\n') p++;
		*p = '\0';
		//parse:
		if(!parse_input(sInput))
		{	if(parse_move(sInput, &move))
			{	if(!make_if_legal(move))
				{	printf("Illegal move\n\n");
					continue;
				}
				opt->comp_side = board->side;
				think();	
			}	
			else
			{	printf("Unknown option: %s",sInput);
				printf("\n\n");
				continue;
			}
		}
	}
}
	

bool parse_input(char *sInput)
{
	char strbuff[256];
	bool result = false;
	static int epdtime = 0;
	int x;
	
		if(*sInput == '\0') return true;
		else if(!strcmp(sInput, "uci")) uci();
		else if(!strncmp(sInput, "hash", 4))
		{	tt_init(atoi(sInput + 5));
			return true;
		}
		else if(!strncmp(sInput, "phash", 5))
		{	pnt_init(atoi(sInput + 6));
			return true;
		}
		else if(!strcmp(sInput,"help"))
		{
			printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
			"\nPawny chess engine\nby Mincho Georgiev (c)2009-2011\n\nConsole mode commands:\n",
			"----------------------------\n",
			"- uci - entering uci mode.\n",
			"- hash <size> - determines the hash size in MB.(min. 8MB, max. 1024MB)\n",
			"- phash <size> - determines the size of the pawn hash table (min. 8MB, max. 128MB)\n",
			"- quit - exit(0);\n",
			"- go - force engine to start searching.\n",
			"- undo - undo the last move.\n",
			"- new - new game.\n",
			"- perft <depth> - performance test.\n",
			"- divide <depth> - divided perft.\n",
			"- setboard <fen> - sets the position from FEN string\n",
			"- getfen - display the FEN string, coresponding to the current pos.\n",
			"- sd <depth> - maximum search depth (time given is suppressed)\n",
			"- st <time> - maximum time per move (seconds)\n",
			"- dtc <on/off> - turns the dynamic time control on or off\n",
			"- epd <filename> - starts a test, based on epd testsuite\n",
			"- epdtime <seconds> - sets the seconds per position for the 'epd' command\n",
			"- version - displays current version.\n",
			"note: the moves are parsed in algebratic notation ('e2e4' 'e7e8q || e7e8Q', 'e1g1' instead of 'O-O')\n",
			"note: the default initial time per move is 50 sec.\n",
			"note: perft and divide are using another hash table with constant size of 25MB.\n\n");
			result = true;
		}
		else if(!strcmp(sInput,"version"))
		{	printf("%s %s\n\n",ENGINE_NAME,VERSION);
			result = true;
		}
		else if(!strcmp(sInput,"quit"))
		{	book_close();
			tt_free();
			exit(0);
		}
		else if(!strcmp(sInput,"go"))
		{	opt->comp_side = board->side;
			think();
			result = true;
		}
		else if(!strcmp(sInput,"undo"))
		{	move_undo();
			result = true;
		}
		else if(!strcmp(sInput,"new"))
		{	board_from_fen(INITIAL_POSITION);
			result = true;
		}
		
		else if(!strncmp(sInput, "epdtime",7))
		{	epdtime = atoi(sInput + 8);
			return true;
		}
		
		else if(!strncmp(sInput, "epdtest",7))
		{	sInput += sizeof("epdtest");
			while(isspace(*sInput)) sInput++;
			if(!test_epd(sInput))
				printf ("EPD error!\n");
			result = true;
		}
		else if(!strncmp(sInput, "epd",3))
		{	FILE *f;
			sInput += sizeof("epd");
			while(isspace(*sInput)) sInput++;
			f = fopen(sInput, "r");
			if(f == NULL) printf("error: wrong or missing filename\n\n");
			else
			{	epd_file_test(f, epdtime);
				fclose(f);
			}
			result = true;
		}
		
		else if(strstr(sInput,"xperft"))
		{	sInput += sizeof("xperft");
			while(isspace(*sInput)) sInput++;
			if(isdigit(*sInput)) 
			{	x = atoi(sInput);
				test_xperft(x);
			}
			result = true;
		}
		else if(strstr(sInput,"perft"))
		{	sInput += sizeof("perft");
			while(isspace(*sInput)) sInput++;
			if(isdigit(*sInput)) 
			{	x = atoi(sInput);
				test_perft(x);
			}
			result = true;
		}
		else if(strstr(sInput,"divide"))
		{	sInput += sizeof("divide");
			while(isspace(*sInput)) sInput++;
			if(isdigit(*sInput)) 
			{	x = atoi(sInput);
				test_divide(x);
			}
			result = true;
		}
		
		else if(strstr(sInput,"pawndiv"))
		{	sInput += sizeof("pawndiv");
			while(isspace(*sInput)) sInput++;
			if(isdigit(*sInput)) 
			{	x = atoi(sInput);
				test_pawn_divide(x);
			}
			result = true;
		}
		
		else if(!strcmp(sInput,"getfen"))
		{	board_to_fen(&strbuff[0]);
			printf("\n");
			printf("%s",strbuff);
			printf("\n\n");
			result = true;
		}
		else if(strstr(sInput,"setboard"))
		{	sInput += sizeof("setboard");
			while(isspace(*sInput)) sInput++;
			if(!board_from_fen(sInput))
				printf ("Illegal position (FEN) !\n");
			result = true;
		}
		else if(strstr(sInput,"sd"))
		{	sInput += sizeof("sd");
			while(isspace(*sInput)) sInput++;
			if(isdigit(*sInput)) 
			{	x = atoi(sInput);
				opt->max_depth = x;
				opt->analyze = true;
			}
			result = true;
		}
		else if(strstr(sInput,"st"))
		{	sInput += sizeof("st");
			while(isspace(*sInput)) sInput++;
			if(isdigit(*sInput)) 
			{	x = atoi(sInput);
				opt->time_high = x*1000;
				opt->analyze = false;
				opt->time_fixed = true;
			}
			result = true;
		}
		else if(!strcmp(sInput,"eval"))
		{	int r;float score;
			r = eval();
			if(opt->comp_side == BLACK && r != 0)
				score = -(r / ((float)100));
			else
				score = (r / ((float)100));
			printf(" %.2f \n",score);
			
			result = true;
		}
		return result;
}

bool parse_move(char *sInput,move_t *move)
{///parsing the input as move string
///for now just a simple "e2e4" notation.

	int file,rank;
	char c = (char)tolower(*sInput);
		
		memset(move,0,sizeof(move_t));
	
		//determine what the first char is
		switch(c)
		{
			//files
			case 'a': file = FILE_A; break;
			case 'b': file = FILE_B; break;
			case 'c': file = FILE_C; break;
			case 'd': file = FILE_D; break;
			case 'e': file = FILE_E; break;
			case 'f': file = FILE_F; break;
			case 'g': file = FILE_G; break;
			case 'h': file = FILE_H; break;
			default:	return false;break;
		}
		sInput++;
		
		//the second char:
		switch(*sInput)
		{
			case '1': rank = RANK_1; break;
			case '2': rank = RANK_2; break;
			case '3': rank = RANK_3; break;
			case '4': rank = RANK_4; break;
			case '5': rank = RANK_5; break;
			case '6': rank = RANK_6; break;
			case '7': rank = RANK_7; break;
			case '8': rank = RANK_8; break;
						
			default: return false; break;
		}
		sInput++;
		move->from = (sq_t)calc_sq(file, rank);
		
		
		//the third char
		c = (char)tolower(*sInput);
		switch(c)
		{
			//files
			case 'a': file = FILE_A; break;
			case 'b': file = FILE_B; break;
			case 'c': file = FILE_C; break;
			case 'd': file = FILE_D; break;
			case 'e': file = FILE_E; break;
			case 'f': file = FILE_F; break;
			case 'g': file = FILE_G; break;
			case 'h': file = FILE_H; break;
			default:	return false;break;
		}
		sInput++;
		
		//the fourth char
		switch(*sInput)
		{
			case '1': rank = RANK_1; break;
			case '2': rank = RANK_2; break;
			case '3': rank = RANK_3; break;
			case '4': rank = RANK_4; break;
			case '5': rank = RANK_5; break;
			case '6': rank = RANK_6; break;
			case '7': rank = RANK_7; break;
			case '8': rank = RANK_8; break;
						
			default: return false; break;
		}
		sInput++;
		move->to = (sq_t)calc_sq(file, rank);
		
		if(*sInput) //there is a 5th charachter
		{
			c = (char)tolower(*sInput);
			switch(c)
			{
				case 'q': move->promoted = (uint8)Coloured(QUEEN); break;
				case 'n': move->promoted = (uint8)Coloured(KNIGHT); break;
				case 'r': move->promoted = (uint8)Coloured(ROOK); break;
				case 'b': move->promoted = (uint8)Coloured(BISHOP); break;
				case '\n': case ' ': break;
				default : return false; break;
			}
		}
		
		//do something with the data here
		return true;
}

void print_move(uint32 move)
{///simple procedure to print a given move:
	int file,rank;
	char mstr[8];
	char *c = &mstr[0];
	move_data_t m;
	
		m.p = move;
		file = calc_file(m.from);
		rank = calc_rank(m.from);
	
		switch(file)
		{
			case FILE_A: *c = 'a'; break;
			case FILE_B: *c = 'b'; break;
			case FILE_C: *c = 'c'; break;
			case FILE_D: *c = 'd'; break;
			case FILE_E: *c = 'e'; break;
			case FILE_F: *c = 'f'; break;
			case FILE_G: *c = 'g'; break;
			case FILE_H: *c = 'h'; break;
			default: break;
		}
		c++;
		
		switch(rank)
		{
			case RANK_1: *c = '1'; break;
			case RANK_2: *c = '2'; break;
			case RANK_3: *c = '3'; break;
			case RANK_4: *c = '4'; break;
			case RANK_5: *c = '5'; break;
			case RANK_6: *c = '6'; break;
			case RANK_7: *c = '7'; break;
			case RANK_8: *c = '8'; break;
		}
		c++;
		
		file = calc_file(m.to);
		rank = calc_rank(m.to);
		
		switch(file)
		{
			case FILE_A: *c = 'a'; break;
			case FILE_B: *c = 'b'; break;
			case FILE_C: *c = 'c'; break;
			case FILE_D: *c = 'd'; break;
			case FILE_E: *c = 'e'; break;
			case FILE_F: *c = 'f'; break;
			case FILE_G: *c = 'g'; break;
			case FILE_H: *c = 'h'; break;
			default: break;
		}
		c++;
		
		switch(rank)
		{
			case RANK_1: *c = '1'; break;
			case RANK_2: *c = '2'; break;
			case RANK_3: *c = '3'; break;
			case RANK_4: *c = '4'; break;
			case RANK_5: *c = '5'; break;
			case RANK_6: *c = '6'; break;
			case RANK_7: *c = '7'; break;
			case RANK_8: *c = '8'; break;
		}
		c++;
		
		switch(GetType(m.promoted))
		{
			case QUEEN:  *c = 'Q'; c++; break;
			case ROOK:   *c = 'R'; c++; break;
			case BISHOP: *c = 'B'; c++; break;	
			case KNIGHT: *c = 'N'; c++; break;
			default: break;
		}
		*c = '\0';
		printf("%s ",mstr);
}

