/** ZCT/zct.c--Created 060205 **/

/** Copyright 2008 Zach Wegner **/
/*
 * This file is part of ZCT.
 *
 * ZCT 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.
 *
 * ZCT 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 ZCT.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "zct.h"
#include "functions.h"
#include "globals.h"
#include "eval.h"
#include "smp.h"

int main(int argc, char **argv)
{
	/* Call all the initialization functions. These are in a specific order!! */
	initialize_settings();
	initialize_data();
	initialize_cmds();
	initialize_eval();
	initialize_attacks();
	initialize_board(NULL);
#ifdef SMP
	initialize_smp(MAX_CPUS);
#else
	zct->process_count = 1;
#endif

	/* Parse command line arguments. */
	parse_command_line(argc, argv);

	/* Read config file. */
	parse_config_file();

	/* Done parsing command line arguments and .ini files--read from stdin. */
	zct->input_stream = stdin;
	game_loop();

	return 0;
}

/**
game_loop():
At the basic level, ZCT just sits here in a loop, reading commands from its
input source, and performing whatever action it is supposed to.
Created 052709; last modified 052709
**/
void game_loop(void)
{
	/* The main processing loop. Check the game state, see if we should
		move, ponder, read input, process input, etc. */
	while (TRUE)
	{
		/* Book keeping... */
		generate_root_moves();
		if (zct->protocol != UCI)
			check_result(TRUE);

		/* Update our clocks. */
		if (zct->protocol != ANALYSIS && zct->protocol != ANALYSIS_X &&
			zct->protocol != DEBUG)
			update_clocks();

		/* Pong a ping back to the GUI. */
		if (zct->ping)
		{
			if (zct->protocol == XBOARD)
				print("pong %i\n", zct->ping);
			else
				print("readyok\n");
			zct->ping = 0;
		}

		/* Check if we should move. */
		if ((zct->engine_state == DEBUGGING || zct->protocol == ANALYSIS ||
			zct->protocol == ANALYSIS_X || board.side_tm == zct->zct_side) &&
			!zct->game_over)
		{
			/* Set the engine state and search for a move. */
			if (zct->protocol == ANALYSIS || zct->protocol == ANALYSIS_X)
				zct->engine_state = ANALYZING;
			else if (zct->engine_state != DEBUGGING &&
				zct->engine_state != INFINITE)
				zct->engine_state = NORMAL;
			search_root();
			zct->engine_state = IDLE;

			/* Output/make the move. */
			if (zct->protocol != DEBUG)
			{
				if (board.pv_stack[0][0] != NO_MOVE)
				{
					if (zct->protocol == DEFAULT)
					{
						prompt();
						print("%M\n", board.pv_stack[0][0]);
					}
					else if (zct->protocol == XBOARD)
						print("move %M\n", board.pv_stack[0][0]);
					else if (zct->protocol == UCI)
					{
						print("bestmove %M", board.pv_stack[0][0]);
						if (board.pv_stack[0][1] != NO_MOVE)
							print(" ponder %M", board.pv_stack[0][1]);
						print("\n");
						zct->zct_side = EMPTY;
					}
					if (zct->protocol != ANALYSIS &&
						zct->protocol != ANALYSIS_X && zct->protocol != UCI)
					{
						make_move(board.pv_stack[0][0]);
						continue;
					}
				}
				else
					print("No move.\n");
			}
		}
		/* See if we should ponder. */
		else if (zct->hard && zct->zct_side != EMPTY &&
			board.game_entry > board.game_stack && zct->input_buffer != NULL &&
		   	zct->input_buffer[0] == '\0' && !zct->game_over &&
			zct->protocol != UCI)
			ponder();

		prompt();
		/* Read input. If input is already waiting, we print the line out
			to verify that we have read it. */
		if (zct->input_buffer == NULL || zct->input_buffer[0] == '\0')
		{
			if (!read_line())
				return;
		}
		else if (zct->protocol != XBOARD && zct->protocol != UCI)
			print("%s\n", zct->input_buffer);
		
		/* Try to process the line. Read it as a command, and if it fails,
			assume it is a move. The input_move() function will barf if the
			input isn't really a move. */
		if (command(zct->input_buffer) == CMD_BAD && zct->protocol != UCI)
		{
			input_move(zct->input_buffer, INPUT_USER_MOVE);
			/* Clear the input buffer. */
			zct->input_buffer[0] = '\0';
		}
	}
}

/**
parse_command_line():
If we are given command line arguments, we parse them just like normal commands.
Each "option" starts with -- (or / on windows...) and is treated just like a
line of input from our input source. Just so you know, the real reason I do it
like this (besides it being really cool to be able to specify any command) is
because I'm too lazy to write a separate set of commands for command line args.
Created 060109; last modified 060109
**/
void parse_command_line(int argc, char **argv)
{
	/* If we actually have arguments, run the game loop. */
	if (argc > 1)
	{
		zct->command_line_args = TRUE;
		zct->source = TRUE;

		/* First "argument" is actually executable name, so get rid of it. */
		zct->argc = argc - 1;
		zct->argv = argv + 1;

		/* Interpret and execute the commands. */
		game_loop();

		/* Done with the command line... */
		zct->source = FALSE;
		zct->command_line_args = FALSE;
	}
}

/**
get_next_command_line():
If we're in command line argument mode, we have to grab the next line from our
saved argc and argv.
Created 060109; last modified 060109
**/
BOOL get_next_command_line(void)
{
	char *argv;
	int size;
	int arg_size;
	static char *buffer = NULL;

	/* Out of arguments. Return! */
	if (zct->argc == 0)
		return FALSE;

	/* Reallocate the buffer. Not really fast but whatever. */
	if (buffer != NULL)
		free(buffer);

	/* We're grabbing the next line. Presumably the first two characters of the
		input are going to be '--', because that's what delimits the arguments.
		But we don't really check it like that. The dashes only end a line
		if we detect that the next argument starts with them. So technically
		the first argument doesn't need the '--', but I really don't care. */
	argv = *zct->argv;
	if (strncmp(argv, "--", 2) == 0)
		argv += 2; 

	size = 1;
	buffer = malloc(1);
	buffer[0] = '\0';

	/* This is a do-while loop because otherwise we could get stuck in an
		infinite loop where somebody enters "./zct ----" or something stupid
		like that. This is just the easiest fix I can think of. */
	do
	{
		arg_size = strlen(argv) + 1;
		/* Extend the buffer. We overshoot by a byte because we have to add in
			a space after the argument. */
		buffer = realloc(buffer, size + arg_size);

		/* Copy this argument into the buffer. */
		strcat(buffer, argv);
		strcat(buffer, " ");
		size += arg_size;

		/* Go to next argument. */
		zct->argc--;
		zct->argv++;
		argv = *zct->argv;
	} while (zct->argc > 0 && strncmp(argv, "--", 2) != 0);

	zct->input_buffer = buffer;
	fprintf(zct->log_stream, "CMD LINE: %s\n", zct->input_buffer);

	return TRUE;
}

/**
parse_config_file():
At program startup, we try to read an initialization file that has settings
and possibly other junk in it.
Created 060109; last modified 060109
**/
void parse_config_file(void)
{
	FILE *config_file;
	PROTOCOL old_protocol;

	config_file = fopen(zct->config_file_name, "rt");
	if (config_file != NULL)
	{
		zct->source = TRUE;
		zct->input_stream = config_file;
		old_protocol = zct->protocol;

		/* Reset our protocol in case somebody did something weird like
			put "uci" on the command line. This is a tricky case since that's
			actually valid, but we must parse config file in default mode,
			so we keep whatever was on the command line. If somebody does
		   	something really stupid and puts a different protocol inside the
		   	config file, then we ignore it. */
		set_protocol(DEFAULT);
		game_loop();

		set_protocol(old_protocol);
		zct->source = FALSE;
	}
}

/**
zct_version_string():
Return a string that gives the engine name and version, along with any set
name that the user might have specified (like a personality name).
Created 052709; last modified 052709
**/
char *zct_version_string(void)
{
	static char str[256];
	sprint(str, sizeof(str), "%s%i%s\n", ZCT_VERSION_STR, ZCT_VERSION,
			zct->name_string);

	return str;
}

/**
prompt():
If the user is using console mode, this prompts for input depending on the
engine mode.
Created 081906; last modified 110608
**/
void prompt(void)
{
	char *mode;

	if (zct->source)
		return;

	if (input_available())
	{
		print("\n");
		return;
	}

	/* Determine the prompt mode based on the protocol. */
	mode = NULL;
	if (zct->protocol == DEFAULT)
		mode = "";
	else if (zct->protocol == DEBUG)
		mode = ".debug";
	else if (zct->protocol == ANALYSIS)
		mode = ".analyze";

	/* Print the prompt. */
	if (mode != NULL)
		print("(zct%s)%i.%s ", mode, board.move_number,
			board.side_tm == WHITE ? "" : "..");
}

/**
bench():
Makes a standard measure of performance on a set of positions.
Created 013008; last modified 031809
**/
void bench(void)
{
	BOOL old_post;
	BITBOARD total_nodes;
	int time;
	struct bench_position
	{
		char *fen;
		int depth;
	} position[] =
	{
		{ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -", 15 },
		{ "2Q2bk1/5pp1/p3p2p/q1p1P2P/1pP5/1P2B1P1/P4PK1/8 w - -", 20 },
		{ "r1bqkb1r/pp1n1ppp/2p1pn2/8/2BP4/2N1PN2/PP3PPP/R1BQK2R b KQkq -",
			13 },
		{ "rnbqk2r/pp2ppbp/3p1np1/8/3NP3/2N5/PPP1BPPP/R1BQK2R w KQkq -", 12 },
		{ "4q1kr/p6p/1prQPppB/4n3/4P3/2P5/PP2B2P/R5K1 w - -", 16 },
		{ "k2N4/1qpK1p2/1p6/1P4p1/1P4P1/8/8/8 w - -", 26 },
		{ "5k2/1p4pP/p7/1p1p4/8/2r3K1/6PP/8 w - -", 15 },
		{ "8/2R5/p2P4/1kP2p2/6rb/P3pp2/1P6/2K4R w - - 0 1",  11 },
        { "r2q1rk1/4Qpp1/b6p/1p6/3pB1p1/8/PP4PP/R4RK1 w - - 2 1",  13 },
        { "r1b1Nn2/pp3p1k/2p3p1/q5Pp/8/1P4QP/P3PPB1/5RK1 b - - 1 1",  13 },
        { "q2knr2/2nb2pp/1p1p4/rPpPbpP1/P1B2N1P/2N1PR2/1BQK4/R7 w - - 1 1",
			13 },
        { "r1b2rk1/pp3ppB/4pq2/2n5/3N4/8/PPP2PPP/R2QR1K1 b - - 0 1", 16 },
        { "r2r2k1/1bq1bp1p/p1nppp2/8/4PP2/1NPBB2Q/P1P4P/4RRK1 b - - 0 1", 13 },
        { "r2q1rk1/1ppb1pb1/3p1npp/p2Pp3/2P1P2B/2NB4/PP3PPP/R2Q1RK1 b - - 0 1",
			14 },
        { "3rr1k1/2Rb1pp1/7p/1p1P4/1R2P3/P4N2/6PP/6K1 b - - 3 1",  14 },
		{ NULL, 0 }
	};
	struct bench_position *current_position;

	total_nodes = 0;
	time = get_time();
	old_post = zct->post;
	zct->use_book = FALSE;
	zct->post = FALSE;
	zct->engine_state = BENCHMARKING;
	print("Running benchmark");

	/* Loop through the positions. */
	for (current_position = &position[0]; current_position->fen != NULL;
		current_position++)
	{
		print(".");
		initialize_board(current_position->fen);
		generate_root_moves();
		zct->max_depth = current_position->depth - 1;
		search_root();
		total_nodes += zct->nodes + zct->q_nodes;
		if (zct->input_buffer[0])
			break;
	}
	time = get_time() - time;
	print("\n");
	print_statistics();
	print("Total nodes=%L\n", total_nodes);
	print("Total time=%T\n", time);
	print("Nodes per second=%L\n", total_nodes * 1000 / time);
	zct->engine_state = IDLE;
	zct->use_book = TRUE;
	zct->post = old_post;
}

/**
fatal_error():
When the program crashes, we call fatal_error() to print a message and die.
How sad...
Created 031709; last modified 031709
**/
void fatal_error(char *text, ...)
{
	char buffer[1<<16];
	va_list args;
	
	va_start(args, text);

	sprint_(buffer, sizeof(buffer), text, args);

	va_end(args);
	
	LOCK(smp_data->io_lock);
	fprintf(zct->output_stream, "%s", buffer);
	fprintf(zct->log_stream, "%s", buffer);
	UNLOCK(smp_data->io_lock);

	exit(EXIT_FAILURE);
}
