/* Commands for the Mac interface to Xconq.
   Copyright (C) 1992-2000 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"
#include "print.h"
#include "macconq.h"

/* Define to enable (but not activate) profiling. */
#undef PROFILING

/* Define PROFILING if CodeWarrior profiling is on. 
Also include Profiler.h if not already included. */
#if __profile__
#include <Profiler.h>
#define PROFILING
#endif

#ifdef PROFILING
#ifdef THINK_C			
extern int _trace;	/* Not used by CodeWarrior. */
#include <profile.h>
#endif
#endif

extern WindowPtr playersetupwin;

extern int forcedtoresign;

int position_set_modally;

Point modal_point;

Map *modal_map;

#ifdef DESIGNERS
#define side_may_select(unit) (in_play(unit) && ((unit)->side == dside || dside->designer))
#define valid_selection(unit) (in_play(unit) && ((unit)->side == dside || dside->designer))
#else
#define side_may_select(unit) (in_play(unit) && ((unit)->side == dside))
#define valid_selection(unit) (in_play(unit) && ((unit)->side == dside))
#endif

extern char *cursavename;

int givetakeprefixarg;

extern Side *tmpcmdside;

/* Local function declarations. */

static int get_a_position(Map **map, int *xp, int *yp, int *dirp);
static int get_a_unit(Map **mapp, Unit **unitp);
static int get_a_visible_unit(Map **mapp, Unit **unitp);
static int add_unit_position(Unit *unit);
static void message_dialog(int sidenum);
static int do_one_add_terrain(Unit *unit);
static int do_one_attack(Unit *unit);
static void unit_do_build_2(Unit *unit);
static int do_one_collect(Unit *unit);
static int do_one_fire(Unit *unit);
static int do_one_fire_into(Unit *unit);
static int do_one_move_to(Unit *unit);
static int do_one_occupant(Unit *unit);
static int do_one_remove_terrain(Unit *unit);
static int do_one_repair(Unit *unit);
static int do_one_set_name(Unit *unit);
static int do_one_dir_move(Unit *unit);
static int do_one_dir_multiple_move(Unit *unit);
static void create_command_window(void);
static void get_command_and_do(void);
static int unit_rename_dialog(Unit *unit);

void cmd_error(Side *side, char *fmt, ...);

/* Static (local) variables. */

static int tmpcmdarg;
static int tmprecurse;

static int tmpcmdx, tmpcmdy, tmpcmddir;

static Unit *tmpcmdunit;

/* Prefixed numeric argument to commands. */

static int tmpdir;

/* This flag is set to prevent running an "other" command when already
   executing an "other" command. */

static int doingother = FALSE;

/* Given a character, find a command for it and execute. */

void
do_keyboard_command(int key)
{
    void (*fn)(void);

	DGprintf("Typed '%c' (0x%x)\n", key, key);
	if (between('0', key, '9')) {
		/* Add a decimal digit to the prefix argument. */
		if (dside->prefixarg < 0)
		  dside->prefixarg = 0;
		dside->prefixarg += dside->prefixarg * 10 + (key - '0');
		/* (should add some sort of feedback) */
	} else {
		execute_command(dside, key);
	}
}

int
get_a_position(Map **mapp, int *xp, int *yp, int *dirp)
{
	Point at;

	if (position_set_modally) {
		position_set_modally = FALSE;
		*mapp = modal_map;
		return m_nearest_boundary(*mapp, modal_point.h, modal_point.v, xp, yp, dirp);
	} else if ((*mapp = frontmap) != NULL) {

		GetMouse(&at);
		return m_nearest_boundary(*mapp, at.h, at.v, xp, yp, dirp);
	}
	return FALSE;
}

int
get_a_unit(Map **mapp, Unit **unitp)
{
	Point at;

	if (position_set_modally) {
		position_set_modally = FALSE;
		*mapp = modal_map;
		return m_nearest_unit(*mapp, modal_point.h, modal_point.v, unitp);
	} else if ((*mapp = frontmap) != NULL) {

		GetMouse(&at);
		return m_nearest_unit(*mapp, at.h, at.v, unitp);
	}
	return FALSE;
}

int
get_a_visible_unit(Map **mapp, Unit **unitp)
{
	int rslt = FALSE;
	Point at;

	if (position_set_modally) {
		position_set_modally = FALSE;
		*mapp = modal_map;
		rslt = m_nearest_unit(*mapp, modal_point.h, modal_point.v, unitp);
	} else if ((*mapp = frontmap) != NULL) {

		GetMouse(&at);
		rslt = m_nearest_unit(*mapp, at.h, at.v, unitp);
	}
	if (rslt
		&& !(*mapp)->see_all
		&& !units_visible(dside, (*unitp)->x, (*unitp)->y)
		)
	  rslt = FALSE;
	return rslt;
}

static UnitVector *selvec;

UnitVector *
get_selected_units(Side *side)
{
	int i, rslt, numcould = 0, numnot = 0;
	Map *map;
	List *list;  
	UnitCloseup *closeup;
	Unit *unit;

	if (selvec == NULL)
	  selvec = make_unit_vector(100);
	clear_unit_vector(selvec);

	if (frontmap) {
		for (i = 0; i < frontmap->numselections; ++i) {
			unit = frontmap->selections[i];
			/* Don't apply to selected units that we don't control. */
			if (in_play(unit) && side_controls_unit(dside, unit)) {
			    selvec = add_unit_to_vector(selvec, unit, 0);
			}
		}
	} else if ((list = list_from_window(topWindow)) != NULL) {
		if ((unit = (Unit *) selected_unit_in_list(list)) != NULL) {
		    selvec = add_unit_to_vector(selvec, unit, 0);
		}
	} else if ((closeup = unit_closeup_from_window(topFloat)) != NULL) {
		unit = closeup->unit;
		/* Don't apply to units that we don't control. */
		if (side_controls_unit(dside, unit)) {
		    selvec = add_unit_to_vector(selvec, unit, 0);
		}
	} else if (maplist != NULL) {
		map = maplist;
		for (i = 0; i < map->numselections; ++i) {
			unit = map->selections[i];
			/* Don't apply to selected units that we don't control. */
			if (in_play(unit) && side_controls_unit(dside, unit)) {
			    selvec = add_unit_to_vector(selvec, unit, 0);
			}
		}
	}
	return selvec;
}

/* Start of alphabetized commands. */

void do_flash(Side *side) {}
void do_world_map(Side *side) {}
void do_unit_closeup(Side *side) {}
void do_side_closeup(Side *side) {}
void do_orders_popup(Side *side) {}
void do_new_map(Side *side) {}
void do_map(Side *side) {}

void
do_add_terrain(Side *side)
{
    int x, y, dir;
	Map *map;

	if (get_a_position(&map, &x, &y, &dir)) {
		tmpcmdx = x;  tmpcmdy = y;  tmpcmddir = dir;
		apply_to_all_selected(do_one_add_terrain, TRUE);
		return;
	}
	beep();
}

static int
do_one_add_terrain(Unit *unit)
{
	int u, t, t1, x, y, dir, tfirst = -1, numtypes, possibles[MAXTTYPES];

    u = unit->type;
    numtypes = 0;
	for_all_terrain_types(t) {
		if (ut_acp_to_add_terrain(u, t) > 0) {
			possibles[numtypes++] = t;
		}
	}
	if (numtypes == 0) {
		return FALSE;
	} else if (numtypes == 1) {
		t1 = possibles[0];
	} else {
		/* should have better way to choose type */
		if (between(0, dside->prefixarg, numtypes - 1))
		  t1 = possibles[dside->prefixarg];
		else {
			notify(dside, "Prefix should be 0 to %d", numtypes - 1); 
			return FALSE;
		}
	}
	/* (should put following in ui.c?) */
	switch (t_subtype(t1)) {
		case cellsubtype:
			if (valid(check_alter_cell_action(unit, unit, tmpcmdx, tmpcmdy, t1))) {
				if (net_prep_alter_cell_action(unit, unit, tmpcmdx, tmpcmdy, t1))
				  return TRUE;
			}
			break;
		case bordersubtype:
		case connectionsubtype:
		    if (distance(tmpcmdx, tmpcmdy, unit->x, unit->y) <= ut_alter_range(u, t1)) {
				x = tmpcmdx;  y = tmpcmdy;
				dir = tmpcmddir;
			} else {
				x = unit->x;  y = unit->y;
				dir = approx_dir(tmpcmdx - unit->x, tmpcmdy - unit->y);
			}
			if (valid(check_add_terrain_action(unit, unit, x, y, dir, t1))) {
				if (net_prep_add_terrain_action(unit, unit, x, y, dir, t1))
				  return TRUE;
			}
			break;
		case coatingsubtype:
			break;
	}
	return FALSE;
}

void
do_attack(Side *side)
{
	Map *map;
	
	if (get_a_visible_unit(&map, &tmpcmdunit)) {
		apply_to_all_selected(do_one_attack, TRUE);
		return;
	}
	beep();
}

static int
do_one_attack(Unit *atker)
{
	int i;
	Unit *other;

	other = tmpcmdunit;
	if (other != NULL
		&& other != atker
		&& other->side != atker->side) {
		if (distance(atker->x, atker->y, other->x, other->y) <= 1
			&& valid(check_attack_action(atker, atker, other, 100))) {
			net_prep_attack_action(atker, atker, other, 100);
			return TRUE;
		} else if (valid(check_fire_at_action(atker, atker, other, -1))) {
			net_prep_fire_at_action(atker, atker, other, -1);
			return TRUE;
		}
	}
	return FALSE;
}

void
do_build(Side *side)
{
	int i;
	List *list;  
	UnitCloseup *closeup;
	Unit *unit;

	list = list_from_window(topWindow);
	if (list != NULL) {
		unit = selected_unit_in_list(list);
		if (unit != NULL 
		    && can_build_or_help(unit)
		    && side_controls_unit(dside, unit)) {
			show_unit_build_dialog(unit);
			return;
		}
		return;
	}
	closeup = unit_closeup_from_window(topFloat);
	if (closeup != NULL) {
		unit = closeup->unit;
		if (unit != NULL 
		    && can_build_or_help(unit)
		    && side_controls_unit(dside, unit)) {
			show_unit_build_dialog(unit);
			return;
		}
		return;
	}
	if (frontmap) {
		for (i = 0; i < frontmap->numselections; ++i) {
			unit = frontmap->selections[i];
			if (unit != NULL 
			    && can_build_or_help(unit)
			    && side_controls_unit(dside, unit)) {
				show_unit_build_dialog(unit);
				return;
			}
		}
		return;
	}
	/* No way to figure out the unit to be building with, so complain. */
	beep();
}

void
unit_do_build_2(Unit *unit)
{
    extern int editedrunlength;

	show_construction_window();
	if (dside->prefixarg > 0)
	  set_construction_run_length(dside->prefixarg);
	if (dside->prefixarg > 0)
	  editedrunlength = dside->prefixarg;
	else
	  editedrunlength = -1;
	select_unit_in_construction_window(unit);
	select_type_in_construction_window(favored_type(unit));
}

int mtocollect;

void
do_collect(Side *side)
{
    char *arg, *rest;

	mtocollect = NONMTYPE;
	if (nummtypes == 0) {
		cmd_error(dside, "No materials to collect");
		return;
	}
	if (!empty_string(cmdargstr)) {
		rest = get_next_arg(cmdargstr, tmpbuf, &arg);
		mtocollect = mtype_from_name(arg);
	} else {
		cmd_error(dside, "No material name given");
		return;
	}
	if (!is_material_type(mtocollect)) {
		cmd_error(dside, "`%s' is not a recognized material name", arg);
		return;
	}
	apply_to_all_selected(do_one_collect, TRUE);	
}

int
do_one_collect(Unit *unit)
{
	if (unit->plan) {
		net_set_collect_task(unit, mtocollect, unit->x, unit->y);
		return TRUE;
	}
	return FALSE;
}

void
do_copying(Side *side)
{
	show_help_window(copying_help_node);
}

/* Command all selected mobile units to move in a given direction. */

/* The function that gets called on each selected unit. */

int
do_one_dir_move(Unit *unit)
{
    int nx, ny;

	if (mobile(unit->type)) {
		if (point_in_dir(unit->x, unit->y, tmpdir, &nx, &ny)) {
			return advance_into_cell(dside, unit, nx, ny, unit_at(nx, ny), NULL);
		}
		return FALSE;
	}
	return FALSE;
}

/* The command function proper. */

void
do_dir(Side *side)
{
	int ndirs, dir1, dir2, modif;

	if (frontmap) {
		ndirs = char_to_dir(tmpkey, &dir1, &dir2, &modif);
		if (ndirs >= 1) {
			tmpdir = dir1;
		} else {
			beep();
			return;
		}
		apply_to_all_selected(do_one_dir_move, TRUE);
		if (frontmap->moveonclick)
		  frontmap->scrolltocurunit = TRUE;
	}
}

/* Command all selected mobile units to move in a given direction. */

/* The function that gets called on each selected unit. */

int
do_one_dir_multiple_move(Unit *unit)
{
	if (mobile(unit->type)) {
		net_set_move_dir_task(unit, tmpdir, (dside->prefixarg <= 0 ? 9999 : dside->prefixarg));
		return TRUE;
	}
	return FALSE;
}

/* The command function proper. */

void
do_dir_multiple(Side *side)
{
	int ndirs, dir1, dir2, modif;

	if (frontmap) {
		ndirs = char_to_dir(tmpkey, &dir1, &dir2, &modif);
		if (ndirs >= 1) {
			tmpdir = dir1;
		} else {
			beep();
			return;
		}
		apply_to_all_selected(do_one_dir_multiple_move, TRUE);
		if (frontmap->moveonclick)
		  frontmap->scrolltocurunit = TRUE;
	}
}

void
do_distance(Side *side)
{
    notify(dside, "Click, then drag to location to which you want the distance.");
	query_position_modally(DISTANCE_MODAL);
}

#if 0 /* (should fix this) */
void
do_end_turn(Side *side)
{
	/* <return> is also interpreted by dialogs, so special-case this, depending
	   on which window was in front. */
	if (topWindow == constructionwin) {
		Point pt;
		extern ControlHandle constructbutton;
		
		pt.h = (*constructbutton)->contrlRect.left + 8;
		pt.v = (*constructbutton)->contrlRect.top + 8;
		do_mouse_down_construction(pt, 0);
	} else {
		net_finish_turn(dside);
	}
}
#endif

void
do_escape(Side *side)
{
	map_modal = NO_MODAL;
}

void
do_fire(Side *side)
{
	Map *map;
	
	if (!get_a_visible_unit(&map, &tmpcmdunit)) {
		beep();
		return;
	}
	apply_to_all_selected(do_one_fire, TRUE);
}

static int
do_one_fire(Unit *atker)
{
	Unit *other;

	other = tmpcmdunit;
	if (other != NULL
		&& other != atker
		&& other->side != atker->side
		&& valid(check_fire_at_action(atker, atker, other, -1))) {
		net_prep_fire_at_action(atker, atker, other, -1);
		return TRUE;
	}
	return FALSE;
}

void
do_fire_into(Side *side)
{
	Map *map;
	
	if (!get_a_position(&map, &tmpcmdx, &tmpcmdy, &tmpcmddir)) {
		beep();
		return;
	}
	apply_to_all_selected(do_one_fire_into, TRUE);
}

static int
do_one_fire_into(Unit *atker)
{
	int x, y;

	x = tmpcmdx;  y = tmpcmdy;	
	if (valid(check_fire_into_action(atker, atker, x, y, 0, -1))) {
		net_prep_fire_into_action(atker, atker, x, y, 0, -1);
		return TRUE;
	}
	return FALSE;
}

void
do_follow_action(Side *side)
{
	if (frontmap) {
		frontmap->follow_action = !frontmap->follow_action;
		if (frontmap->follow_action)
		  notify(dside, "Following the action on map %d", frontmap->id);
		else
		  notify(dside, "No longer following action on map %d", frontmap->id);
	} else {
		beep();
	}
}

void
do_give_unit(Side *side)
{
	int sn = 0;

    if (side->prefixarg >= 0) {
		if (side->prefixarg <= numsides) {
			sn = side->prefixarg;
		} else {
			beep();
			return;
		}
    }
	tmpcmdside = side_n(sn);
	apply_to_all_selected(do_one_give_unit, TRUE);
}

int
do_one_give_unit(Unit *unit)
{
	if (unit->side == tmpcmdside)
	  return FALSE;
#ifdef DESIGNERS
	if (is_designer(dside)) {
		net_designer_change_side(unit, tmpcmdside);
		return TRUE;
	}
#endif /* DESIGNERS */
    if (0 /* unit is a type that cannot act */) {
		/* (should add case for nonacting units) */
   	} else {
		net_prep_change_side_action(unit, unit, tmpcmdside);
	}
	return TRUE;
}

void
do_help(Side *side)
{
    /* Bring up the help window at wherever it was, if already
       created, otherwise will start at topics node. */
	show_help_window(NULL);
}

void
do_message(Side *side)
{
	message_dialog(side->prefixarg);
}

/* Dialog for the input of a textual message. */

/* (should add way to specify which sides to receive this) */

void
message_dialog(int arg)
{
	short done = FALSE, ditem;
	char *msg = NULL, *sidedesc, *nsidedesc, buf[100], buf2[100];
	SideMask sidemask;
	Side *side2;
	Str255 tmpstr;
	DialogPtr win;
	short itemtype;  Handle itemhandle;  Rect itemrect;

	if (arg < 0) {
		sidemask = ALLSIDES;
	} else {
		sidemask = NOSIDES;
		side2 = side_n(arg);
		if (side2)
		  sidemask = add_side_to_set(side2, sidemask);
		else
		  beep();
	}
	sidedesc = sidemask_desc(buf2, sidemask);
	strcpy(buf, sidedesc);
	c2p(buf, tmpstr);
	win = GetNewDialog(dMessage, NULL, (DialogPtr) -1L);
	GetDItem(win, diMessageTo, &itemtype, &itemhandle, &itemrect);
	SetIText(itemhandle, tmpstr);
	ShowWindow(win);
	while (!done) {
		draw_default_button(win, diMessageOK);
		SetCursor(&QD(arrow));
		ModalDialog(NULL, &ditem);
		switch (ditem) {
			case diMessageOK:
				GetDItem(win, diMessageText, &itemtype, &itemhandle, &itemrect);
				msg = get_string_from_item(itemhandle);
				GetDItem(win, diMessageTo, &itemtype, &itemhandle, &itemrect);
				nsidedesc = get_string_from_item(itemhandle);
				/* Fall into next case. */
			case diMessageCancel:
				done = TRUE;
				break;
		}
	}
	/* Close down the dialog (*before* executing any command). */
	DisposeDialog(win);
	update_all_map_windows();
	/* Now send the message (if it wasn't cancelled) */
	if (msg != NULL && nsidedesc != NULL) {
		/* (should parse nsidedesc and use) */
		net_send_message(dside, sidemask, msg);
	}
}

void
do_move_to(Side *side)
{
    notify(dside, "Click on location to which you want to move.");
	query_position_modally(MOVE_TO_MODAL);
}

int
do_move_to_command()
{
	int x, y, dir;
	Map *map;

	if (get_a_position(&map, &x, &y, &dir)) {
		tmpcmdx = x;  tmpcmdy = y;
		apply_to_all_selected(do_one_move_to, TRUE);
		return TRUE;
	}
	return FALSE;
}

static int
do_one_move_to(Unit *unit)
{
#ifdef DESIGNERS
	if (dside->designer) {
		net_designer_teleport(unit, tmpcmdx, tmpcmdy, NULL);
		return TRUE;
	}
#endif /* DESIGNERS */
	net_set_move_to_task(unit, tmpcmdx, tmpcmdy, 0);
	return TRUE;
}

void
do_name(Side *side)
{
	apply_to_all_selected(do_one_set_name, TRUE);
}

/* Unit naming/renaming. */

int
do_one_set_name(Unit *unit)
{
	return unit_rename_dialog(unit);
}

int
unit_rename_dialog(Unit *unit)
{
	short done = FALSE, ditem;
	char *newname;
	char *namer = unit_namer(unit);
	Str255 tmpstr;
	DialogPtr win;
	short itemtype;  Handle itemhandle;  Rect itemrect;

	if (unit == NULL)
	  return FALSE;
	win = GetNewDialog(dRename, NULL, (DialogPtr) -1L);
	/* Seed the text item with the original name. */
	newname = unit->name;
	if (newname == NULL)
	  newname = "";
	GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
	c2p(newname, tmpstr);
	SetIText(itemhandle, tmpstr);
	/* Gray out the random renaming button if no namers available. */
	GetDItem(win, diRenameRandom, &itemtype, &itemhandle, &itemrect);
	HiliteControl((ControlHandle) itemhandle, (!empty_string(namer) ? 0 : 255));
	ShowWindow(win);
	while (!done) {
		draw_default_button(win, diRenameOK);
		SetCursor(&QD(arrow));
		ModalDialog(NULL, &ditem);
		switch (ditem) {
			case diRenameOK:
				GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
				net_set_unit_name(dside, unit, get_string_from_item(itemhandle));
				/* Fall into the next case. */
			case diRenameCancel:
				done = TRUE;
				break;
			case diRenameRandom:
				newname = propose_unit_name(unit);
				if (!empty_string(newname)) {
					GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
				 	c2p(newname, tmpstr);
					SetIText(itemhandle, tmpstr);
				}
				break;
		}
	}
	DisposeDialog(win);
	return TRUE;
}

int
do_one_occupant(Unit *unit)
{
	Unit *nextocc;

	nextocc = find_next_occupant(unit);
	if (nextocc != unit) {
		if (frontmap) {
			unselect_unit_on_map(frontmap, unit);
			select_unit_on_map(frontmap, nextocc);
			/* Overkill, but draw_row alone not as desirable? */
			update_cell_display(dside, unit->x, unit->y, UPDATE_ALWAYS);
		}
	}
	return TRUE;
}

void
do_occupant(Side *side)
{
	apply_to_all_selected(do_one_occupant, TRUE);
}

int other_command_prefixarg;

void
do_other(Side *side)
{
	/* Don't allow recursion with this command. */
	if (!doingother) {
		doingother = TRUE;
		other_command_prefixarg = side->prefixarg;
		show_command_window();
		other_command_prefixarg = -1;
		doingother = FALSE;
	} else {
		beep();
	}
}

/* Dialog for the input of a textual command. */

WindowPtr commandwin;

TEHandle command_text = nil;

Rect commandtextrect;

void
show_command_window()
{
	if (commandwin == nil) {
		create_command_window();
	}
	SelectTheWindow(commandwin);
	ActivateWindow(commandwin, true);
}

void
create_command_window()
{
	Rect tmprect;

	if (hasColorQD) {
		commandwin = GetNewCWindow(wCommand, NULL, NULL);
	} else {
		commandwin = GetNewWindow(wCommand, NULL, NULL);
	}
	SetPort(commandwin);
	tmprect = commandwin->portRect;
	commandtextrect = tmprect;
	commandtextrect.bottom -= 14;
	InsetRect(&commandtextrect, 5, 5);
	/* Create and clear the TextEdit record. */
	command_text = TENew(&commandtextrect, &commandtextrect);
	TESetSelect(0, 32767, command_text);
	TEDelete(command_text);
	/* Seed the command buffer with a prefix arg if was supplied. */
	if (other_command_prefixarg >= 0) {
		sprintf(spbuf, "%d ", other_command_prefixarg);
		TEInsert(spbuf, strlen(spbuf), command_text);
	}
	/* Finally make it floating. */
	MakeFloat(commandwin);
}

void
draw_command()
{
	Rect tmprect;

	MoveTo(5, 34);
	TextFont(small_font_id);
	TextSize(small_font_size);
	DrawString("\pReturn executes the command. Escape just closes the window.");
	TEUpdate(&(commandwin->portRect), command_text);
	TESetSelect(0, 32767, command_text);
	tmprect = commandtextrect;
	InsetRect(&tmprect, -1, -1);
	FrameRect(&tmprect);
}

void
activate_command(int activate)
{
	if (activate)
	  TEActivate(command_text);
	else
	  TEDeactivate(command_text);
}

int
do_key_down_command(int keyCode, int charCode)
{
	/* Return and Enter executes the command and closes
	the window */
	if (charCode == 13 || charCode == 3) {
		get_command_and_do();
		close_window(commandwin);
	/* Escape key just closes the window. */
	} else if (charCode == 27) {
		close_window(commandwin);
	/* Return FALSE if this is not a normal keypad key. */
	}else if (keyCode > 0x3E) {
	    return FALSE;
	/* Else feed char to command text field. */
	} else {
		TEKey(charCode, command_text);
	}
	return TRUE;
}

void
do_mouse_down_command(Point mouse, int mods)
{
	if (PtInRect(mouse, &commandtextrect))
		TEClick(mouse, mods, command_text);
}

static void
get_command_and_do()
{
	int len;
	char buffer[BUFSIZE];
	CharsHandle text;

	text = TEGetText(command_text);
	len = min((*command_text)->teLength, BUFSIZE);
	strncpy(buffer, *text, len);
	buffer[len] = '\0';
	if (!empty_string(buffer)) {
		execute_long_command(dside, buffer);
	} else {
		notify(dside, "No command.");
		beep();
	}
}

void
do_print_view(Side *side)
{
	dump_ps_view(dside, NULL, "View PS");
}

static void unit_do_produce_2(Unit *unit);

void
do_produce(Side *side)
{
	int i;
	List *list;  
	UnitCloseup *closeup;
	Unit *unit;

	/* Try different alternatives to find a unit. */
	list = list_from_window(topWindow);
	if (list != NULL) {
		unit = selected_unit_in_list(list);
		if (unit != NULL && can_produce(unit)) {
			unit_do_produce_2(unit);
			return;
		}
		return;
	}
	closeup = unit_closeup_from_window(topFloat);
	if (closeup != NULL) {
		unit = closeup->unit;
		if (unit != NULL && can_produce(unit)) {
			unit_do_produce_2(unit);
			return;
		}
		return;
	}
	if (frontmap) {
		for (i = 0; i < frontmap->numselections; ++i) {
			unit = frontmap->selections[i];
			if (unit != NULL && can_produce(unit)) {
				unit_do_produce_2(unit);
				return;
			}
		}
		return;
	}
	/* No way to figure out the unit to be producing with, so complain. */
	beep();
}

static void
unit_do_produce_2(Unit *unit)
{
	int m, n;

    n = 9999;
    if (dside->prefixarg > 0)
	  n = dside->prefixarg;
	for_all_material_types(m) {
		if (um_acp_to_produce(unit->type, m) > 0) {
			net_push_produce_task(unit, m, n);
			return;
		}
	}
}

void
do_quit(Side *side)
{
	quit_the_game();
}

static int allsumx, allsumy, unitcount;

int
add_unit_position(Unit *unit)
{
	allsumx += unit->x;  allsumy += unit->y;
	++unitcount;
	return TRUE;
}

void
do_recenter(Side *side)
{
	int avgx, avgy;

	if (frontmap) {
		allsumx = allsumy = 0;
		unitcount = 0;
		apply_to_all_selected(add_unit_position, FALSE);
		if (unitcount == 0) {
			beep();
			return;
		}
		avgx = allsumx / unitcount;  avgy = allsumy / unitcount;
		set_map_focus(frontmap, avgx, avgy);
	}
}

/* Recalculate and redraw everything. */

void
do_refresh(Side *side)
{
	Map *map;
	List *list;
	UnitCloseup *closeup;

	reset_coverage();
	reset_all_views();
	compute_all_feature_centroids();
	/* Force updates to all open windows. */
	force_update(gamewin);
	force_update(noticewin);
	force_update(historywin);
	force_update(constructionwin);
	force_update(helpwin);
	for_all_maps(map) {
		force_update(map->window);
	}
	for_all_lists(list) {
		force_update(list->window);
	}
	for_all_unit_closeups(closeup) {
		force_update(closeup->window);
	}
}

void
do_remove_terrain(Side *side)
{
    int x, y, dir;
	Map *map;
	
	if (get_a_position(&map, &x, &y, &dir)) {
		tmpcmdx = x;  tmpcmdy = y;  tmpcmddir = dir;
		apply_to_all_selected(do_one_remove_terrain, TRUE);
		return;
	}
	beep();
}

static int
do_one_remove_terrain(Unit *unit)
{
	int u, t, x, y, dir;

	u = unit->type;
	for_all_terrain_types(t) {
		if (ut_acp_to_remove_terrain(u, t) > 0
			&& unit->act
		    && unit->act->acp >= ut_acp_to_remove_terrain(u, t)) {
		    if (distance(tmpcmdx, tmpcmdy, unit->x, unit->y) <= ut_alter_range(u, t)) {
				x = tmpcmdx;  y = tmpcmdy;
				dir = tmpcmddir;
			} else {
				x = unit->x;  y = unit->y;
				dir = approx_dir(tmpcmdx - unit->x, tmpcmdy - unit->y);
			}
			if (valid(check_remove_terrain_action(unit, unit, x, y, dir, t))) {
				if (net_prep_remove_terrain_action(unit, unit, x, y, dir, t))
				  return TRUE;
			}
		}
	}
	return FALSE;
}

void
do_repair(Side *side)
{
	Map *map;
	
	if (get_a_unit(&map, &tmpcmdunit)) {
		apply_to_all_selected(do_one_repair, TRUE);
		return;
	}
	beep();
}

static int
do_one_repair(Unit *unit)
{
	int i;
	Unit *other;

	other = tmpcmdunit;
	/* If no unit designated, assume self-repair desired. */
	if (tmpcmdunit == NULL)
	  other = unit;
	if (other != NULL
		&& other->side == unit->side
		&& valid(check_repair_action(unit, unit, other))) {
		net_prep_repair_action(unit, unit, other);
		return TRUE;
	}
	return FALSE;
}

void
do_resign(Side *side)
{
	if (endofgame) {
		notify(side, "Game is already over.");
		beep();
	} else if (!side->ingame) {
		notify(side, "You are already out of the game.");
		beep();
	} else if (CautionAlert(aConfirmResign, nil) == aiConfirmResignResign) {
		net_resign_game(side, NULL);
	}
}

void
do_save(Side *side)
{
	save_the_game(FALSE, FALSE);
}

void
do_set_formation(Side *side)
{
	int i, numcould = 0, numnot = 0;
	Map *map;
	Unit *follower, *leader;
	
	if (get_a_unit(&map, &leader)) {
		if (leader != NULL) {
			for (i = 0; i < map->numselections; ++i) {
				if ((follower = map->selections[i]) != NULL && valid_selection(follower)) {
					if (leader != follower
						&& leader->side == follower->side
						) {
						net_set_formation(follower, leader, follower->x - leader->x, follower->y - leader->y, 1, 1);
						++numcould;
					} else {
						++numnot;
					}
				}
			}
		} else {
			/* Can't make a formation without a leader. */
			cmd_error(dside, "no leader found");
		}
	}
	/* If nobody could do the command, beep once. */
	if (numcould == 0 && numnot > 0) {
		beep();
	}
}

void
do_set_view_angle(Side *side)
{
	int angle;

	if (frontmap) {
		angle = frontmap->vp->angle;
		if (angle == 90)
		  angle = 30;
		else if (angle == 30)
		  angle = 15;
		else
		  angle = 90;
		/* A suitable vertscale is now computed by set_map_angle. 
		Only override it if provided with a valid prefixarg. */
		set_map_angle(frontmap, angle);
		frontmap->vp->vertscale 
		   = (side->prefixarg < 1 ? frontmap->vp->vertscale : side->prefixarg);
	} else {
		beep();
	}
}

void
do_standing_orders(Side *side)
{
	int rslt;

	if (cmdargstr) {
		rslt = parse_standing_order(dside, cmdargstr);
		if (rslt < 0)
		  beep();
		return;
	}
	beep();
}

void 
do_survey(Side *side)
{
	if (frontmap) {
		toggle_survey(frontmap);
	} else {
		beep();
	}
}

void
do_up(Side *side)
{
	int i, numcould = 0, numnot = 0, rawamt = 1;
	int amt, newalt;
	Point target;
	Unit *unit;

	if (frontmap) {
		for (i = 0; i < frontmap->numselections; ++i) {
			if ((unit = frontmap->selections[i]) != NULL 
			       && valid_selection(unit)) {
				amt = rawamt;
				newalt = (unit_alt(unit) + amt) << 1;
				if (valid(check_move_action(unit, unit, unit->x, unit->y, newalt))) {
					net_prep_move_action(unit, unit, unit->x, unit->y, newalt);
					++numcould;
				} else {
					++numnot;
				}
			}
		}
	}
	/* If nobody could do the action, beep once. */
	if (numcould == 0 && numnot > 0) {
		beep();
	}
}

#if 0		/* Not supported in the kernel yet. */

void
do_down(Side *side)
{
	int i, numcould = 0, numnot = 0, rawamt = 0;
	int amt, newalt;
	Point target;
	Unit *unit;

	rawamt = 0;	
	if (frontmap) {
		for (i = 0; i < frontmap->numselections; ++i) {
			if ((unit = frontmap->selections[i]) != NULL 
			       && valid_selection(unit)) {
				amt = rawamt;
				if (amt == 0 && unit_alt(unit) > 0)
				  amt = unit_alt(unit) / 2;
				newalt = (unit_alt(unit) - amt) << 1;
				if (valid(check_move_action(unit, unit, unit->x, unit->y, newalt))) {
					net_prep_move_action(unit, unit, unit->x, unit->y, newalt);
					++numcould;
				} else {
					++numnot;
				}
			}
		}
	}
	/* If nobody could do the action, beep once. */
	if (numcould == 0 && numnot > 0) {
		beep();
	}
}

#endif

void
do_version(Side *side)
{
	do_about_box();
}

void
do_warranty(Side *side)
{
	show_help_window(warranty_help_node);
}

void
do_zoom_in(Side *side)
{
	if (frontmap) {
		magnify_map(frontmap, 1);
	} else {
		beep();
	}
}

void
do_zoom_out(Side *side)
{
	if (frontmap) {
		magnify_map(frontmap, -1);
	} else {
		beep();
	}
}

/* End of alphabetized commands. */

#ifdef DESIGNERS

/* Toggle the designer mode. */

void
do_design(Side *side)
{
	if (!side->designer) {
		enable_designing(FALSE);
	} else {
		disable_designing();
	}
}

#endif

#ifdef DEBUGGING

void
do_profile(Side *side)
{
	toggle_profiling();
}

void
do_trace(Side *side)
{
	toggle_profiling();

#ifdef PROFILING

#ifdef THINK_C			/* Not used by CodeWarrior. */

	_trace = 1;

#endif /* THINK_C */

#endif /* PROFILING */

}

void
toggle_profiling()
{

#ifdef PROFILING		

#ifdef THINK_C			/* Not used by CodeWarrior. */

	extern int _profile;

#endif /* THINK_C */

	/* Only toggle Profile if profiling is enabled! */
	Profile = !Profile;

	/* Necessary to update game window with the
	new profiling status. */
	draw_game();

#ifdef THINK_C

	if (Profile && !_profile) {
		InitProfile(1000, 100);
		freopen("Xconq.ProfileOut", "w", stdout);
	}
	if (!Profile && _profile) {
		DumpProfile();
		fflush(stdout);
	}

#endif /* THINK_C */

#if __profile__			/* CodeWarrior support. */

	if (Profile && ProfilerGetStatus() == false) {
		ProfilerSetStatus(true);
	}
	if (!Profile && ProfilerGetStatus() == true) {
		ProfilerSetStatus(false);
		ProfilerDump("\pXconq.Profile");
		ProfilerClear();
	}

#endif /* __profile__ */

#endif /* PROFILING */

}

#endif /* DEBUGGING */

/* Generic command error feedback. */

void
cmd_error(Side *side, char *fmt, ...)
{
	va_list ap;

	if (!empty_string(fmt)) {
		va_start(ap, fmt);
    	vnotify(side, fmt, ap);
		va_end(ap);
	}
    /* (should) Only beep once, even if a command generates multiple error messages. */
    beep();
}
