/* 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/>.
 *
 */

/* Based on the Hugo interpreter version 3.3.0 */

#ifndef GLK_HUGO_HUGO
#define GLK_HUGO_HUGO

#include "common/scummsys.h"
#include "common/str.h"
#include "glk/glk_api.h"
#include "glk/hugo/htokens.h"
#include "glk/hugo/hugo_defines.h"
#include "glk/hugo/hugo_types.h"
#include "glk/hugo/stringfn.h"

namespace Glk {
namespace Hugo {

class ResourceArchive;

/**
 * Hugo game interpreter
 */
class Hugo : public GlkAPI, public HTokens, public StringFunctions {
	friend class ResourceArchive;
private:
	int _savegameSlot;
	winid_t mainwin, currentwin;
	winid_t secondwin, auxwin;
	bool runtime_warnings;
	int dbnest;

	/**
	 * address_scale refers to the factor by which addresses are multiplied to
	 * get the "real" address.  In this way, a 16-bit integer can reference
	 * 64K * 16 = 1024K of memory.
	 */
	int address_scale;

	// heexpr
	int eval[MAX_EVAL_ELEMENTS];		///< expression components
	int evalcount;						///< # of expr. components
	int var[MAXLOCALS + MAXGLOBALS];	///< variables
	int incdec;							///< value is being incremented/dec.
	char getaddress;					///< true when finding &routine
	char inexpr;						///< true when in expression
	char inobj;							///< true when in object compound
	int last_precedence;

	// hemedia
	schanid_t mchannel;
	schanid_t schannel;
	long resids[2][MAXRES];
	int numres[2];

	// hemisc
	char gamefile[255];
	int game_version;
	int object_size;
	Common::SeekableReadStream *game;
	HUGO_FILE script;
	HUGO_FILE playback;
	HUGO_FILE record;
	HUGO_FILE io; char ioblock; char ioerror;

	char id[3];
	char serial[9];
	unsigned int codestart;
	unsigned int objtable;
	unsigned int eventtable;
	unsigned int proptable;
	unsigned int arraytable;
	unsigned int dicttable;
	unsigned int syntable;
	unsigned int initaddr;
	unsigned int mainaddr;
	unsigned int parseaddr;
	unsigned int parseerroraddr;
	unsigned int findobjectaddr;
	unsigned int endgameaddr;
	unsigned int speaktoaddr;
	unsigned int performaddr;
	int objects;
	int events;
	int dictcount;
	int syncount;
	char context_command[MAX_CONTEXT_COMMANDS][64];
	int context_commands;
	unsigned char *mem;
	bool loaded_in_memory;
	unsigned int defseg;
	unsigned int gameseg;
	long codeptr;
	long codeend;
	char pbuffer[MAXBUFFER * 2 + 1];
	int currentpos;
	int currentline;
	int full;
	signed char def_fcolor, def_bgcolor, def_slfcolor, def_slbgcolor;
	signed char fcolor, bgcolor, icolor, default_bgcolor;
	int currentfont;
	char capital;
	unsigned int textto;
	int SCREENWIDTH, SCREENHEIGHT;
	int physical_windowwidth, physical_windowheight,
		physical_windowtop, physical_windowleft,
		physical_windowbottom, physical_windowright;
	int inwindow;
	int charwidth, lineheight, FIXEDCHARWIDTH, FIXEDLINEHEIGHT;
	int current_text_x, current_text_y;
	bool skipping_more;
	int undostack[MAXUNDO][5];
	int undoptr;
	int undoturn;
	char undoinvalid;
	char undorecord;

	bool in_valid_window;
	int glk_fcolor, glk_bgcolor;
	int mainwin_bgcolor;
	int glk_current_font;
	bool just_cleared_screen;
	int secondwin_bottom;

	// heobject
	int display_object;				///< i.e., non-existent (yet)
	char display_needs_repaint;		///< for display object
	int display_pointer_x, display_pointer_y;

	// heparse
	char buffer[MAXBUFFER + MAXWORDS];
	char errbuf[MAXBUFFER + 1];				///< last invalid input
	char line[1025];						///< line buffer

	int words;								///< parsed word count
	char *word[MAXWORDS + 1];				///< breakdown into words
	unsigned int wd[MAXWORDS + 1];			///<     "      "   dict. entries
	unsigned int parsed_number;				///< needed for numbers in input

	signed char remaining;					///< multiple commands in input
	char parseerr[MAXBUFFER + 1];			///< for passing to RunPrint, etc.
	char parsestr[MAXBUFFER + 1];			///< for passing quoted string
	char xverb;								///< flag; 0 = regular verb
	char starts_with_verb;					///< input line; 0 = no verb word
	unsigned int grammaraddr;				///< address in grammar
	char *obj_parselist;					///< objects with noun/adjective
	int domain, odomain;					///< of object(s)
	int objlist[MAXOBJLIST];				///< for objects of verb
	char objcount;							///< of objlist
	char parse_allflag;						///< for "all" in MatchObject
	pobject_structure pobjlist[MAXPOBJECTS];	///< for possible objects
	int pobjcount;							///< of pobjlist
	int pobj;								///< last remaining suspect
	int obj_match_state;					///< see MatchCommand() for details
	char objword_cache[MAXWORDS];			///< for MatchWord() xobject, etc.
	char object_is_number;					///< number used in player command
	unsigned int objgrammar;				///< for 2nd pass
	int objstart;							///<  "   "   "
	int objfinish;							///<  "   "   "
	bool addflag;							///< true if adding to objlist[]
	int speaking;							///< if command is addressed to obj.

	char oops[MAXBUFFER + 1];				///< illegal word
	int oopscount;							///< # of corrections in a row

	char parse_called_twice;
	char reparse_everything;
	char punc_string[64];					///< punctuation string
	byte full_buffer;

	/**
	 * to MatchObject()
	 * Necessary for proper disambiguation when addressing a character;
	 * i.e., when 'held' doesn't refer to held by the player, etc.
	 */
	char recursive_call;
	int parse_location;						///< usually var[location]

	// heres
	HUGO_FILE resource_file;
	int extra_param;
	char loaded_filename[MAX_RES_PATH];
	char loaded_resname[MAX_RES_PATH];
	char resource_type;

	// herun
	int passlocal[MAXLOCALS];		///< locals passed to routine
	int arguments_passed;			///< when calling routine
	int ret;						///< return value and returning flag
	char retflag;
	bool during_player_input;
	char override_full;
	bool game_reset;				///< for restore, undo, etc.

	CODE_BLOCK code_block[MAXSTACKDEPTH];
	int stack_depth;
	int tail_recursion;
	long tail_recursion_addr;

	// Used by RunWindow for setting current window dimensions:
	int last_window_top, last_window_bottom, last_window_left, last_window_right;
	int lowest_windowbottom,				///< in text lines
		physical_lowest_windowbottom;		///< in pixels or text lines
	bool just_left_window;

	// heset
	char game_title[MAX_GAME_TITLE];
	char arrexpr;							///< true when assigning array
	char multiprop;							///< true in multiple prop. assign.
	int set_value;

#if defined (DEBUGGER)
	char debug_line[MAX_DEBUG_LINE];
	bool debug_eval;
	bool debug_eval_error;
	bool debugger_step_over;
	bool debugger_finish;
	bool debugger_run;
	bool debugger_interrupt;
	bool debugger_skip;
	bool runtime_error;
	uint currentroutine;
	bool complex_prop_breakpoint;
	bool trace_complex_prop_routine;
	char *objectname[MAX_OBJECT];
	char *propertyname[MAX_PROPERTY];
	CALL call[MAXCALLS];
	int routines;
	int properties;
	WINDOW window[9];
	int codeline[9][100];
	char localname[9][100];
	int current_locals;
	long this_codeptr;
	int debug_workspace;
	int attributes;
	int original_dictcount;
	int buffered_code_lines;
	bool debugger_has_stepped_back;
	bool debugger_step_back;
	int debugger_collapsing;
	int runaway_counter;
	int history_count;
	int active_screen;
	int step_nest;
	BREAKPOINT breakpoint[MAXBREAKPOINTS];
	BREAKPOINT watch[MAXBREAKPOINTS];
	int code_history[MAX_CODE_HISTORY];
	int dbnest_history[MAX_CODE_HISTORY];
	int history_last;
#endif
private:
	/**
	 * \defgroup heexpr
	 * @{
	 */

	 /**
	  * The new-and-improved expression evaluator.  Evaluates the current expression
	  * (or sub-expression therein) beginning at eval[p].
	  */
	int EvalExpr(int p);

	/**
	 * Called by GetValue(); does the actual dirty work of returning a value from a
	 * simple data type.
	 */
	int GetVal();

	/**
	 * Does any reckoning for more sophisticated constructions.
	 */
	int GetValue();

	/**
	 * Actually performs the increment given below by IsIncrement.
	 */
	int Increment(int a, char inctype);

	/**
	 * If an increment/decrement is next up (i.e. ++, --, or +=, *=, etc.),
	 * then sets incdec equal to the increment/decrement and repositions codeptr.
	 * Returns the token number of the operation, if any.
	 */
	char IsIncrement(long addr);

	/**
	 *  Returns the precedence ranking of the operator represented by token[t].
	 * The lower the return value, the higher the rank in terms of processing order.
	 */
	int Precedence(int t);

	/**
	 * Reads the current expression from the current code position into eval[],
	 * using the following key:
	 *
	 * if eval[n] is 0, eval[n+1] is a value
	 * if eval[n] is 1, eval[n+1] is a token
	 *
	 * <inexpr> is used in various routines to keep track of whether or not we're currently
	 * reading an expression.  If <inexpr> is 1, we're in an expression; if 2, we may have
	 * to step back one code position if encountering a closing parentheses.
	 */
	void SetupExpr();

	/**
	 * Cuts off straggling components of eval[] after an expression or sub-expression
	 * has been successfully evaluated.
	 */
	void TrimExpr(int ptr);

	/**@}*/

	/**
	 * \defgroup heglk
	 * @{
	 */

	 /**
	  * Does whatever has to be done to initially set up the display
	  */
	void hugo_init_screen();

	/**
	 * Does whatever has to be done to clean up the display pre-termination
	 */
	void hugo_cleanup_screen() {
		// No implementation
	}

	void hugo_closefiles() {
		// Glk closes all files on exit
	}

	int hugo_getkey() const {
		// Not needed here--single-character events are handled solely by hugo_waitforkey(), below
		return 0;
	}

	/**
	 * Gets a line of input from the keyboard, storing it in <buffer>.
	 */
	void hugo_getline(const char *prmpt);

	/**
	 * Provided to be replaced by multitasking systems where cycling while waiting
	 * for a keystroke may not be such a hot idea.
	 */
	int hugo_waitforkey();

	/**
	 * Returns true if a keypress is waiting to be retrieved.
	 */
	int hugo_iskeywaiting();

	/**
	 * Waits for 1/n seconds.  Returns false if waiting is unsupported.
	 */
	int hugo_timewait(int n);

	/**
	 * Clears everything on the screen, moving the cursor to the top-left corner of the screen
	 */
	void hugo_clearfullscreen();

	/**
	 * Clears the currently defined window, moving the cursor to the top-left corner of the window
	 */
	void hugo_clearwindow();

	/**
	 * This function does whatever is necessary to set the system up for a standard text display
	 */
	void hugo_settextmode();

	void hugo_settextwindow(int left, int top, int right, int bottom);

	/**
	 * Specially accommodated in GetProp() While the engine thinks that the linelength is 0x7fff,
	this tells things like the display object the actual length.  (Defined as ACTUAL_LINELENGTH)
	*/
	int heglk_get_linelength();

	/**
	 * Similar to heglk_get_linelength().  (Defined as ACTUAL_SCREENHEIGHT)
	 */
	int heglk_get_screenheight();

	void hugo_settextpos(int x, int y);

	/**
	 * Essentially the same as printf() without formatting, since printf() generally doesn't take
	 * into account color setting, font changes, windowing, etc.
	 *
	 * The newline character '\n' must be explicitly included at the end of a line in order to
	 * produce a linefeed.  The new cursor position is set to the end of this printed text.
	 * Upon hitting the right edge of the screen, the printing position wraps to the start
	 * of the next line.
	 */
	void hugo_print(const char *a);

	/**
	 * Scroll the text window
	 */
	void hugo_scrollwindowup() {
		// No implementation. Glk takes care of it
	}

	/**
	 * Set the font
	 * @param f		The <f> argument is a mask containing any or none of:
	 *				BOLD_FONT, UNDERLINE_FONT, ITALIC_FONT, PROP_FONT.
	 */
	void hugo_font(int f);

	/**
	 * Set the foreground (print) color
	 */
	void hugo_settextcolor(int c);

	/**
	 * Set the background color
	 */
	void hugo_setbackcolor(int c);

	/**
	 * Color-setting functions should always pass the color through hugo_color()
	 * in order to properly set default fore/background colors
	 */
	int hugo_color(int c);

	/**
	 * Get the width of a character
	 * @remarks		As given here, this function works only for non-proportional printing.
	 * For proportional printing, hugo_charwidth() should return the width of the supplied
	 * character in the current font and style.
	*/
	int hugo_charwidth(char a) const;

	/**
	 * Return the width of a string
	 */
	int hugo_textwidth(const char *a) const;

	/**
	 * Return the length of a string
	 */
	int hugo_strlen(const char *a) const;

	void hugo_setgametitle(const char *t);

	int hugo_hasvideo() const;

	int hugo_playvideo(HUGO_FILE infile, long reslength, char loop_flag, char background, int volume);

	void hugo_stopvideo();

	/**@}*/

	/**
	 * \defgroup hemisc
	 * @{
	 */

	/**
	 * The all-purpose printing routine that takes care of word-wrapping.
	 */
	void AP(const char *a);

	/**
	 * Used whenever a routine is called, assumes the routine address and begins
	 * with the arguments (if any).
	 */
	int CallRoutine(unsigned int addr);

	/**
	 * Adds a command to the context command list.  A zero value (i.e., an empty string)
	 * resets the list.
	 */
	void ContextCommand();

	/**
	 * Dynamically creates a new dictionary entry.
	 */
	unsigned int Dict();

	/**
	 * Generates a fatal error
	 */
	void FatalError(int n);

	void FileIO();

	void Flushpbuffer();

	void GetCommand();

	/**
	 * From any address <addr>; the segment must be defined prior to calling the function.
	 */
	char *GetString(long addr);

	/**
	 * Get text block from position <textaddr> in the text bank.  If the game was not fully loaded
	 * in memory, i.e., if loaded_in_memory is not true, the block is read from disk.
	 */
	char *GetText(long textaddr);

	/**
	 * From the dictionary table.
	 */
	char *GetWord(unsigned int w);

	void HandleTailRecursion(long addr);

	void InitGame();

	void LoadGame();

	/**
	 * Must be called before running every new routine, i.e. before calling RunRoutine().
	 * Unfortunately, the current locals must be saved in a temp array prior to calling.
	 * The argument n gives the number of arguments passed.
	 */
	void PassLocals(int n);

	inline unsigned char Peek(long a) {
		return MEM(defseg * 16L + a);
	}

	inline unsigned int PeekWord(long a) {
		return (unsigned char)MEM(defseg * 16L + a) + (unsigned char)MEM(defseg * 16L + a + 1) * 256;
	}

	inline void Poke(unsigned int a, unsigned char v) {
		SETMEM(defseg * 16L + a, v);
	}

	inline void PokeWord(unsigned int a, unsigned int v) {
		SETMEM(defseg * 16L + a, (char)(v % 256));
		SETMEM(defseg * 16L + a + 1, (char)(v / 256));
	}

	/**
	 * Returns <a> as a hex-number string in XXXXXX format.
	 */
	static const char *PrintHex(long a);

	/**
	 * Print to client display taking into account cursor relocation,
	 * font changes, color setting, and window scrolling.
	 */
	void Printout(char *a, int no_scrollback_linebreak);

	void PromptMore();

	int RecordCommands();

	/**
	 * Formats:
	 *
	 * end of turn:    (0, undoturn, 0, 0, 0)
	 * move obj.:      (MOVE_T, obj., parent, 0, 0)
	 * property:       (PROP_T, obj., prop., # or PROP_ROUTINE, val.)
	 * attribute:      (ATTR_T, obj., attr., 0 or 1, 0)
	 * variable:       (VAR_T, var., value, 0, 0)
	 * array:          (ARRAYDATA_T, array addr., element, val., 0)
	 * dict:           (DICT_T, entry length, 0, 0, 0)
	 * word setting:   (WORD_T, word number, new word, 0, 0)
	 */
	void SaveUndo(int a, int b, int c, int d, int e);

	/**
	 * Properly sets up the code_block structure for the current stack depth depending
	 * on if this is a called block (RUNROUTINE_BLOCK) or otherwise.
	 */
	void SetStackFrame(int depth, int type, long brk, long returnaddr);

	void SetupDisplay();

	/**
	 * The method is passed <a> as the string and <*i> as the position in the string.
	 * The character(s) at a[*i], a[*(i+1)], etc. are converted into a single Latin-1
	 * (i.e., greater than 127) character value.
	 *
	 * Assume that the AP() has already encountered a control character ('\'),
	 * and that a[*i]... is one of:
	 *
	 * `a	accent grave on following character (e.g., 'a')
	 * 'a	accent acute on following character (e.g., 'a')
	 * ~n	tilde on following (e.g., 'n' or 'N')
	 * :a	umlaut on following (e.g., 'a')
	 * ^a	circumflex on following (e.g., 'a')
	 * ,c	cedilla on following (e.g., 'c' or 'C')
	 * <	Spanish left quotation marks
	 * >	Spanish right quotation marks
	 * !	upside-down exclamation mark
	 * ?	upside-down question mark
	 * ae	ae ligature
	 * AE	AE ligature
	 * c	cents symbol
	 * L	British pound
	 * Y	Japanese Yen
	 * -	em (long) dash
	 * #nnn	character value given by nnn
	 *
	 * Note that the return value is a single character--which will be either unchanged
	 * or a Latin-1 character value.
	 */
	char SpecialChar(const char *a, int *i);

	int Undo();

	/**@}*/

	/**
	 * \defgroup hemedia
	 * @{
	 */

	int loadres(HUGO_FILE infile, int reslen, int type);

	int hugo_hasgraphics();

	void initsound();

	void initmusic();

	int hugo_playmusic(HUGO_FILE infile, long reslen, char loop_flag);

	void hugo_musicvolume(int vol);

	void hugo_stopmusic();

	int hugo_playsample(HUGO_FILE infile, long reslen, char loop_flag);

	void hugo_samplevolume(int vol);

	void hugo_stopsample();

	/**@}*/

	/**
	 * \defgroup heobject - Object/property/attribute management functions
	 * @{
	 */
	int Child(int obj);

	int Children(int obj);

	int Elder(int obj);

	/**
	 * Returns one of four sets of 32 attributes.
	 */
	unsigned long GetAttributes(int obj, int attribute_set);

	/**
	 * Returns the value of '<obj>.<p> #<n>'  If <s> is true, the self global
	 * is not set to <obj> in order to facilitate <obj>..<p> calls.
	 */
	int GetProp(int obj, int p, int n, char s);

	/**
	 * Returns the value of the last object above <obj> in the tree before object 0.
	 */
	int GrandParent(int obj);

	void MoveObj(int obj, int p);

	const char *Name(int obj);

	int Parent(int obj);

	/**
	 * Returns address of <obj>.<p> (with <offset> provided for additive properties--
	 * i.e. subsequent calls with the same <obj> and <p>.
	 */
	unsigned int PropAddr(int obj, int p, unsigned int offset);

	/**
	 * Writes (puts) one of four sets of 32 attributes.
	 */
	void PutAttributes(int obj, unsigned long a, int attribute_set);

	/**
	 * Set an attribute
	 * c = 1 for set, 0 for clear
	 */
	void SetAttribute(int obj, int attr, int c);

	int Sibling(int obj);

	int TestAttribute(int obj, int attr, int nattr);

	int Youngest(int obj);

	/**@}*/

	/**
	 * \defgroup heparse
	 * @{
	 */

	void AddAllObjects(int loc);

	/**
	 * Adds the object <obj> to objlist[], making all related adjustments.
	 */
	void AddObj(int obj);

	/**
	 * Adds <obj> as a contender to the possible object list, noting that it was referred
	 * to as either a noun or an adjective.
	 */
	void AddPossibleObject(int obj, char type, unsigned int w);

	/**
	 * Move the address in the grammar table past the current token.
	 */
	void AdvanceGrammar();

	/**
	 * For when it's only necessary to know if word[wn] is an object word for any object,
	 * not a particular object.  Returns 1 for an object word or -1 for a non-object word.
	 */
	int AnyObjWord(int wn);

	/**
	 * The non_grammar argument is true when called from a non-grammar function such as RunEvents().
	 */
	int Available(int obj, char non_grammar);

	void CallLibraryParse();

	/**
	 * Takes into account the preset domain for checking an object's presence;
	 * <domain> is 0, -1, or an object number..
	 */
	int DomainObj(int obj);

	/**
	 * Returns the dictionary address of <a>.
	 */
	unsigned int FindWord(const char *a);

	/**
	 * Checks to see if <obj> is in objlist[].
	 */
	int InList(int obj);

	/**
	 * Deletes word[a].
	 */
	void KillWord(int a);

	/**
	 * Here, briefly, is how MatchCommand() works:
	 *
	 * 1.  Match the verb.
	 *
	 * 2.  If no match, check to see if the line begins with an object (character)
	 * and try to match it.
	 *
	 * 3.  If found, try to match a syntax for that verb, including objects, dictionary words,
	 * numbers, attributes, and routines.  If any objects are specified, skip over them for now,
	 * marking the start and finish.  This is done mostly in MatchWord().
	 *
	 * 4.  Match the xobject, if there is one--via MatchObject().
	 *
	 * 5.  If all is well, return to match the objects that were previously skipped over,
	 * loading them into objlist[]. Once again, this is done by MatchObject().
	 *
	 * (The reason the objects are initially skipped is because it may be necessary to know
	 * where to look for them--this may require knowing what the xobject is, if the syntax
	 * is something like:
	 *
	 * "get" <object> "from" <xobject>)
	 *
	 * The variable <obj_match_state> is the indicator of what stage object-matching is at:
	 *
	 * obj_match_state = 0  - haven't matched anything yet
	 *
	 * obj_match_state = 1  - xobject has been matched
	 *
	 * obj_match_state = 2  - matching object(s), loading objlist[]
	 *
	 * obj_match_state = 5  - matching first word/name, i.e., "Bob, <do something>"
	 */
	int MatchCommand();

	/**
	 * The argument is the word number we're starting matching on.
	 *
	 * NOTE:  recusive_call is set to 0 if this is the first call. MatchObject() sets it to 1
	 * when calling itself when asking for clarification as to which object is meant.
	 *
	 * Return true on a recursive call to allow parsing to continue.
	 */
	bool MatchObject(int *wordnum);

	int MatchWord(int *wordnum);

	/**
	 * Returns true if the specified object has the specified word as an adjective or noun
	 * (as specified by type).
	 */
	int ObjWordType(int obj, unsigned int w, int type);

	/**
	 * Returns <adjective> if the word at dictionary address <w> is an adjective of <obj>,
	 * or <noun> if it is a noun.
	 */
	int ObjWord(int obj, unsigned int w);

	/**
	 * Turns word[] into dictionary addresses stored in wd[].  Takes care of fingering illegal
	 * (unknown) words and doing alterations such as compounds, removals, and synonyms.
	 */
	int Parse();

	void ParseError(int e, int a);

	/**
	 * Deletes wd[a].
	 */
	void RemoveWord(int a);

	/**
	 * Call FindObject(0, 0) to reset library's disambiguation mechanism.
	 */
	void ResetFindObject();

	/**
	 * Splits <buffer> into the word[] array.  Also does nifty things such as turning time
	 * values such as hh:mm into a single number (representing minutes from midnight).
	 */
	void SeparateWords();

	/**
	 * Removes object <obj> from objlist[], making all related adjustments.
	 */
	void SubtractObj(int obj);

	/**
	 * Removes <obj> as a possible contender for object disambiguation.
	 */
	void SubtractPossibleObject(int obj);

	/**
	 * Called by MatchObject() to see if <obj> is available, and add it to or subtract
	 * it from objlist[] accordingly.
	 */
	void TryObj(int obj);

	/**
	 * Checks first of all to see if an object is available, then checks if it meets
	 * all the qualifications demanded by the grammar syntax.
	 */
	int ValidObj(int obj);

	/**@}*/

	/**
	 * \defgroup heres
	 * @{
	 */

	void DisplayPicture();

	void PlayMusic();

	void PlaySample();

	void PlayVideo();

	/**
	 * Assumes that filename/resname contain a resourcefile name and a resource name.
	 * If resname is "", filename contains the path of the resource on disk.
	 * Returns the length of the resource if the named resource is found.
	 *
	 * If FindResource() returns non-zero, the file is hot, i.e., it is open and positioned
	 * to the start of the resource.
	 *
	 * Note that resourcefiles are expected to be in (if not the current directory) "object" or "games",
	 * and on-disk resources in (if not the given directory) "source" or "resource" (where these are the
	 * environment variables "HUGO_...", not actual on-disk directories).
	 */
	long FindResource(const char *filename, const char *resname);

	/**
	 * Processes resourcefile/filename (and resource, if applicable).
	 * Returns 0 if a valid 0 parameter is passed as in "music 0" or "sound 0".
	 */
	int GetResourceParameters(char *filename, char *resname, int restype);

	/**@}*/

	/**
	* \defgroup herun
	* @{
	*/

	void RunDo();

	void RunEvents();

	void playGame();

	void RunIf(char override);

	void RunInput();

	/**
	 * (All the debugger range-checking is important because invalid memory writes due
	 * to invalid object location calculations are a good way to crash the system.)
	 */
	void RunMove();

	void RunPrint();

	int RunRestart();

	int RestoreGameData();

	int RunRestore();

	/**
	 * This is the main loop for running each line of code in sequence;
	 * the main switch statement is based on the first token in each line.
	 *
	 * This routine is relatively complex, especially given the addition of debugger control.
	 * Basically it is structured like this:
	 *
	 * 1.  If this is the debugger build, see what debugger information has to be set up upon
	 * calling this block of code
	 *
	 * 2.  Get the next token, and as long as it isn't CLOSE_BRACE_T ('}')...
	 *
	 * 3.  ...If this is the debugger build, see if there is a standing debugger_interrupt
	 * to pass control back to the debugger, and perform all operations for stepping
	 * tracing, breakpoints, etc.
	 *
	 * 4.  ...See what token we're dealing with and execute accordingly
	 *
	 * 5.  ...Loop back to (2)
	 *
	 * 6.  If this is the debugger build, do whatever is necessary to tidy up after finishing
	 * this block of code
	 *
	 * There's a bit of a trick involved since the original language design uses "{...}"
	 * structures for both conditionals and blocks that necessitate another (i.e., nested) call
	 * to RunRoutine(). The call_block structure array and stack_depth variable are the
	 * navigation guides.
	 */
	void RunRoutine(long addr);

	int RunSave();

	int RunScriptSet();

	/**
	 * As in 'x = string(<array>, "<string>"[, maxlen]'.
	 */
	int RunString();

	int RunSystem();

	void SaveWindowData(SAVED_WINDOW_DATA *spw);

	void RestoreWindowData(SAVED_WINDOW_DATA *spw);

	void RunWindow();

	/**@}*/

	/**
	 * \defgroup heglk
	 * @{
	 */

	/**
	 * If gotvalue is passed as -1, then no value has already been as the (potential) object, etc.
	 * comprising the first part of the object.property, for example, to be set.
	 */
	void RunSet(int gotvalue);

	unsigned int GetAnonymousFunction(long addr);

	int SetCompound(int t);

	/**@}*/

	/**
	* \defgroup Miscellaneous
	* @{
	*/

	int hugo_fseek(Common::SeekableReadStream *s, long int offset, int whence) {
		return !s->seek(offset, whence);
	}
	int hugo_fseek(strid_t s, long int offset, int whence) {
		Common::SeekableReadStream *rs = *s;
		return hugo_fseek(rs, offset, whence);
	}

	int hugo_fgetc(Common::SeekableReadStream *s) {
		return s->readByte();
	}
	int hugo_fgetc(strid_t s) {
		Common::SeekableReadStream *ws = *s;
		return hugo_fgetc(ws);
	}

	int hugo_fputc(int c, Common::WriteStream *s) {
		s->writeByte(c);
		return s->err() ? EOF : 0;
	}
	int hugo_fputc(int c, strid_t s) {
		Common::WriteStream *ws = *s;
		return hugo_fputc(c, ws);
	}

	char *hugo_fgets(char *buf, int max, Common::SeekableReadStream *s) {
		char *ptr = buf;
		char c;
		while (s->pos() < s->size() && --max > 0) {
			c = hugo_fgetc(s);
			if (c == '\n' || c == '\0')
				break;
			*ptr++ = c;
		}
		*ptr++ = '\0';
		return buf;
	}
	char *hugo_fgets(char *buf, int max, strid_t s) {
		Common::SeekableReadStream *rs = *s;
		return hugo_fgets(buf, max, rs);
	}

	size_t hugo_fread(void *ptr, size_t size, size_t count, Common::SeekableReadStream *s) {
		return s->read(ptr, size * count);
	}
	size_t hugo_fread(void *ptr, size_t size, size_t count, strid_t s) {
		Common::SeekableReadStream *rs = *s;
		return hugo_fread(ptr, size, count, rs);
	}

	int hugo_fprintf(Common::WriteStream *s, const char *fmt, ...) {
		va_list va;
		va_start(va, fmt);
		Common::String text = Common::String::vformat(fmt, va);
		va_end(va);

		s->write(text.c_str(), text.size());
		return s->err() ? -1 : 0;
	}
	int hugo_fprintf(strid_t s, const char *fmt, ...) {
		va_list va;
		va_start(va, fmt);
		Common::String text = Common::String::vformat(fmt, va);
		va_end(va);

		Common::WriteStream *str = *s;
		str->write(text.c_str(), text.size());
		return str->err() ? -1 : 0;
	}

	int hugo_fputs(const char *str, Common::WriteStream *s) {
		return s->write(str, strlen(str)) == strlen(str) ? 0 : -1;
	}
	int hugo_fputs(const char *str, strid_t s) {
		Common::WriteStream *ws = *s;
		return hugo_fputs(str, ws);
	}

	bool hugo_ferror(Common::SeekableReadStream *s) const {
		return s->err();
	}
	bool hugo_ferror(strid_t s) const {
		Common::SeekableReadStream *rs = *s;
		return hugo_ferror(rs);
	}

	long hugo_ftell(Common::SeekableReadStream *s) {
		return s->pos();
	}
	long hugo_ftell(strid_t s) {
		Common::SeekableReadStream *rs = *s;
		return hugo_ftell(rs);
	}

	int hugo_fclose(strid_t f) {
		delete f;
		return 0;
	}

	void hugo_exit(const char *msg) {
		error("%s", line);
	}

	uint hugo_rand() {
		return _random.getRandomNumber(0xffffff);
	}

	char *itoa(int value, char *str, int base, size_t str_size) {
		assert(base == 10);
		Common::sprintf_s(str, str_size, "%d", value);
		return str;
	}

	/**@}*/
private:
	/**
	 * Allocate memory block
	 */
	void *hugo_blockalloc(size_t num) { return malloc(num); }

	void hugo_blockfree(void *block) { free(block); }

#if defined (DEBUGGER)
	int CheckinRange(uint v1, uint v2, const char *v3) { return 1; }

	/**
	* Shorthand since many of these object functions may call CheckinRange() if the debugger
	* is running and runtime_warnings is set.
	*/
	int CheckObjectRange(int obj);

	void DebugRunRoutine(long addr) { RunRoutine(addr); }

	void RuntimeWarning(const char *msg) {}

	void DebugMessageBox(const char *title, const char *msg) {}

	bool IsBreakpoint(long loc) const { return false; }

	const char *RoutineName(long loc) { return "Routine"; }

	void AddStringtoCodeWindow(const char *str) {}

	void SwitchtoDebugger() {}

	void Debugger() {}

	void UpdateDebugScreen() {}

	void SwitchtoGame() {}

	void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }

	void AddLinetoCodeWindow(int lineNum) {}

	void RecoverLastGood() {}

	void SetupWatchEval(int num) {}

	bool EvalWatch() { return false; }
#endif
public:
	/**
	 * Constructor
	 */
	Hugo(OSystem *syst, const GlkGameDescription &gameDesc);

	/**
	 * Destructor
	 */
	~Hugo();

	/**
	 * Run the game
	 */
	void runGame() override;

	/**
	 * Returns the running interpreter type
	 */
	InterpreterType getInterpreterType() const override { return INTERPRETER_HUGO; }

	/**
	 * Load a savegame from the passed Quetzal file chunk stream
	 */
	Common::Error readSaveData(Common::SeekableReadStream *rs) override;

	/**
	 * Save the game. The passed write stream represents access to the UMem chunk
	 * in the Quetzal save file that will be created
	 */
	Common::Error writeGameData(Common::WriteStream *ws) override;
};

extern Hugo *g_vm;

} // End of namespace Hugo
} // End of namespace Glk

#endif
