/* Generic commands.
   Copyright (C) 1998, 1999 Stanley T. Shebs.

Xconq 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 2, or (at your option)
any later version.  See the file COPYING.  */

#include "conq.h"
extern int warnings_logged;
extern Agreement *find_agreement(int id);
#include "kpublic.h"
#include "imf.h"
#include "ui.h"
int autofinish_start;
int autofinish_count;

/* The command table is an array of all the commands. */

typedef struct cmdtab {
    char fchar;			/* character to match against */
    char *name;			/* Full name of command */
    void (*fn)(Side *side);	/* Pointer to command's function */
    char *help;                 /* short documentation string */
} CmdTab;

#define C(c) ((c)-0x40)

#undef DEF_CMD
#define DEF_CMD(LETTER,NAME,FN,HELP) { LETTER, NAME, FN, HELP },

/* Define the table of commands. */

CmdTab commands[] = {

#include "cmd.def"

  { 0, NULL, /* NULL, */ NULL, NULL }
};

char *cmdargstr;

char tmpkey;

HelpNode *key_commands_help_node;

HelpNode *long_commands_help_node;

/* Search in command table and execute function if found, complaining if
   the command is not recognized. */

void
execute_command(side, ch)
Side *side;
int ch;
{
    CmdTab *cmd;
    void (*fn)(Side *side);
    
    cmdargstr = NULL;
    for (cmd = commands; cmd->name != NULL; ++cmd) {
	if (ch == cmd->fchar) {
	    fn = cmd->fn;
	    if (fn == NULL) {
		run_warning("no command function for %s (0x%x)?",
			    cmd->name, ch);
		return;
	    }
	    tmpkey = ch;
	    (*fn)(side);
	    /* Whatever might have happened, we *did* find the command. */
	    return;
	}
    }
    cmd_error(side, "unrecognized command key '%c'", ch);
}

/* Given a string, find and interpret a long-name command in it. */

void
execute_long_command(side, cmdstr)
Side *side;
char *cmdstr;
{
    int prefix;
    char *cmdname;
    CmdTab *cmd;
    void (*fn)(Side *side);

    /* (should do something with prefix, which is presently ignored) */
    parse_long_name_command(cmdstr, &prefix, &cmdname, &cmdargstr,
			    copy_string(cmdstr));
    if (empty_string(cmdname)) {
	/* Treat this is a deliberate cancellation, not an error. */
	notify(side, "No command.");
	return;
    }
    for (cmd = commands; cmd->name != NULL; ++cmd) {
	if (strcmp(cmdname, cmd->name) == 0) {
	    fn = cmd->fn;
	    if (fn == NULL) {
		run_warning("no command function for %s?", cmd->name);
		return;
	    }
	    tmpkey = cmd->fchar;
	    (*fn)(side);
	    /* Whatever might have happened, we *did* find the command. */
	    return;
	}
    }
    cmd_error(side, "unrecognized command name \"%s\"", cmdname);
}

/* Collect a command name and an argument string from the given
   string, discarding excess whitespace. */

void
parse_long_name_command(cmdstr, prefixp, namep, argp, buf)
char *cmdstr, **namep, **argp, *buf;
int *prefixp;
{
    int j, prefixarg = -1;
    char *cmdname, *cmdarg;

    if (empty_string(cmdstr)) {
	*namep = *argp = NULL;
	return;
    }
    strcpy(buf, cmdstr);
    /* Look for the first nonwhite char, make it start of command name. */
    cmdname = buf;
    while ((*cmdname == ' ' || *cmdname == '\t') && *cmdname != '\0')
      ++cmdname;
    /* If this is a digit, then it's a prefix arg; extract it. */
    if (isdigit(*cmdname)) {
	prefixarg = *cmdname - '0';
	++cmdname;
	while (isdigit(*cmdname)) {
	    prefixarg = prefixarg * 10 + (*cmdname - '0');
	    ++cmdname;
	}
	/* Skip over any space between prefix arg and cmdname. */
	while ((*cmdname == ' ' || *cmdname == '\t') && *cmdname != '\0')
	  ++cmdname;
    }
    /* Scan over command name, which is delimited by whitespace or end of line. */
    for (j = 0; cmdname[j] != ' ' && cmdname[j] != '\t' && cmdname[j] != '\n' && cmdname[j] != '\0'; ++j)
      ;
    /* If there's more than just the command name, extract an argument. */
    if (cmdname[j] != '\0') {
	cmdarg = cmdname + j + 1;
	while ((*cmdarg == ' ' || *cmdname == '\t' || *cmdname == '\n') && *cmdarg != '\0')
	  ++cmdarg;
	if (*cmdarg == '\0')
	  cmdarg = NULL;
    } else {
	cmdarg = NULL;
    }
    /* Terminate the command name. */
    cmdname[j] = '\0';
    /* Remove trailing whitespace from the argument. */
    if (!empty_string(cmdarg)) {
	for (j = strlen(cmdarg) - 1; j >= 0 && (cmdarg[j] == ' ' || cmdarg[j] == '\t' || cmdarg[j] == '\n'); --j)
	  ;
	cmdarg[j + 1] = '\0';
    }
    DGprintf("Command is \"%s\", argument is \"%s\", prefix is %d\n",
	     (cmdname != NULL ? cmdname : "<null>"),
	     (cmdarg != NULL ? cmdarg : "<null>"),
	     prefixarg);
    *prefixp = prefixarg;
    *namep = cmdname;
    *argp = cmdarg;
}

/* Describe all the single-key commands for help. */

void
describe_key_commands(arg, key, buf)
int arg;
char *key;
TextBuffer *buf;
{
    CmdTab *cmd;

    for (cmd = commands; cmd->name != NULL; ++cmd) {
	describe_command(cmd->fchar, cmd->name, cmd->help, TRUE, buf);
    }
}

/* Describe all the long-name commands for help. */

void
describe_long_commands(arg, key, buf)
int arg;
char *key;
TextBuffer *buf;
{
    CmdTab *cmd;

    for (cmd = commands; cmd->name != NULL; ++cmd) {
	describe_command (cmd->fchar, cmd->name, cmd->help, FALSE, buf);
    }
}

/* Generic command functions.  These are the command functions that
   behave identically for all interfaces. */

void
do_add_player(side)
Side *side;
{
    net_request_additional_side(cmdargstr);
}

void
do_agreement_draft(side)
Side *side;
{
    char *title, titlebuf[20];
    Agreement *ag;

    /* (should have a net_create_agreement) */
    ag = create_agreement(0);
    if (!empty_string(cmdargstr)) {
	title = cmdargstr;
    } else {
	sprintf(titlebuf, "#%d", ag->id);
	title = copy_string(titlebuf);
    }
    ag->name = title;
    /* The creating side is by definition one of the drafters. */
    ag->drafters = add_side_to_set(side, ag->drafters);
}

void
do_agreement_drafter(side)
Side *side;
{
}

void
do_agreement_propose(side)
Side *side;
{
}

void
do_agreement_proposer(side)
Side *side;
{
}

void
do_agreement_sign(side)
Side *side;
{
}

void
do_agreement_signer(side)
Side *side;
{
}

void
do_agreement_term(side)
Side *side;
{
    int agid, agterm;
    char *arg, *rest, *term;
    Agreement *ag;

    rest = get_next_arg(cmdargstr, tmpbuf, &arg);
    if (empty_string(arg)) {
	cmd_error(side, "no agreement id");
	return;
    }
    agid = strtol(arg, NULL, 10);
    agterm = 0;
    if (isdigit(*rest)) {
	rest = get_next_arg(rest, tmpbuf, &arg);
	agterm = strtol(arg, NULL, 10);
    }
    term = copy_string(rest);
    ag = find_agreement(agid);
    if (ag == NULL) {
	cmd_error(side, "no agreement #%d", agid);
	return;
    }
    /* (should be kernel function) */
    ag->terms = cons(new_string(term), ag->terms);
}

/* Set which AI is to run the side's play. */

void
do_ai_side(side)
Side *side;
{
    char *arg, *rest, *aitypename = "mplayer";

    if (!empty_string(cmdargstr)) {
	rest = get_next_arg(cmdargstr, tmpbuf, &arg);
	if (strcmp(arg, "+") == 0) {
	    /* Note that this flag is used by AIs only, so don't need
	       to synchronize kernels here. */
	    side->ai_may_resign = TRUE;
	    notify(side, "AI may decide to draw or resign.");
	    rest = get_next_arg(rest, tmpbuf, &arg);
	} else if (strcmp(arg, "-") == 0) {
	    side->ai_may_resign = FALSE;
	    notify(side, "AI may only recommend whether to draw or resign.");
	    rest = get_next_arg(rest, tmpbuf, &arg);
	}
	aitypename = arg;
    }
    if (empty_string(cmdargstr) && side_has_ai(side)) {
	net_set_side_ai(side, NULL);
    } else if (empty_string(aitypename)) {
	/* Was just toggling AI control flag. */
    } else {
	net_set_side_ai(side, aitypename);
    }
}

void
do_distrust(side)
Side *side;
{
    Side *side2;

    if (!empty_string(cmdargstr)) {
	side2 = parse_side_spec(cmdargstr);
	if (side2 != NULL && side2 != side) {
	    net_set_trust(side, side2, 0);
	    /* publicize? */
	    return;
	} 
    }
}

void
do_doctrine(side)
Side *side;
{
    if (!empty_string(cmdargstr)) {
	if (strcmp(cmdargstr, "set") == 0) {
	    notify(side, "Set what?");
	} else if (strncmp(cmdargstr, "set ", 4) == 0) {
	    net_set_doctrine(side, cmdargstr + 4);
	} else if (strcmp(cmdargstr, "help") == 0
		   || strcmp(cmdargstr, "?") == 0) {
	    notify(side, "doctrine set <doct-name> <property> <value>");
	    notify(side, "\"doctrine\" alone to see current settings");
	}
	return;
    }
    notify_doctrine(side, cmdargstr);
}

void
do_down(side)
Side *side;
{
    cmd_error(side, "not yet implemented");
}

void
do_draw_willingness(side)
Side *side;
{
    if (side->prefixarg < 0)
      side->prefixarg = 1;
    net_set_willing_to_draw(side, (side->prefixarg ? 1 : 0));
}

/* Command to end the side's activity for the turn. */

void
do_end_turn(side)
Side *side;
{
    net_finish_turn(side);
}

void
do_force_global_replan(side)
Side *side;
{
    Unit *unit;
    
    for_all_side_units(side, unit) {
	if (in_play(unit) && unit->plan != NULL) {
	    net_force_replan(side, unit, FALSE);
	}
    }
}

void
do_release(side)
Side *side;
{
    Side *side2;

    if (!empty_string(cmdargstr)) {
	side2 = parse_side_spec(cmdargstr);
	if (side2 != NULL && side2 != side) {
	    net_set_controlled_by(side2, side, FALSE);
	    return;
	}
    }
    cmd_error(side, "");
}

void
do_run(side)
Side *side;
{
    int turns;

    if (!empty_string(cmdargstr))
      turns = atoi(cmdargstr);
    else
      turns = side->prefixarg;
    if (turns < 0)
      turns = 0;
    notify(side, "Running free for %d turn%s.",
	   turns, (turns == 1 ? "" : "s"));
    if (turns > 0) {
	net_set_autofinish(side, TRUE);
	autofinish_start = g_turn();
	autofinish_count = turns;
    }
}

void
do_set_rate(side)
Side *side;
{
    int slow, fast;
    char *reststr;

    if (!empty_string(cmdargstr)) {
	slow = strtol(cmdargstr, &reststr, 10);
	fast = strtol(reststr, &reststr, 10);
	set_play_rate(slow, fast);
    }
}

void
do_submit(side)
Side *side;
{
    Side *side2;

    if (!empty_string(cmdargstr)) {
	side2 = parse_side_spec(cmdargstr);
	if (side2 != NULL && side2 != side) {
	    net_set_controlled_by(side, side2, TRUE);
	    return;
	} 
    }
}

void
do_trust(side)
Side *side;
{
    Side *side2;

    if (!empty_string(cmdargstr)) {
	side2 = parse_side_spec(cmdargstr);
	if (side2 != NULL && side2 != side) {
	    net_set_trust(side, side2, 1);
	    return;
	} 
    }
}

void
do_warning_log(side)
Side *side;
{
    warnings_logged = !warnings_logged;
    if (warnings_logged)
      notify_all("Warnings now being logged in \"Xconq.Warnings\".\n");
    else
      notify_all("Warnings will not be logged.\n");
}

#ifdef DESIGNERS

void
do_gdl(side)
Side *side;
{
    /* (should check designer status?) */
    if (!empty_string(cmdargstr))
      interp_form(NULL, read_form_from_string(cmdargstr, NULL, NULL, NULL));
    /* (should broadcast to all?) */
}

#endif /* DESIGNERS */

#ifdef DEBUGGING

/* Debugging-related commands. */

/* General debugging toggles. */

void
do_debug(side)
Side *side;
{
#ifndef Debug
    toggle_debugging(&Debug);
#endif
    update_everything();
}

void
do_debugg(side)
Side *side;
{
#ifndef DebugG
    toggle_debugging(&DebugG);
#endif
    update_everything();
}

void
do_debugm(side)
Side *side;
{
#ifndef DebugM
    toggle_debugging(&DebugM);
#endif
    update_everything();
}

#endif /* DEBUGGING */
