/** ZCT/cmd.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 "cmd.h"

/* Prototypes for this file */
COMMAND *cmd_lookup(char *input);

#ifndef ZCT_POSIX
char *strsep(char **stringp, const char *delim);
#endif

/* Command-globals */
CMD_INPUT cmd_input = { NULL, NULL, NULL, 0 };
PROTOCOL last_protocol;
COMMAND tune_commands[] =
{
	{ 0, "exit", "Exit the tuning process.", 1, cmd_null },
	{ 0, NULL, NULL, 0, NULL }
};
/* Locals */
COMMAND *commands[] = { def_commands, xb_commands, uci_commands, an_commands,
	an_commands, dbg_commands, tune_commands };
COMMAND cmd_table[CMD_TABLE_SIZE];

/**
command():
Processes user input.
Created 060205; last modified 083108
**/
int command(char *input)
{
	COMMAND *cmd;

	/* Shell escape. */
	if (input[0] == '!')
	{
		printf("system(%s)\n",input+1);
		system(input + 1);
		input[0] = '\0';
		return CMD_GOOD;
	}
	set_cmd_input(input);
	/* Check for comments, and truncate the rest of the line for them. */
	if (strchr(cmd_input.input, '#') != NULL)
		*strchr(cmd_input.input, '#') = '\0';
	cmd_parse(" \t\n");
	/* No commands. Just return, don't print an error message. */
	if (cmd_input.arg_count == 0)
	{
		input[0] = '\0';
		return CMD_GOOD;
	}
	/* Try to find the command in the table for the current protocol. */
	cmd = cmd_lookup(cmd_input.arg[0]);
	if (cmd != NULL)
	{
		/* Some commands require a search to be stopped before they
			are interpreted, like "new". Some require a ponder search
			to end, like "easy" and "go". */
		if ((cmd->search_flag == 1 && zct->engine_state != IDLE) ||
			(cmd->search_flag == 2 && zct->engine_state == PONDERING))
			return CMD_STOP_SEARCH;

		/* Clear the input. XXX relies on non-const string */
		input[0] = '\0';

		/* Execute the command. */
		cmd->cmd_func();
		return CMD_GOOD;
	}
	return CMD_BAD;
}

/**
set_cmd_input():
This function is just a wrapper for allocating the internal input buffer and
copying input buffers into it.
Created 051108; last modified 083108
**/
void set_cmd_input(char *input)
{
	if (cmd_input.old_input != NULL)
	{
		free(cmd_input.old_input);
		cmd_input.old_input = NULL;
	}
	cmd_input.old_input = cmd_input.input;
	cmd_input.input = malloc(strlen(input) + 1);
	strcpy(cmd_input.input, input);
}

/**
initialize_cmds():
Initializes the internal command tables.
Created 070105; last modified 080308
**/
void initialize_cmds(void)
{
	COMMAND *cmd;
	STR_HASHKEY key;

	for (cmd = cmd_table; cmd < cmd_table + CMD_TABLE_SIZE; cmd++)
		cmd->cmd_name = NULL;
	for (cmd = commands[zct->protocol]; cmd->cmd_name != NULL; cmd++)
	{
		for (key = str_hashkey(cmd->cmd_name, CMD_TABLE_SIZE);
			key < CMD_TABLE_SIZE && cmd_table[key].cmd_name != NULL; key++)
			;
		if (key < CMD_TABLE_SIZE)
		{
			cmd_table[key] = *cmd;
			cmd_table[key].hashkey = key;
		}
		else
			fatal_error("fatal error: could not allocate "
				"command hash table.\n");
	}
}

/**
set_protocol():
Reset our current engine protocol that we are communicating in, and reload the
command tables. Additionally, if we are in certain protocols, we have to change
some crap like move output mode.
Created 060109; last modified 060109
**/
void set_protocol(PROTOCOL protocol)
{
	zct->protocol = protocol;
	if (protocol == UCI)
	{
		zct->notation = COORDINATE;
		zct->zct_side = EMPTY;
	}
	else if (protocol == ANALYSIS || protocol == DEBUG)
		zct->zct_side = EMPTY;
	initialize_cmds();
}

/**
cmd_parse():
Parses an input string into arguments.
Created 081205; last modified 083108
**/
void cmd_parse(char *delim)
{
	char *next;
	char *str;

	/* Free old argument lists. */
	if (cmd_input.args != NULL)
	{
		free(cmd_input.args);
		cmd_input.args = NULL;
	}
	if (cmd_input.arg != NULL)
	{
		free(cmd_input.arg);
		cmd_input.arg = NULL;
	}
	/* Check for blank input. If we're not reading from a file, duplicate
		the last command given. */
	if (!zct->source && strlen(cmd_input.input) == 0 &&
		cmd_input.old_input != NULL)
	{
		cmd_input.input = malloc(strlen(cmd_input.old_input) + 1);
		strcpy(cmd_input.input, cmd_input.old_input);
	}

	/* Allocate argument lists. */
	cmd_input.args = (char *)malloc(strlen(cmd_input.input) + 1);
	/* Allocate enough space for as many arguments as there are characters.
		Overkill, but memory is cheap and it's maybe 1K wasted at most. */
	cmd_input.arg = (char **)malloc(strlen(cmd_input.input) * sizeof(char *));
	strcpy(cmd_input.args, cmd_input.input);
	str = cmd_input.args;
	for (cmd_input.arg_count = 0; (next = strsep(&str, delim)) != NULL;
		cmd_input.arg_count++)
	{
		if (*next != '\0')
		{
			cmd_input.arg[cmd_input.arg_count] = next;
			/* Check if the next argument is enclosed in quotes. */
			if (str != NULL && *str == '"')
			{
				str++; /* Advance past the quote character */
				cmd_input.arg_count++;
				cmd_input.arg[cmd_input.arg_count] = str;
				if ((str = strchr(str, '"')) != NULL)
					*str++ = '\0'; /* Delete the ending quote character */
			}	
		}
		else
			cmd_input.arg_count--;
	}
}

/**
display_help():
Displays a list of commands in the current zct->protocol for the user.
Created 071207; last modified 111408
**/
void display_help(void)
{
	COMMAND *cmd;
	static char help_str[BUFSIZ];

	for (cmd = commands[zct->protocol]; cmd->cmd_func != NULL; cmd++)
	{
		if (cmd->help_text != NULL)
		{
			sprint(help_str, sizeof(help_str),
				"%-10s    %s", cmd->cmd_name, cmd->help_text);
			line_wrap(help_str, sizeof(help_str), 16);
			print("%s\n", help_str);
		}
	}
}

/**
cmd_lookup():
Look up command on internal hash table.
Created 070105; last modified 102506
**/
COMMAND *cmd_lookup(char *input)
{
	STR_HASHKEY key;
	for (key = str_hashkey(input, CMD_TABLE_SIZE); key < CMD_TABLE_SIZE &&
		cmd_table[key].cmd_name != NULL; key++)
		if (!strcmp(input, cmd_table[key].cmd_name))
			return &cmd_table[key];
	return NULL;
}

/**
str_hashkey():
Creates a hashkey from a given string for a lookup table of the given size.
Created 060205; last modified 080308
**/
STR_HASHKEY str_hashkey(char *input, int size)
{
	int h;
	h = strlen(input) * 319;
	h += input[0];
	h *= input[strlen(input) >> 1];
	h -= input[strlen(input) - 1];
	return h % size;
}

#if !defined(ZCT_POSIX)
/**
strsep():
This is just a filler function for non-POSIX systems.
Created 042908; last modified 042908
**/
char *strsep(char **string, const char *delim)
{
	char *str;
	char *token;
	char *d;
	char c;
	char t;

	if (*string == NULL)
		return NULL;
	token = str = *string;
	while (TRUE)
	{
		/* Take the next char in the string and see if it matches
			something in the set delimiter characters. */
		c = *str++;
		d = delim;
		do
		{
			/* look at the next char in delim */
			if ((t = *d++) == c)
			{
				if (c == '\0')
					str = NULL;
				else
					*(str - 1) = '\0';
				*string = str;
				return token;
			}
		} while (t != '\0');
	}
}
#endif
