/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program 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
 * of the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "glk/agt/agility.h"
#include "glk/agt/interp.h"
#include "glk/agt/exec.h"

namespace Glk {
namespace AGT {

/*

   This contains the code for actually executing each of the
   individual metacommand tokens as well as the routines for
   doing range checking to prevent a bad metacommand from crashing
   the interpreter.

*/

static void it_newdesc(integer item, descr_ptr *newdesc) {
	descr_ptr *desc;

	if (tnoun(item)) desc = noun_ptr + (item - first_noun);
	else if (tcreat(item)) desc = creat_ptr + (item - first_creat);
	else if (item >= first_room && item <= maxroom)
		desc = room_ptr + (item - first_room);
	else {
		writeln("INTERNAL ERROR: it_newdesc called with invalid object");
		return;
	}
	desc->start = newdesc->start;
	desc->size = newdesc->size;
}

static void changepict(int old_pict, int new_pict)
/* Replace old_pict by new_pict */
{
	pictable[old_pict - 1] = new_pict - 1;
}


static const int antidir[12] = {1, 0, 3, 2, 7, 6, 5, 4, 9, 8, 11, 10};

static void change_passage(int start, int dir, int newend)
/* dir is 1..12 */
/* START is a room number: i.e. starting at 0. */
/* NEWEND is an object number, so starts at first_room */
{
	int oldend, i;

	dir--;  /* Convert direction to 0..11 scale */
	oldend = room[start].path[dir];
	room[start].path[dir] = newend;
	if (newend == 0) {
		if (room[oldend - first_room].path[antidir[dir]] == start + first_room)
			room[oldend - first_room].path[antidir[dir]] = 0;
		else for (i = 0; i < 12; i++)
				if (room[oldend - first_room].path[i] == start + first_room) {
					room[oldend - first_room].path[i] = 0;
					break;
				}
	} else
		room[newend - first_room].path[antidir[dir]] = start + first_room;
}


static long ask_for_number(int n1, int n2) {
	char s[50];
	int n;

	if (n1 != n2)
		sprintf(s, "Enter a number from %d to %d: ", n1, n2);
	else
		sprintf(s, "Enter a number: ");
	for (;;) {
		writestr(s);
		n = read_number();
		if (n1 == n2 || (n >= n1 && n <= n2)) return n;
		writeln("");
	}
}

/* Check if n1*n2 will fit in 32 bits and print an error message if not */
/* This errs on the side of caution */
static rbool mult_rangecheck(long n1, long n2) {
	int cnt;

	if (n1 == 0 || n2 == 0) return 1;
	if (n1 < 0) n1 = -n1;
	if (n2 < 0) n2 = -n2;

	for (cnt = 0; n1 != 0; n1 >>= 1, cnt++);
	for (; n2 != 0; n2 >>= 1, cnt++);
	cnt--;

	if (cnt <= 31) return 1; /* We're okay */

	if (!PURE_ERROR)
		writeln("GAME ERROR: Multiplication out of range.");
	return 0;
}

static rbool is_numeric(parse_rec *objrec) {
	char *s;

	if (objrec->num != 0 || objrec->info == D_NUM) return 1;
	if (objrec->adj != 0) return 0;
	if (objrec->noun <= 0) return 0;
	(void)strtol(dict[objrec->noun], &s, 10);
	return (*s == 0); /* *s==0 means no error-- it parsed as a number. */
}

static void setcase(char *s, rbool up) {
	for (; *s != 0; s++)
		if (up) *s = toupper(*s);
		else *s = tolower(*s);
}


void move_in_dir(int obj, int dir) {
	int r;

	r = it_room(obj);
	if (!troom(r)) {
		writeln("GAME ERROR: Object not in a room.");
		return;
	}
	r = room[r - first_room].path[dir - 1];
	if (!troom(r)) return; /* Can't go that way; Fail silently */
	if (obj == 1)
		goto_room(r);
	else
		it_move(obj, r);
}


/* ------------------------------------------------------------------- */
/*  Stack routines:   Manipulating the expression stack                */
/* ------------------------------------------------------------------- */

static long *stack = NULL;
static int sp = 0; /* Stack pointer */
static int stacksize = 0; /* Actual space allocated to the stack */

void init_stack(void) {
	rfree(stack);
	sp = 0;
	stacksize = 0;
}

/* This resets the stack to an empty state. */
void clear_stack(void) {
	sp = 0;
}

static void push_stack(long val) {
	sp++;
	if (sp > stacksize) {
		stacksize += 10;
		stack = (long *)rrealloc(stack, stacksize * sizeof(long));
	}
	stack[sp - 1] = val;
}

static long pop_stack(void) {
	long n;
	if (sp == 0) {
		writeln("GAME ERROR: Stack underflow.");
		return 0;
	}
	n = stack[--sp];
	if (sp + 100 < stacksize) {
		stacksize -= 50;
		stack = (long *)rrealloc(stack, stacksize * sizeof(long));
	}
	return n;
}

long pop_expr_stack(void) {
	return pop_stack();
}


/* opnum: 0=+, 1=-, 2=*, 3=/ 4=% */
static void op_stack(int opnum) {
	long n1, n2;
	n1 = pop_stack();
	n2 = pop_stack();
	switch (opnum) {
	case 0:
		n1 = n1 + n2;
		break;
	case 1:
		n1 = n1 - n2;
		break;
	case 2:
		if (mult_rangecheck(n1, n2)) n1 = n1 * n2;
		break;
	case 3:
		if (n2 != 0) n1 = n1 / n2;
		else writeln("GAME ERROR: Division by zero.");
		break;
	case 4:
		if (n2 != 0) n1 = n1 % n2;
		else writeln("GAME ERROR: Division by zero.");
		break;
	default:
		writeln("INTERNAL ERROR: Invalid stack operation.");
	}
	push_stack(n1);
}

/* This is called from the disassembler */
void print_tos(void) {
	if (sp > 0)
		dbgprintf("TOS(%d)", stack[sp - 1]);
	else
		debugout("TOS(xxx)");
}



/* ------------------------------------------------------------------- */
/* METACOMMAND ROUTINES  */
/*  Functions for scanning and decoding of metacommands */
/* ------------------------------------------------------------------- */

/* #define cret(b) return ((b) ? -1 : 0)*/
#define cret(b) return (b)
#define cretn(i,f) cret(tnoun(i) && noun[i-first_noun].f)
#define cretc(i,f) cret(tcreat(i) && creature[i-first_creat].f)
#define icretc(f) cret(do_disambig==1 || \
                       (tcreat(iobj) && creature[iobj-first_creat].f))

static int obj_cond(int op_, int obj, int arg) {
	switch (op_) {
	case 0:
		cret(in_scope(obj));  /* Present--
                      Do we want to use visible here?? */
	case 1:
		cret(is_within(obj, 1000, 1)); /* IsWearing */
	case 2:
		cret(is_within(obj, 1, 1));
	/* if (PURE_WEAR)  return (it_loc(obj)==1); else */
	case 3:
		cret(it_loc(obj) == 0); /* Nowhere */
	case 4:
		cret(it_loc(obj) != 0);
	case 5:
		cret(!player_has(obj) && in_scope(obj));
	case 6:
		cret(it_loc(obj) == arg);
	case 7:
		cret(it_on(obj));
	case 8:
		cret(!it_on(obj));
	case 9:
		cret(it_open(obj));
	case 10:
		cret(!it_open(obj));
	case 11:
		cretn(obj, locked);
	case 12:
		cret(!tnoun(obj) || !noun[obj - first_noun].locked);
	case 13:
		cretn(obj, edible);
	case 14:
		cretn(obj, drinkable);
	case 15:
		cretn(obj, poisonous);
	case 16:
		cretn(obj, movable);
	default:
		writeln("INTERNAL ERROR: Bad obj_cond value.");
		return 2;
	}
}


static int exec_cond(int op_, int arg1, int arg2) {
	int i;

	switch (op_) {
	/* First the conditions */
	case 0:
		cret(loc + first_room == arg1); /* AtLoc(Room) */
	case 1:
		cret(loc + first_room > arg1);
	case 2:
		cret(loc + first_room < arg1);
	case 3:
		return musiccmd(-1, -1); /* SongPlaying */
	case 4:
		return musiccmd(-2, -1); /* SoundIsOn */
	case 5:
		cret(vb <= 13 && vb > 0 &&
		     room[loc].path[vb - 1] >= first_room); /*DirOK*/
	case 6:
		cret(vb == arg1); /* DirectionIs */
	case 7:
		cret(loc + first_room >= arg1 &&
		     loc + first_room <= arg2); /* BetweenRooms  ?? */
	case 8:
		cret(room[arg1 - first_room].seen);
	case 9:  /* Entered Object? i.e. is iobj valid */
		cret(do_disambig == 1 || iobj > 0);
	case 10:
		cret(curr_time > arg1); /* TimeGT */
	case 11:
		cret(curr_time < arg1); /* TimeLT */
	case 12:
		cret(first_visit_flag);
	case 13:
		cret(newlife_flag);
	case 14:
		cret(player_contents != 0); /* CarrySome */
	case 15:
		cret(player_contents == 0); /* CarryNo */
	case 16:
		cret(player_worn != 0); /* WearSome */
	case 18:
		cret(player_worn == 0); /* WearNo */
	case 17:          /* CarryTreas */
		contloop(i, 1)
		if (tnoun(i) && noun[i - first_noun].points >= arg1) return 1;
		contloop(i, 1000)
		if (tnoun(i) && noun[i - first_noun].points >= arg1) return 1;
		return 0;
	case 19:
		cret(totwt == arg1);
	case 20:
		cret(totwt > arg1);
	case 21:
		cret(totwt < arg1);
	case 22:
	case 23:
	case 24:
	case 25:
	case 26:
	case 27:
	case 28:
		return obj_cond(op_ - 22, arg1, arg2);
	case 29:
		cret(it_loc(arg1) == it_loc(arg2));
	case 30:
	case 31:
		return obj_cond(op_ - 23, arg1, arg2);
	case 32:
		cret(it_group(arg1));
	case 33:
	case 34:
	case 35:
	case 36:
	case 37:
	case 38:
	case 39:
	case 40:
		return obj_cond(op_ - 24, arg1, arg2);
	case 41:
	case 42:
	case 43:
	case 44:
	case 45:
	case 46:
	case 47:
	case 48:
	case 49:
	case 50:
	case 51:
	case 52:
	case 53:
	case 54:
	case 55:
	case 56:
	case 57:
		return obj_cond(op_ - 41, dobj, arg1);
	case 58:
		cretn(dobj, points == arg1);
	case 59:
		cretn(dobj, points > arg1);
	case 60:
		cretn(dobj, points < arg1);
	case 61:
		cretn(dobj, weight == arg1);
	case 62:
		cretn(dobj, weight > arg1);
	case 63:
		cretn(dobj, weight < arg1);
	case 64:
		cret(islit());
	case 65:
		cret(room[loc].light != 0);
	case 66:
		cret(flag[arg1]);
	case 67:
		cret(!flag[arg1]);
	case 68:
		cret(room[loc].flag_noun_bits & (1 << (arg1 - 1)));
	case 70:
		cret(!(room[loc].flag_noun_bits & (1 << (arg1 - 1))));
	case 69:
		cret(room[loc].PIX_bits & (1 << (arg1 - 1))); /* Room Pix here? */
	case 71:
		cret(tscore == arg1);
	case 72:
		cret(tscore > arg1);
	case 73:
		cret(tscore < arg1);
	case 74:
		cret(agt_number == arg1);
	case 75:
		cret(agt_number > arg1);
	case 76:
		cret(agt_number < arg1);
	case 77:
		cret(agt_answer);
	case 78:
		cret(!agt_answer);
	case 79:
		cret(turncnt == arg1);
	case 80:
		cret(turncnt > arg1);
	case 81:
		cret(turncnt < arg1);
	case 82:
		cret(cnt_val(agt_counter[arg1]) == arg2);
	case 83:
		cret(cnt_val(agt_counter[arg1]) > arg2);
	case 84:
		cret(cnt_val(agt_counter[arg1]) < arg2);

	case 85:
		cret(agt_var[arg1] == arg2);
	case 86:
		cret(agt_var[arg1] > arg2);
	case 87:
		cret(agt_var[arg1] < arg2);
	case 88:
		cret(agt_var[arg1] < agt_var[arg2]);
	case 89:
		cret(agt_var[arg1] < agt_rand(1, arg2));
	case 90:
		cret((actor != 0) && (it_loc(actor) == loc + first_room));
	case 91:
		cret(actor == arg1);
	case 92:
		cret(dobj == arg1);
	case 93:
		cret(do_disambig == 1 || iobj == arg1);
	case 94:
		cret(it_contents(arg1) != 0);
	case 95:
		cret(agt_rand(1, 100) <= arg1);
	case 96:
		cret(yesno("Yes or no? "));
	case 97:
		cret(!yesno("Yes or no? "));
	case 98:
		cret(vb > 0 && vb <= 13);
	case 99:
		cret(tcreat(dobj));
	case 100:
		cretc(dobj, gender == 2); /* man */
	case 101:
		cretc(dobj, gender == 1); /* woman */
	case 102:
		cretc(dobj, gender == 0); /* thing */
	case 103:
		cretc(iobj, gender == 2);
	case 104:
		cretc(iobj, gender == 1); /* woman */
	case 105:
		cretc(iobj, gender == 0); /* thing */
	case 106:
		cret(do_disambig == 1 || tcreat(iobj));
	case 107:
		return (do_disambig == 1 || obj_cond(0, iobj, 0));
	/* OR and NOT are handled higher up. */
	/* The following are all v1.8x metacommands */
	case 110:
		cret(beforecmd);
	case 111:
		cret(!beforecmd);
	case 112:
		cret(curr_time / 100 == arg1); /* HoursEqual */
	case 113:
		cret(curr_time / 100 > arg1);
	case 114:
		cret(curr_time / 100 < arg1);
	case 115:
		cret(curr_time % 100 == arg1); /* MinutesEqual */
	case 116:
		cret(curr_time % 100 > arg1);
	case 117:
		cret(curr_time % 100 < arg1);
	case 118:
		cret(curr_time < 1200);  /* IsAM */

	case 119:
		cret(do_disambig);     /* OnDisambig */
	case 120:
		cretc(arg1, hostile);   /* IsHostile */
	case 121:         /* HostilePresent */
		creatloop(i)
		if (creature[i].location == loc + first_room &&
		        creature[i].hostile) return 1;
		return 0;
	/* Otherwise, we're in trouble. */
	case 122:
		cret(actor_in_scope); /* NameWasPresent */
	case 123: /* OncePerTurn */
		if (beforecmd)
			cret(start_of_turn);
		else
			cret(end_of_turn);
	case 124:  /* IsClass */
		cret(arg2 == 0 || matchclass(arg1, arg2));
	case 125:
		cret(getattr(arg1, arg2)); /* IsSet */
	case 126:
		cret(is_numeric(dobj_rec));
	case 127:
		cret(is_numeric(iobj_rec));
	case 128:
		cret(arg1 == arg2);
	case 129:
		cret(arg1 > arg2);
	case 130:
		cret(arg1 < arg2);
	case 131:
		cret(arg1 >= arg2);
	case 132:
		cret(arg1 <= arg2);
	case 133:
		cret(strcmp(userstr[arg1 - 1], userstr[arg2 - 1]) == 0);
	case 134:
		cret(strcmp(userstr[arg1 - 1], userstr[arg2 - 1]) < 0);
	case 135:
		cret(strcmp(userstr[arg1 - 1], userstr[arg2 - 1]) > 0);
	case 136:
		cret(strcasecmp(userstr[arg1 - 1], userstr[arg2 - 1]) == 0);
	case 137:
		cret(strcasecmp(userstr[arg1 - 1], userstr[arg2 - 1]) < 0);
	case 138:
		cret(strcasecmp(userstr[arg1 - 1], userstr[arg2 - 1]) > 0);
	case 139:
		cret(match_answer(rstrdup(userstr[arg1 - 1]), arg2 - 1));
	/* Note that match_answer rfrees it's first argument */
	case 140:
		cret(it_seen(arg1));
	case 141:
		cret(op_objflag(2, arg1, arg2));
	case 142:
		cret(!op_objflag(2, arg1, arg2));
	case 143:
		i = it_room(arg1);
		cret(troom(i) && troom(room[i - first_room].path[arg2 - 1]));
	default:
		writeln("INTERNAL ERROR: Condition token not supported.");
		rprintf("Condition #%d", op_);
		writeln("");
		return 0;
	}
}

#undef cret
#undef cretn
#undef cretc


static void obj_act(int op_, int obj) {
	switch (op_) {
	case 0:
	case 1:  /* open and close */
		if (tnoun(obj))
			noun[obj - first_noun].open = (op_ == 0);
		break;
	case 2:
	case 3: /* lock and unlock */
		if (tnoun(obj))
			noun[obj - first_noun].locked = (op_ == 2);
		break;
	default:
		break;
	}
}


static void exec_action(int op_, int arg1, int arg2) {
	int i, j;
	char *tmpstr;

	switch (op_) {
	case 1000:
		goto_room(arg1 - first_room);
		break;
	case 1001:
		goto_room(agt_rand(arg1, arg2) - first_room);
		break;
	case 1002:
		agt_var[arg1] = loc + first_room;
		break;
	case 1003:
		agt_var[arg1] = dobj;
		break;
	case 1004:
		agt_var[arg1] = iobj;
		break;
	case 1005:
		goto_room(agt_var[arg1] - first_room);
		break;
	case 1006:
		it_move(arg1, agt_var[arg2]);
		break;
	case 1007:
		get_obj(agt_var[arg1]);
		break;
	case 1008:
		msgout(agt_var[arg1], 1);
		break;
	case 1009:
		get_obj(arg1);
		break;
	case 1010:
		get_obj(arg1);
		it_move(arg1, 1000);
		break;
	case 1011:
		drop_obj(arg1);
		break;
	case 1012:
		if (it_loc(arg1) == 1000) {
			if (PURE_WEAR) drop_obj(arg1);
			else it_move(arg1, 1);
		}
		break;
	case 1013:
		fontcmd(0, arg1 - 1);
		break;  /* Load font */
	case 1014:
		pictcmd(1, pictable[arg1 - 1]);
		break;  /* Show picture */
	case 1015:
		changepict(arg1, arg2);
		break; /* ChangePicture */
	case 1016:
		if (PICT_SUPPORT &&
		        yesno("Would you like to see the picture?"))
			pictcmd(1, pictable[arg1 - 1]);
		break;
	case 1017:
		pictcmd(2, arg1);
		break;  /* Show room pix */
	case 1018:
		if (PICT_SUPPORT &&
		        yesno("Would you like to see the picture?"))
			pictcmd(2, arg1 - 1);
		break;
	case 1019:
		musiccmd(1, arg1 - 1);
		break;
	case 1020:
		musiccmd(1, agt_rand(arg1, arg2) - 1);
		break;
	case 1021:
		musiccmd(2, arg1 - 1);
		break;
	case 1022:
		musiccmd(3, -1);
		break;  /* Stop Repeat */
	case 1023:
		musiccmd(4, -1);
		break;  /* Stop song */
	case 1024:
		musiccmd(5, -1);
		break;  /* Suspend song */
	case 1025:
		musiccmd(6, -1);
		break;   /* Resume song */
	case 1026:
		if (tnoun(dobj))
			noun[dobj - first_noun].movable = !noun[dobj - first_noun].movable;
		break;
	case 1027:
		it_newdesc(arg1, &msg_ptr[arg2 - 1]);
		break;
	case 1028:
		if (tnoun(arg1)) noun[arg1 - first_noun].points = arg2;
		else if (tcreat(arg1)) creature[arg1 - first_creat].points = arg2;
		else if (troom(arg1)) room[arg1 - first_room].points = arg2;
		break;
	case 1029:
		it_destroy(iobj);
		break;
	case 1030:
		tmpstr = agt_readline(3);
		i = strlen(tmpstr) - 1;
		if (i > 0 && tmpstr[i] == '\n') tmpstr[i] = 0;
		strncpy(userstr[arg1 - 1], tmpstr, 80);
		rfree(tmpstr);
		break;
	case 1031:
		agt_var[arg1] = read_number();
		break;
	case 1032:
		agt_var[arg1] = curr_time;
		break;
	case 1033:
		curr_time = normalize_time(agt_var[arg1]);
		break;
	case 1034:
		curr_time = normalize_time(arg1);
		break;
	case 1035:
		add_time(arg1);
		break;
	case 1036:
		delta_time = arg1;
		break;
	/* 1037 and 1038 are subroutine commands */
	case 1039:
		get_obj(dobj);
		break;
	case 1040:
		it_move(dobj, 1000);
		break;
	case 1041:
		drop_obj(dobj);
		break;
	case 1042:
		if (it_loc(dobj) == 1000) {
			if (PURE_WEAR) it_move(dobj, 1);
			else drop_obj(dobj);
		}
		break;
	case 1043: /* drop all */
		safecontloop(i, j, 1) drop_obj(i);
		break;
	case 1044: /* remove all */
		safecontloop(i, j, 1000) drop_obj(i);
		break;
	case 1045:
		deadflag = 1;
		break;
	case 1046:
		it_move(arg1, loc + first_room);
		break;
	case 1047:
		it_move(arg1, arg2);
		break;
	case 1048:
		it_reposition(arg1, arg2, 1);
		break; /* RePosition */
	case 1049:
		it_move(dobj, loc + first_room);
		break;
	case 1050:
		it_move(dobj, arg1);
		break;
	case 1051:
		safecontloop(i, j, 1) it_move(i, arg1);
		safecontloop(i, j, 1000) it_move(i, arg1);
		break;
	case 1052:
		nounloop(i)
		if (player_has(i + first_noun) && noun[i].points > arg2)
			it_move(i + first_noun, arg1);
		break;
	case 1053:
		safecontloop(i, j, arg1)
		if (tnoun(i)) it_move(i, arg2);
		break;
	case 1054:
		it_destroy(arg1);
		break;
	case 1055:
		it_destroy(dobj);
		break;
	case 1056:
		i = it_loc(arg1);
		it_move(arg1, it_loc(arg2));
		it_move(arg2, i);
		break;
	case 1057:
		it_move(arg1, it_loc(arg2));
		break;
	case 1058:
		it_move(dobj, it_loc(arg2));
		break;
	case 1059:
	case 1060: /* Add to/remove from group */
		if (tcreat(arg1))
			creature[arg1 - first_creat].groupmemb = (op_ == 1059);
		break;
	case 1061:   /* Move group */
		safecontloop(i, j, loc + first_room)
		if (it_group(i)) it_move(i, arg1);
		break;
	/* 1062 is RedirectTo */
	case 1063:
		msgout(agt_rand(arg1, arg2), 1);
		break;
	case 1064:
		print_contents(arg1, 1);
		break;
	case 1065:
	case 1066:
	case 1067:
	case 1068:
		obj_act(op_ - 1065, arg1);
		break;
	case 1069:
	case 1070:
	case 1071:
	case 1072:
		obj_act(op_ - 1069, dobj);
		break;
	case 1073:
		print_score();
		break;
	case 1074:
		tscore += arg1;
		break;
	case 1075:
		tscore -= arg1;
		break;
	case 1076:
		v_inventory();
		break;
	case 1077:
		wait_return();
		break;
	case 1078:
		writeln("Time passes...");
		break;
	case 1079:
		agt_delay(arg1);
		break;
	case 1080:
		agt_clrscr();
		break;
	case 1081:
		it_describe(arg1);
		break;
	case 1082:
		look_room();
		break;   /* LOOK */
	case 1083:
		msgout(arg1, 1);
		break;
	case 1084:
		writeln("");
		break;
	case 1085:
		if (PURE_TONE && sound_on)
			agt_tone(arg1, arg2);
		break; /* Tone */
	case 1086:
		agt_number = ask_for_number(arg1, arg2);
		break;
	case 1087:
		agt_answer = ask_question(arg1);
		break;
	case 1088:
		change_passage(loc, arg1, arg2);
		break;
	case 1089:
		flag[arg1] = 1;
		break;
	case 1090:
		flag[arg1] = 0;
		break;
	case 1091:
		flag[arg1] = !flag[arg1];
		break;
	case 1092:
		room[loc].flag_noun_bits |= (1 << (arg1 - 1));
		break; /* Roomflag on */
	case 1093:
		room[loc].flag_noun_bits &= ~(1 << (arg1 - 1));
		break; /* Off */
	case 1094:
		room[loc].flag_noun_bits ^= (1 << (arg1 - 1));
		break; /* Toggle */
	case 1095: /* if (agt_counter[arg1]==-1)*/
		agt_counter[arg1] = 1;
		break;
	case 1096:
		agt_counter[arg1] = -1;
		break;
	case 1097:
		agt_var[arg1] = arg2;
		break;
	case 1098:
		agt_var[arg1] += arg2;
		break;
	case 1099:
		agt_var[arg1] -= arg2;
		break;
	case 1100:
		agt_var[arg1] += agt_var[arg2];
		break;
	case 1101:
		agt_var[arg1] -= agt_var[arg2];
		break;
	case 1102:
		agt_var[arg1] = agt_rand(0, arg2);
		break;
	case 1103:
		agt_var[arg1] = dobj_rec->num;
		break;
	case 1104:
		agt_var[arg1] = iobj_rec->num;
		break;

	/* The following are v1.8x specific */
	case 1105:
		quote(arg1);
		break;
	case 1106:
		add_time(arg1);
		break;
	case 1107:
		add_time(-arg1);
		break;
	case 1108:
		curr_time = (curr_time % 100) + 100 * arg1;
		break;
	case 1109:
		curr_time = (curr_time / 100) * 100 + arg1;
		break;
	case 1110:
		add_time(agt_var[arg1]);
		break;
	case 1111:
		add_time(-agt_var[arg1]);
		break;
	case 1112:
		curr_time = (curr_time % 100) + 100 * agt_var[arg1];
		break;
	case 1113:
		curr_time = (curr_time / 100) * 100 + agt_var[arg1];
		break;

	/* Now for the AGX additions */
	case 1114:
		add_time(-arg1);
		break; /* ME-style SubtractFromTime */
	case 1115:
		disambig_score = arg1;
		break; /* SetDisambigPriority */
	case 1116:
		agt_var[arg1] = delta_time;
		break;
	case 1117: /* ChangeStatus */
		statusmode = arg1;
		break;
	case 1118:
		if (!mult_rangecheck(agt_var[arg1], arg2)) break;
		agt_var[arg1] *= arg2;
		break;
	case 1119:
		if (arg2 == 0) {
			if (!PURE_ERROR)
				writeln("GAME ERROR: Division by zero.");
		} else agt_var[arg1] /= arg2;
		break;
	case 1120:
		if (arg2 == 0) {
			if (!PURE_ERROR)
				writeln("GAME ERROR: Attempt to divide by zero.");
		} else agt_var[arg1] %= arg2;
		break;
	case 1121:
		agt_waitkey();
		break;
	case 1122:
		last_he = arg1;
		break;   /* SetHE */
	case 1123:
		last_she = arg1;
		break;
	case 1124:
		last_it = arg1;
		break;
	case 1125:
		last_they = arg1;
		break;
	case 1126:
		msgout(arg1, 0);
		break;
	case 1127:
		if (!PURE_ERROR)
			sysmsg(arg1, "GAME ERROR: Standard message not defined.");
		break;
	case 1128:
		msgout(arg1, 1);
		break; /* FailMessage */
	case 1129:  /* StdMessage */
		sysmsg(arg1, "GAME ERROR: Standard message not defined.");
		break;
	case 1130:
		msgout(arg2, 1);
		break; /* ErrMessage */
	case 1131: /* StdErrMessage */
		sysmsg(arg1, "GAME ERROR: Standard message not defined.");
		break;
	case 1132: /* AND */
		break;  /* These don't do anything under normal circumstances */
	case 1133:  /* SetClass */
		if (troom(arg1)) room[arg1 - first_room].oclass = arg2;
		else if (tnoun(arg1)) noun[arg1 - first_noun].oclass = arg2;
		else if (tcreat(arg1)) noun[arg1 - first_creat].oclass = arg2;
		break;
	case 1134:
		agt_var[arg1] = it_class(arg2);
		break; /* SetVariableToClass */

	/* Stack commands */
	case 1135:
		push_stack(arg1);
		break;
	case 1136:
		agt_var[arg1] = pop_stack();
		break;
	case 1137:
	case 1138:
	case 1139:
	case 1140:
	case 1141:
		op_stack(op_ - 1137); /* +,-,*, /,% * */
		break;
	case 1142: { /* DupStack */
		long n;
		n = pop_stack();
		push_stack(n);
		push_stack(n);
		break;
	}
	case 1143:
		pop_stack();
		break; /* Discard TOS */
	case 1144:
		agt_var[arg1] = agt_number;
		break; /* SetVariableToInput */
	case 1145:
		setattr(arg1, arg2, 1);
		break; /* Set */
	case 1146:
		setattr(arg1, arg2, 0);
		break; /* Clear */
	case 1147:
		push_stack(getprop(arg1, arg2));
		break; /* PushProp */
	case 1148:
		setprop(arg1, arg2, pop_stack());
		break; /* PopProp */
	/* 1149, 1150 handled by run_metacommand */
	/* 1151 is EndDisambig */
	/* 1152 is XRedirect */
	case 1153:
		rstrncpy(userstr[arg1 - 1], userstr[arg2 - 1], 81);
		break;
	case 1154:
		setcase(userstr[arg1 - 1], 1);
		break;
	case 1155:
		setcase(userstr[arg1 - 1], 0);
		break;
	case 1156:
		op_objflag(1, arg1, arg2);
		break;
	case 1157:
		op_objflag(0, arg1, arg2);
		break;
	case 1158:
		op_objflag(3, arg1, arg2);
		break;
	case 1159:
		push_stack(op_objprop(2, arg1, arg2, 0));
		break;
	case 1160:
		op_objprop(1, arg1, arg2, pop_stack());
		break;
	case 1161:
		move_in_dir(arg1, arg2);
		break;
	default:
		writeln("INTERNAL ERROR: Action token not supported.");
		rprintf("Action #%d", op_);
		writeln("");
	}
}

int exec_instr(op_rec *oprec)
/* This routine is responsible for executing all conditions and action
   tokens. Remember action tokens are numbered from 1000. */
/* Return codes:
   0 to continue running commands
   1 for a failed conditional token
   100 Next metacommand
   101 Stop running metacommands
   102 End turn
   103 Redirection */
{
	rbool r;

	if (oprec->op < 1000) {
		r = exec_cond(oprec->op, oprec->arg1, oprec->arg2);
		return (oprec->negate ? r : !r); /* Is it prefixed by NOT? */
	}
	switch (oprec->op) {
	case 1151: /* EndDisambig <num> */
		if (do_disambig) {
			disambig_score = oprec->arg1;
			return 102;  /* "End turn" if disambiguating */
		}
		return 0;  /* ... otherwise do nothing */
	case 1062:
	case 1152:
		return 103; /* Redirect */
	case WIN_ACT:
		winflag = 1;
		return 0;  /* win game */
	case (WIN_ACT+1):
		endflag = 1;
		return 102; /* end game */
	case (WIN_ACT+2):
		return 100; /* end this command */
	case (WIN_ACT+3):
		return 101; /* end all commands */
	case (WIN_ACT+4):
		return 102; /* end turn */
	default:
		exec_action(oprec->op, oprec->arg1, oprec->arg2);
		if (oprec->failmsg) return 102;
		else return 0;
	}
}

} // End of namespace AGT
} // End of namespace Glk