/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2012  Warzone 2100 Project

	Warzone 2100 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.

	Warzone 2100 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 Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * Interp.h
 *
 * Script interpreter definitions
 */
#ifndef _interp_h
#define _interp_h

/* The type of function called by an OP_CALL */
typedef bool (*SCRIPT_FUNC)(void);

/* The type of function called to access an object or in-game variable */
typedef bool (*SCRIPT_VARFUNC)(UDWORD index);

/* The possible value types for scripts */
enum INTERP_TYPE
{
	// Basic types
	VAL_BOOL,
	VAL_INT,
	VAL_FLOAT,
	VAL_STRING,

	// events and triggers
	VAL_TRIGGER, //!< trigger index
	VAL_EVENT, //!< event (or in-script function)

	/* Type used by the compiler for functions that do not return a value */
	VAL_VOID,

	VAL_OPCODE, //!< Script opcode
	VAL_PKOPCODE, //!< Packed script opcode

	VAL_OBJ_GETSET, //!< Pointer to the object .set or .get function
	VAL_FUNC_EXTERN, //!< External function pointer

	VAL_USERTYPESTART, //!< user defined types should start with this id
	VAL_REF = 0x00100000 //!< flag to specify a variable reference rather than simple value
};


/* A value consists of its type and value */
struct INTERP_VAL
{
	INTERP_TYPE		type;					//Value type for interpreter; opcode or value type for compiler
	union
	{
		char					*sval;		//String value - VAL_STRING
		SCRIPT_VARFUNC		pObjGetSet;		//Set/Get method for objects - VAL_OBJ_GETSET
		SCRIPT_FUNC			pFuncExtern;	//External (C) function - VAL_FUNC_EXTERN
		void					*oval;		//Object value - any in-game object
		float					fval;		//Float value - VAL_FLOAT
		int						ival;		// Integer value - VAL_INT
		int32_t					bval;		//Boolean value - VAL_BOOL [NOTE: Even though this is a bool, we must make it this size to prevent issues]
	} v;
};


// maximum number of equivalent types for a type
#define INTERP_MAXEQUIV		10

// type equivalences
struct TYPE_EQUIV
{
	INTERP_TYPE		base;		// the type that the others are equivalent to
	unsigned int		numEquiv;	// number of equivalent types
	INTERP_TYPE		aEquivTypes[INTERP_MAXEQUIV]; // the equivalent types
};

/* Opcodes for the script interpreter */
enum OPCODE
{
	OP_PUSH,		// Push value onto stack
	OP_PUSHREF,		// Push a pointer to a variable onto the stack
	OP_POP,			// Pop value from stack

	OP_PUSHGLOBAL,	// Push the value of a global variable onto the stack
	OP_POPGLOBAL,	// Pop a value from the stack into a global variable

	OP_PUSHARRAYGLOBAL,	// Push the value of a global array variable onto the stack
	OP_POPARRAYGLOBAL,	// Pop a value from the stack into a global array variable

	OP_CALL,		// Call the 'C' function pointed to by the next value
	OP_VARCALL,		// Call the variable access 'C' function pointed to by the next value

	OP_JUMP,		// Jump to a different location in the script
	OP_JUMPTRUE,	// Jump if the top stack value is true
	OP_JUMPFALSE,	// Jump if the top stack value is false

	OP_BINARYOP,	// Call a binary maths/boolean operator
	OP_UNARYOP,		// Call a unary maths/boolean operator

	OP_EXIT,			// End the program
	OP_PAUSE,			// temporarily pause the current event

	// The following operations are secondary data to OP_BINARYOP and OP_UNARYOP

	// Maths operators
	OP_ADD,
	OP_SUB,
	OP_MUL,
	OP_DIV,
	OP_NEG,
	OP_INC,
	OP_DEC,

	// Boolean operators
	OP_AND,
	OP_OR,
	OP_NOT,

	//String concatenation
	OP_CONC,

	// Comparison operators
	OP_EQUAL,
	OP_NOTEQUAL,
	OP_GREATEREQUAL,
	OP_LESSEQUAL,
	OP_GREATER,
	OP_LESS,

	OP_FUNC,		//custom (in-script) function call
	OP_POPLOCAL,	//local var
	OP_PUSHLOCAL,

	OP_PUSHLOCALREF,	//variable of object type (pointer)
	OP_TO_FLOAT,			//float cast
	OP_TO_INT,				//int cast
};

/* How far the opcode is shifted up a UDWORD to allow other data to be
 * stored in the same UDWORD
 */
#define OPCODE_SHIFT		24
#define OPCODE_DATAMASK		0x00ffffff

// maximum sizes for arrays
#define VAR_MAX_DIMENSIONS		4
#define VAR_MAX_ELEMENTS		UBYTE_MAX

/* The mask for the number of array elements stored in the data part of an opcode */
#define ARRAY_BASE_MASK			0x000fffff
#define ARRAY_DIMENSION_SHIFT	20
#define ARRAY_DIMENSION_MASK	0x00f00000

/* The possible storage types for a variable */
enum enum_STORAGE_TYPE
{
	ST_PUBLIC,		// Public variable
	ST_PRIVATE,		// Private variable
	ST_OBJECT,		// A value stored in an objects data space.
	ST_EXTERN,		// An external value accessed by function call
	ST_LOCAL,		// A local variable
};

typedef UBYTE STORAGE_TYPE;

/* Variable debugging info for a script */
struct VAR_DEBUG
{
	char			*pIdent;
	STORAGE_TYPE	storage;
};

/* Array info for a script */
struct ARRAY_DATA
{
	UDWORD			base;			// the base index of the array values
	INTERP_TYPE		type;			// the array data type
	UBYTE			dimensions;
	UBYTE			elements[VAR_MAX_DIMENSIONS];
};

/* Array debug info for a script */
struct ARRAY_DEBUG
{
	char			*pIdent;
	UBYTE			storage;
};

/* Line debugging information for a script */
struct SCRIPT_DEBUG
{
	UDWORD	offset;		// Offset in the compiled script that corresponds to
	UDWORD	line;		// this line in the original script.
	char	*pLabel;	// the trigger/event that starts at this line
};

/* Different types of triggers */
enum TRIGGER_TYPE
{
	TR_INIT,		// Trigger fires when the script is first run
	TR_CODE,		// Trigger uses script code
	TR_WAIT,		// Trigger after a time pause
	TR_EVERY,		// Trigger at repeated intervals
	TR_PAUSE,		// Event has paused for an interval and will restart in the middle of it's code

	TR_CALLBACKSTART,	// The user defined callback triggers should start with this id
};

/* Description of a trigger for the SCRIPT_CODE */
struct TRIGGER_DATA
{
	TRIGGER_TYPE		type;		// Type of trigger
	UWORD			code;		// bool - is there code with this trigger
	UDWORD			time;		// How often to check the trigger
};

/* A compiled script and its associated data */
struct SCRIPT_CODE
{
	UDWORD			size;			// The size (in bytes) of the compiled code
	INTERP_VAL		*pCode;			// Pointer to the compiled code

	UWORD			numTriggers;	// The number of triggers
	UWORD			numEvents;		// The number of events
	UWORD			*pTriggerTab;	// The table of trigger offsets
	TRIGGER_DATA	*psTriggerData;	// The extra info for each trigger
	UWORD			*pEventTab;		// The table of event offsets
	SWORD			*pEventLinks;	// The original trigger/event linkage
									// -1 for no link

	UWORD			numGlobals;		// The number of global variables
	UWORD			numArrays;		// the number of arrays in the program
	UDWORD			arraySize;		// the number of elements in all the defined arrays
	INTERP_TYPE		*pGlobals;		// Types of the global variables


	INTERP_TYPE		**ppsLocalVars;		//storage for local vars (type)
	UDWORD			*numLocalVars;		//number of local vars each event has
	INTERP_VAL		**ppsLocalVarVal;	//Values of the local vars used during interpreting process
	UDWORD			*numParams;			//number of arguments this event has


	VAR_DEBUG		*psVarDebug;	// The names and storage types of variables
	ARRAY_DATA		*psArrayInfo;	// The sizes of the program arrays
	ARRAY_DEBUG		*psArrayDebug;	// Debug info for the arrays

	UWORD			debugEntries;	// Number of entries in psDebug
	SCRIPT_DEBUG	*psDebug;		// Debugging info for the script
};


/* What type of code should be run by the interpreter */
enum INTERP_RUNTYPE
{
	IRT_TRIGGER,					// Run trigger code
	IRT_EVENT,						// Run event code
};


/* The size of each opcode */
extern SDWORD aOpSize[];

/* Check if two types are equivalent */
extern bool interpCheckEquiv(INTERP_TYPE to, INTERP_TYPE from);

// Initialise the interpreter
extern bool interpInitialise(void);

// true if the interpreter is currently running
extern bool interpProcessorActive(void);

/* Output script call stack trace */
extern void scrOutputCallTrace(code_part part);

#endif
