/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "glk/agt/agility.h"

namespace Glk {
namespace AGT {

void dbgprintf(const char *fmt, ...) {
	va_list vp;
	char buff[300];

	va_start(vp, fmt);
	Common::vsprintf_s(buff, fmt, vp);
	va_end(vp);

	debugout(buff);
}


static void print_msg(descr_ptr dptr) {
	int j;
	descr_line *txt;

	txt = read_descr(dptr.start, dptr.size);
	if (txt != nullptr) {
		for (j = 0; txt[j] != nullptr; j++) {
			dbgprintf("\n");
			debugout(txt[j]);
		}
	}
	free_descr(txt);
}


static char *getname(int inum)
/* Name should be 20 chars or less */
{
	if (inum == 0) return rstrdup("* 0 *");
	return objname(inum);
}


extern integer dobj, iobj, actor;

void print_special_obj(int i)
/* This is called by the disassembler in agtdbg.c */
/* i=0 NOUN, 1 OBJECT, 2 NAME */
{
	int dval;
	char *s;
	switch (i) {
	case 0:
		dval = dobj;
		dbgprintf("NOUN");
		break;
	case 1:
		dval = iobj;
		dbgprintf("OBJECT");
		break;
	case 2:
		dval = actor;
		dbgprintf("NAME");
		break;
	default:
		dval = 0; /* Silence compiler warnings. */
		fatal("INTERNAL ERROR: Invalid *dval* in print_special_obj.");
	}
	if (dbgflagptr == nullptr)
		/* This determines whether we are linked with agtout or agil */
		return;
	s = getname(dval);
	dbgprintf("(%d:%s)", dval, s);
	rfree(s);
}

#define printval(str,index,ptr) {dbgprintf("[%s%d",str,index);\
		if (ptr==NULL) dbgprintf("]");\
		else dbgprintf("=%ld]",(long)ptr[index]);}

int argout(int dtype, int dval, int optype) {
	char *s;

	if (dtype & AGT_VAR) dtype = AGT_VAR;

	if ((optype & 3) == 1) /* variable */
		dtype = AGT_VAR;
	if (optype & 2) { /* NOUN or OBJECT */
		if (dtype >= 64 && dtype != AGT_NUM)
			dbgprintf("ILL:");
		if (optype == 2)
			print_special_obj(0); /* NOUN */
		else
			print_special_obj(1);  /* OBJECT */
		return 0;
	}

	if (!interp_arg)
		dbgprintf("%d", dval);
	else {
		if (dtype < 64) {
			if (dval == -1)
				print_special_obj(2); /* NAME */
			else {
				s = getname(dval);
				if (dtype & (AGT_ITEM | AGT_CREAT | AGT_SELF | AGT_WORN))
					dbgprintf("<%d:%s>", dval, s);
				else
					dbgprintf("{%d:%s}", dval, s);
				rfree(s);
			}
		} else if ((dtype & AGT_VAR) != 0) {
			if (dval == -1)
				print_tos();
			else
				printval("Var", dval, dbgvarptr);
		} else switch (dtype) {
			case AGT_TIME:
				dbgprintf("%2d:%2d", dval / 100, dval % 100);
				break;
			case AGT_NUM:  /* Numeric */
				dbgprintf("%d", dval);
				break;
			case AGT_FLAG:  /* Flag */
				printval("Flg", dval, dbgflagptr);
				break;
			case AGT_ROOMFLAG: /* Roomflag */
				dbgprintf("RoomFlag%d", dval);
				break;
			case AGT_QUEST:  /* Question */
				if (dval <= MaxQuestion && dval >= 1 && question != nullptr) {
					dbgprintf("\nQ%d:%s\n", dval, question[dval - 1]);
					dbgprintf("[A:%s]", answer[dval - 1]);
				} else if (quest_ptr != nullptr) {
					dbgprintf("\nQ%d: ", dval);
					print_msg(quest_ptr[dval - 1]);
					dbgprintf("[A:");
					print_msg(ans_ptr[dval - 1]);
				}
				break;
			case AGT_MSG: /* Message */
				if (dval > last_message || dval < 1 || msg_ptr == nullptr)
					dbgprintf("ILLEGAL MESSAGE");
				else {
					dbgprintf("(Msg%d)", dval);
					if (!dbg_nomsg)
						print_msg(msg_ptr[dval - 1]);
				}
				break;
			case AGT_ERR: /* Message */
				if (dval > NUM_ERR || dval < 1 || err_ptr == nullptr)
					dbgprintf("ILLEGAL MESSAGE");
				else {
					dbgprintf("(Std%d)", dval);
					if (!dbg_nomsg)
						print_msg(err_ptr[dval - 1]);
				}
				break;
			case AGT_STR: /* String */
				if (dval - 1 >= MAX_USTR || userstr == nullptr)
					dbgprintf("ILLEGAL STRING");
				else
					dbgprintf("\nStr%d:%s", dval, userstr[dval]);
				break;
			case AGT_CNT: /* Counter */
				printval("Cnt", dval, dbgcntptr);
				break;
			case AGT_DIR: /* Direction */
				if (dval >= 1 && dval <= 13)
					dbgprintf("%s", exitname[dval - 1]);
				else dbgprintf("ILL_DIR(%d)", dval);
				break;
			case AGT_SUB: /* Subroutine */
				dbgprintf("Subroutine %d", dval);
				break;
			case AGT_PIC: /* Picture */
			case AGT_PIX:
				dbgprintf("Picture #%d", dval);
				break;
			case AGT_FONT: /* Font */
				dbgprintf("Font #%d", dval);
				break;
			case AGT_SONG:  /* Song */
				dbgprintf("Song #%d", dval);
				break;
			case AGT_OBJFLAG:
				dbgprintf("ObjFlag%d", dval);
				break;
			case AGT_OBJPROP:
				dbgprintf("ObjProp%d", dval);
				break;
			case AGT_ATTR:
				if (dval < 0 || dval >= NUM_ATTR)
					dbgprintf("UnkownAttr%d", dval);
				else
					dbgprintf("%s", attrlist[dval].name);
				break;
			case AGT_PROP:
				if (dval < 0 || dval >= NUM_PROP)
					dbgprintf("UnknownProp%d", dval);
				else
					dbgprintf("%s", proplist[dval].name);
				break;
			case AGT_EXIT:
				if (dval >= exitmsg_base)
					argout(AGT_MSG, dval - exitmsg_base, 0);
				else
					argout(AGT_ROOM, dval, 0);
				break;
			default:
				dbgprintf("?+%d", dval);
			}
	}
	return 1;
}


void debug_newline(integer op, rbool first_nl) {
	rbool early_nl;

	if (!dbg_nomsg) return;
	early_nl = (op == 1008 || op == 1027 || op == 1083 || op == 1105
	            || (op >= 1126 && op <= 1131));
	if (early_nl == first_nl)
		debugout("\n");
}


void debug_cmd_out(int ip, integer op, int arg1, int arg2, int optype) {
	int j;
	const opdef *opdata;
	rbool save_dbg_nomsg;

	dbgprintf("  %2d:", ip);
	save_dbg_nomsg = 0; /* Just to silence compiler warnings. */

	opdata = get_opdef(op);
	if (opdata == &illegal_def)
		dbgprintf("ILLEGAL %d\n", op);
	else {
		if (op >= END_ACT) dbgprintf("!"); /* "Terminal" Actions */
		else if (op <= MAX_COND) dbgprintf("?"); /* Condition */
		if (op == 1063) { /* RandomMessage needs special handling */
			save_dbg_nomsg = dbg_nomsg;
			dbg_nomsg = 1;
		}
		dbgprintf("%s", opdata->opcode);
		for (j = 0; j < opdata->argnum; j++) {
			dbgprintf("\t");
			argout(j == 0 ? opdata->arg1 : opdata->arg2 , j == 0 ? arg1 : arg2,
			       optype >> 2);
			optype <<= 2;
		}
		if (op == 1063)
			dbg_nomsg = save_dbg_nomsg;
	}
	debug_newline(op, 1);
}


void debug_head(int i) {
	int v, w, a;

	v = verb_code(command[i].verbcmd);
	if (v >= BASE_VERB && v < BASE_VERB + DUMB_VERB && syntbl[synlist[v]] != 0)
		w = syntbl[synlist[v]];
	else w = command[i].verbcmd;
	if (command[i].actor > 0) {
		dbgprintf("CMD %d: ", i);
		a = command[i].actor;
	} else {
		dbgprintf("REDIR: ");
		a = -command[i].actor;
	}

	if (a == 2)
		dbgprintf("anybody, ");
	else if (a > 2) {
		char *name;
		name = objname(a);
		name[0] = toupper(name[0]);
		dbgprintf("%s, ", name);
		rfree(name);
	}

	dbgprintf("%s ", w == 0 ? "any" : dict[w]);
	if (command[i].noun_adj != 0)
		dbgprintf("%s ", gdict(command[i].noun_adj));
	dbgprintf("%s %s ", gdict(command[i].nouncmd),
	          (ver == 3) ? gdict(command[i].prep) : "->");
	if (command[i].obj_adj != 0)
		dbgprintf("%s ", gdict(command[i].obj_adj));
	dbgprintf("%s\n", gdict(command[i].objcmd));

}

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