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

extern void set_construction_run_length(int len);
extern void do_idle_command(int n);
extern int do_down_command(void);
extern int do_up_command(void);

/* Define in order to get profiling controls. */
#undef PROFILING

#ifdef THINK_C
#include <profile.h>
#endif

#ifdef PROFILING
extern int _trace;
#endif

extern WindowPtr playersetupwin;

extern int forcedtoresign;

extern int modal_construction;

extern WindowPtr window_behind_construction;

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 do_one_add_terrain(Unit *unit);
static int do_one_ai_control(Unit *unit);
static int do_one_attack(Unit *unit);
static void unit_do_build_2(Unit *unit);
extern int do_one_clear_plan(Unit *unit);
extern int do_one_collect(Unit *unit);
static int do_one_delay(Unit *unit);
extern int do_one_detach(Unit *unit);
extern int do_one_detonate(Unit *unit);
extern int do_one_dir_move(Unit *unit);
extern int do_one_dir_multiple_move(Unit *unit);
extern int do_one_disband(Unit *unit);
extern int do_one_disembark(Unit *unit);
extern int do_one_embark(Unit *unit);
static int do_one_fire(Unit *unit);
static int do_one_fire_into(Unit *unit);
extern int do_one_give(Unit *unit);
extern int do_one_give_unit(Unit *unit);
extern int do_one_idle(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);
extern int do_one_repair(Unit *unit);
static int do_one_reserve(Unit *unit);
extern int do_one_return(Unit *unit);
static int do_one_set_name(Unit *unit);
static int do_one_asleep(Unit *unit);
extern int do_one_take(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;

/* This is the actual key typed, for use if several keyboard commands
   share a single function. */

extern char tmpkey;

/* 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
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;
}

/* 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_auto(Side *side)
{
	apply_to_all_selected(do_one_ai_control, TRUE);	
}

static int
do_one_ai_control(Unit *unit)
{
    int newval;

	if (unit->plan) {
		if (dside->prefixarg < 0)
		  newval = !unit->plan->aicontrol;
		else if (dside->prefixarg == 0)
		  newval = 0;
		else
		  newval = 1;
		net_set_unit_ai_control(dside, unit, newval, FALSE);
	}
	return TRUE;
}

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)) {
			unit_do_build_2(unit);
			return;
		}
		return;
	}
	closeup = unit_closeup_from_window(topFloat);
	if (closeup != NULL) {
		unit = closeup->unit;
		if (unit != NULL && can_build_or_help(unit)) {
			unit_do_build_2(unit);
			return;
		}
		return;
	}
	if (frontmap) {
		for (i = 0; i < frontmap->numselections; ++i) {
			unit = frontmap->selections[i];
			if (unit != NULL && can_build_or_help(unit)) {
				unit_do_build_2(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;

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

/* Create and/or bring up the construction planning window. */

void
show_construction_window()
{
	window_behind_construction = topWindow;
	if (constructionwin == nil) {
		create_construction_window();
	} else {
		reinit_construction_lists();
	}
	SelectTheWindow(constructionwin);
	ActivateWindow(constructionwin, true);
}

void
do_clear_plan(Side *side)
{
	apply_to_all_selected(do_one_clear_plan, TRUE);	
}

int
do_one_clear_plan(Unit *unit)
{
	if (unit->plan) {
		net_set_unit_plan_type(dside, unit, PLAN_NONE);
		net_clear_task_agenda(dside, unit);
		return TRUE;
	}
	return FALSE;
}

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);
}

void
do_delay(Side *side)
{
	apply_to_all_selected(do_one_delay, TRUE);
}

int
do_one_delay(Unit *unit)
{
	if (unit->plan) {
	    net_delay_unit(unit, TRUE);
		return TRUE;
	}
	return FALSE;
}

void
do_detach(Side *side)
{
	apply_to_all_selected(do_one_detach, TRUE);
}

int
do_one_detach(Unit *unit)
{
	if (!completed(unit)) {
		return FALSE;
	} else if (valid(check_transfer_part_action(unit, unit, unit->hp / 2, NULL))) {
		net_prep_transfer_part_action(unit, unit, unit->hp / 2, NULL);
		return TRUE;
	} else {
		/* try to find a nearby unit to do it */
	}
	return FALSE;
}

/* This command samples the current mouse to get the desired detonate location. */

void
do_detonate(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_detonate, TRUE);
		return;
	}
	beep();
}

int
do_one_detonate(Unit *unit)
{
	/* (should use tmpcmdx etc, but fall back to unit pos if x,y not valid?) */
    if (valid(check_detonate_action(unit, unit, unit->x, unit->y, unit->z))) {
		net_prep_detonate_action(unit, unit, unit->x, unit->y, unit->z);
		return TRUE;
	}
	return FALSE;
}

/* 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));
		}
		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_disband(Side *side)
{
	apply_to_all_selected(do_one_disband, TRUE);
}

int
do_one_disband(Unit *unit)
{
    return net_disband_unit(dside, unit);
}

void
do_disembark(Side *side)
{
	apply_to_all_selected(do_one_disembark, TRUE);
}

int
do_one_disembark(Unit *unit)
{
	Unit *transport = unit->transport;

	if (transport == NULL)
	  return FALSE;
	/* Try moving into the transport's transport, if there is one. */
	if (transport->transport != NULL
		&& can_occupy(unit, transport->transport)) {
		net_prep_enter_action(unit, unit, transport->transport);
		/* (should be able to set up task if can't do action immediately) */
		return TRUE;
	}
	/* Try moving into the open in the cell. */
	if (!inside_area(unit->x, unit->y))
	  return FALSE;
	if (valid(check_move_action(unit, unit, unit->x, unit->y, unit->z))) {
		net_prep_move_action(unit, unit, unit->x, unit->y, unit->z);
		return TRUE;
	} else {
		/* (should be able to set up task if can't do action immediately) */
	}
	return FALSE;
}

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

void
do_embark(Side *side)
{
	apply_to_all_selected(do_one_embark, TRUE);
}

int
do_one_embark(Unit *unit)
{
	int x = unit->x, y = unit->y;
	Unit *transport = unit->transport, *unit2, *unit3;

	/* Note that occupation tests check for unit == transport case,
	   so not necessary in the code below. */
	if (transport == NULL) {
		/* Unit is in the open. */
		for_all_stack(x, y, unit2) {
			if (unit2 != transport && can_occupy(unit, unit2)) {
				net_prep_enter_action(unit, unit, unit2);
				return TRUE;
			}
		}
		for_all_stack(x, y, unit2) {
			for_all_occupants(unit2, unit3) {
				if (unit3 != transport && can_occupy(unit, unit3)) {
					net_prep_enter_action(unit, unit, unit3);
					return TRUE;
				}
			}
		}
	} else {
		for_all_occupants(transport, unit2) {
			if (unit2 != transport && can_occupy(unit, unit2)) {
				net_prep_enter_action(unit, unit, unit2);
				return TRUE;
			}
		}
		if (transport->transport == NULL) {
			for_all_stack(x, y, unit2) {
				if (unit2 != transport && can_occupy(unit, unit2)) {
					net_prep_enter_action(unit, unit, unit2);
					return TRUE;
				}
			}
			for_all_stack(x, y, unit2) {
				for_all_occupants(unit2, unit3) {
					if (unit3 != transport && can_occupy(unit, unit3)) {
						net_prep_enter_action(unit, unit, unit3);
						return TRUE;
					}
				}
			}
		} else {
			for_all_occupants(transport->transport, unit2) {
				if (unit2 != transport && can_occupy(unit, unit2)) {
					net_prep_enter_action(unit, unit, unit2);
					return TRUE;
				}
			}
		}
	}
	return FALSE;
}

#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(Side *side)
{
	givetakeprefixarg = side->prefixarg;
	apply_to_all_selected(do_one_give, TRUE);
}

int
do_one_give(Unit *unit)
{
	give_supplies(unit, NULL, NULL);
	return TRUE;
}

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_idle(Side *side)
{
	do_idle_command(side->prefixarg);
}

void
do_idle_command(int value)
{
	tmpcmdarg = value;
	apply_to_all_selected(do_one_idle, TRUE);	
}

static int
do_one_idle(Unit *unit)
{
	net_set_sentry_task(unit, tmpcmdarg);
	return TRUE;
}

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);
}

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

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, TRUE);
		}
	}
	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;

ControlHandle dobutton;

TEHandle command_text = nil;

Rect commandtextrect;

void create_command_window(void);

static void get_command_and_do(void);

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);
	}
	dobutton = GetNewControl(cCommandDoButton, commandwin);
	SetPort(commandwin);
	tmprect = commandwin->portRect;
	commandtextrect = tmprect;
	commandtextrect.bottom -= 25;
	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);
	}
	SelectTheWindow(commandwin);
}

void
draw_command()
{
	Rect tmprect;

	TEUpdate(&(commandwin->portRect), 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(key)
int key;
{
	if (key == 13 || key == 3) {
		get_command_and_do();
	} else {
		TEKey(key, command_text);
	}
	return TRUE;
}

void
do_mouse_down_command(Point mouse, int mods)
{
	short part;
	ControlHandle control;

	part = FindControl(mouse, commandwin, &control);
	if (control == dobutton) {
		TrackControl(control, mouse, NULL);
		get_command_and_do();
	} else 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;

static 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;
}

static int
do_one_reserve(Unit *unit)
{
	net_set_unit_reserve(dside, unit, tmpcmdarg, tmprecurse);
	return TRUE;
}

void
do_reserve_command(int value, int radius, int recurse)
{
	tmpcmdarg = value;
	tmprecurse = recurse;
	apply_to_all_selected(do_one_reserve, TRUE);	
}

void
do_reserve(Side *side)
{
	do_reserve_command(TRUE, side->prefixarg, FALSE);
}

static int
do_one_return(Unit *unit)
{
	if (1 /* has a place to return to */) {
		net_set_resupply_task(unit, NONMTYPE);
		return TRUE;
	}
	return FALSE;
}

void
do_return(Side *side)
{
	apply_to_all_selected(do_one_return, TRUE);	
}

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_sleep(Side *side)
{
	do_sleep_command(TRUE, side->prefixarg, FALSE);
}

void
do_sleep_command(int value, int radius, int recurse)
{
	tmpcmdarg = value;
	tmprecurse = recurse;
	apply_to_all_selected(do_one_asleep, TRUE);	
}

static int
do_one_asleep(Unit *unit)
{
	net_set_unit_asleep(dside, unit, tmpcmdarg, tmprecurse);
	return TRUE;
}

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_take(Side *side)
{
	if (nummtypes == 0) {
		beep();
		return;
	}
	givetakeprefixarg = side->prefixarg;
	apply_to_all_selected(do_one_take, TRUE);
}

int
do_one_take(Unit *unit)
{
	short m, amts[MAXMTYPES], rslts[MAXMTYPES], needed;

	for_all_material_types(m) {
		amts[m] = givetakeprefixarg;
	}
	needed = take_supplies(unit, amts, rslts);
	report_take(dside, unit, needed, rslts);
	return TRUE;
}

void
do_up(Side *side)
{
	do_up_command();
}

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

void
do_wake(Side *side)
{
	do_sleep_command(FALSE, side->prefixarg, FALSE);
	do_reserve_command(FALSE, side->prefixarg, FALSE);
}

void
do_wake_all(Side *side)
{
	do_sleep_command(FALSE, side->prefixarg, TRUE);
	do_reserve_command(FALSE, side->prefixarg, TRUE);
}

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
	_trace = 1;
#endif /* PROFILING */
}

void
toggle_profiling()
{
#ifdef PROFILING
	extern int _profile;
#endif /* PROFILING */

	Profile = !Profile;

#ifdef PROFILING
#ifdef THINK_C
	if (Profile && !_profile) {
		InitProfile(1000, 100);
		freopen("Xconq.ProfileOut", "w", stdout);
	}
	if (!Profile && _profile) {
		DumpProfile();
		fflush(stdout);
	}
#endif /* PROFILING */
#endif /* THINK_C */
}

#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();
}
