#pragma once

/*
	Expressions are evaluated according to a set of rules stored in lookup tables. Each type of operand can 
	be resolved to zero or more	"lower" types as defined by ConversionRule. Operators use OperationRules based
	on the types of each operand to	determine the result type. Types can be ambiguous at compile-time (array 
	elements, command results) but always resolve to a concrete type at run-time. Rules are applied in the order 
	they are defined until the first one matching the operands is encountered. At run-time this means the routines
	which perform the operations can know that the operands are of the expected type.
*/

struct Operator;
struct ScriptEventList;
class ExpressionEvaluator;
struct UserFunctionParam;

#include "ScriptTokens.h"
#include <stack>

#if OBLIVION
#include <cstdarg>
#endif

extern ErrOutput g_ErrOut;

// these are used in ParamInfo to specify expected Token_Type of args to commands taking OBSE expressions as args
enum {
	kOBSEParamType_Number =		(1 << kTokenType_Number) | (1 << kTokenType_Ambiguous),
	kOBSEParamType_Boolean =	(1 << kTokenType_Boolean) | (1 << kTokenType_Ambiguous),
	kOBSEParamType_String =		(1 << kTokenType_String) | (1 << kTokenType_Ambiguous),
	kOBSEParamType_Form =		(1 << kTokenType_Form) | (1 << kTokenType_Ambiguous),
	kOBSEParamType_Array =		(1 << kTokenType_Array) | (1 << kTokenType_Ambiguous),
	kOBSEParamType_ArrayElement = 1 << (kTokenType_ArrayElement) | (1 << kTokenType_Ambiguous),
	kOBSEParamType_Slice =		1 << kTokenType_Slice,
	kOBSEParamType_Command =	1 << kTokenType_Command,
	kOBSEParamType_Variable =	1 << kTokenType_Variable,
	kOBSEParamType_NumericVar =	1 << kTokenType_NumericVar,
	kOBSEParamType_RefVar =		1 << kTokenType_RefVar,
	kOBSEParamType_StringVar =	1 << kTokenType_StringVar,
	kOBSEParamType_ArrayVar =	1 << kTokenType_ArrayVar,
	kOBSEParamType_ForEachContext = 1 << kTokenType_ForEachContext,
	
	kOBSEParamType_Collection = kOBSEParamType_Array | kOBSEParamType_String,
	kOBSEParamType_ArrayVarOrElement = kOBSEParamType_ArrayVar | kOBSEParamType_ArrayElement,
	kOBSEParamType_ArrayIndex = kOBSEParamType_String | kOBSEParamType_Number,
	kOBSEParamType_BasicType = kOBSEParamType_Array | kOBSEParamType_String | kOBSEParamType_Number | kOBSEParamType_Form,
	kOBSEParamType_NoTypeCheck = 0,

	kOBSEParamType_FormOrNumber = kOBSEParamType_Form | kOBSEParamType_Number,
	kOBSEParamType_StringOrNumber = kOBSEParamType_String | kOBSEParamType_Number,
};

// wraps a dynamic ParamInfo array
struct DynamicParamInfo
{
private:
	static const UInt32 kMaxParams = 10;

	ParamInfo	m_paramInfo[kMaxParams];
	UInt32		m_numParams;

public:
	DynamicParamInfo(std::vector<UserFunctionParam> &params);
	DynamicParamInfo() : m_numParams(0) { }

	ParamInfo* Params()	{	return m_paramInfo;	}
	UInt32 NumParams()	{ return m_numParams;	}
};

class ExpressionEvaluator
{
	/*  ExpressionEvaluator objects should always be created on the stack.
		Thread takes ownership on creation, releases it on destruction. */

	enum { kMaxArgs = 10 };

	enum {
		kFlag_SuppressErrorMessages	= 1 << 0,
		kFlag_ErrorOccurred			= 1 << 1,
		kFlag_StackTraceOnError		= 1 << 2,
	};

	static SInt32			 s_numInstances;
	static Bitfield<UInt32>	 s_flags;
	static CommandReturnType s_expectedReturnType;
	static std::stack<const ExpressionEvaluator*>	s_expressionStack;

	UInt8			* m_scriptData;
	UInt32			* m_opcodeOffsetPtr;
	double			* m_result;
	TESObjectREFR	* m_thisObj;
	TESObjectREFR	* m_containingObj;
	UInt8			* m_data;
	ScriptToken		* m_args[kMaxArgs];
	ParamInfo		* m_params;
	UInt8			m_numArgsExtracted;
	UInt16			m_baseOffset;

	UInt8*			&Data()	{ return m_data;	}
	CommandReturnType GetExpectedReturnType() { CommandReturnType type = s_expectedReturnType; s_expectedReturnType = kRetnType_Default; return type; }
public:
	static bool	Active()	{	return s_numInstances != 0;	}
	static void ToggleErrorSuppression(bool bDisable);
	static void	ExpectReturnType(CommandReturnType type) { s_expectedReturnType = type; }
	static void	PrintStackTrace();

	ExpressionEvaluator(COMMAND_ARGS);
	~ExpressionEvaluator();

	Script			* script;
	ScriptEventList	* eventList;

	void			Error(const char* fmt, ...);
	bool			HasErrors() { return s_flags.IsSet(kFlag_ErrorOccurred); }
	bool			ExtractArgs();
	ScriptToken*	Evaluate();			// evaluates a single argument/token

	ScriptToken*	Arg(UInt32 idx) { return idx < kMaxArgs ? m_args[idx] : NULL; }
	UInt8			NumArgs() { return m_numArgsExtracted; }
	void			SetParams(ParamInfo* newParams)	{	m_params = newParams;	}

	TESObjectREFR*	ThisObj() { return m_thisObj; }
	TESObjectREFR*	ContainingObj() { return m_containingObj; }

	UInt8		ReadByte();
	UInt16		Read16();
	double		ReadFloat();
	std::string	ReadString();
	SInt8		ReadSignedByte();
	SInt16		ReadSigned16();
	UInt32		Read32();
	SInt32		ReadSigned32();
};

// controls user function calls.
// Manages a stack of function contexts
// Function args in Call bytecode. FunctionInfo encoded in Begin Function data. Return value from SetFunctionValue.
class UserFunctionManager
{
	struct FunctionContext;

	static UserFunctionManager	* s_singleton;
	static UserFunctionManager	* GetSingleton();

	UserFunctionManager();
	~UserFunctionManager();

	// stores info about function script (params, etc). generated once per function script and cached
	struct FunctionInfo
	{
	private:
		DynamicParamInfo	m_dParamInfo;
		std::vector<UserFunctionParam> m_userFunctionParams;
		Script				* m_script;			// function script
		UInt16				* m_destructibles;	// dynamic array of var indexes of local array vars to be destroyed on function return
		UInt8				m_numDestructibles;
		UInt8				m_functionVersion;	// bytecode version of Function statement
		bool				m_bad;				// if true don't execute function; set if errors occur during function info extraction

	public:
		FunctionInfo(Script* script);
		~FunctionInfo();

		FunctionContext	* CreateContext(UInt8 version);
		bool IsGood() { return !m_bad; }
		Script* GetScript() { return m_script; }
		ParamInfo* Params() { return m_dParamInfo.Params(); }
		UserFunctionParam* GetParam(UInt32 paramIndex);
		bool CleanEventList(ScriptEventList* eventList);
	};

	// represents a function executing on the stack
	struct FunctionContext
	{
	private:
		FunctionInfo	* m_info;
		ScriptEventList	* m_eventList;		// temporary eventlist generated for function script
		ScriptToken		* m_result;
		UInt8			m_callerVersion;
		bool			m_bad;
	public:
		FunctionContext(FunctionInfo* info, UInt8 version);
		~FunctionContext();

		bool Execute(ExpressionEvaluator* eval);
		bool Return(ExpressionEvaluator* eval);
		bool IsGood() { return !m_bad; }
		ScriptToken*  Result() { return m_result; }
		FunctionInfo* Info() { return m_info; }
	};

	static const UInt32	kMaxNestDepth = 30;	// arbitrarily low; have seen 180+ nested calls execute w/o problems
	
	UInt32								m_nestDepth;
	std::stack<FunctionContext*>		m_functionStack;
	std::map<Script*, FunctionInfo*>	m_functionInfos;

	// these take a ptr to the function script to check that it matches executing script
	FunctionContext* Top(Script* funcScript);
	bool Pop(Script* funcScript);
	void Push(FunctionContext* context) { m_functionStack.push(context); }
	FunctionInfo* GetFunctionInfo(Script* funcScript);

public:
	static ScriptToken* Call(ExpressionEvaluator* eval);
	static bool	Return(ExpressionEvaluator* eval);
	static bool Enter(Script* funcScript);
};

class ExpressionParser
{
	enum { kMaxArgs = 10 };

	ScriptBuffer		* m_scriptBuf;
	ScriptLineBuffer	* m_lineBuf;
	UInt32				m_len;
	Token_Type			m_argTypes[kMaxArgs];
	UInt8				m_numArgsParsed;

	enum {								// varargs
		kError_CantParse,
		kError_TooManyOperators,
		kError_TooManyOperands,
		kError_MismatchedBrackets,
		kError_InvalidOperands,			// string:operator
		kError_MismatchedQuotes,
		kError_InvalidDotSyntax,
		kError_CantFindVariable,		// string:varName
		kError_ExpectedStringVariable,
		kError_UnscriptedObject,		// string:objName
		kError_TooManyArgs,	
		kError_RefRequired,				// string:commandName
		kError_MissingParam,			// string:paramName, int:paramIndex
		kError_UserFuncMissingArgList,	// string:userFunctionName
		kError_ExpectedUserFunction,
		kError_UserFunctionContainsMultipleBlocks,
		kError_UserFunctionVarsMustPrecedeDefinition,
		kError_UserFunctionParamsUndefined,

		kWarning_UnquotedString,		// string:unquotedString
		kWarning_FunctionPointer,

		kError_Max
	};

	static ErrOutput::Message	* s_Messages;

	char	Peek(UInt32 idx = -1) {
		if (idx == -1)	idx = m_lineBuf->lineOffset;
		return (idx < m_len) ? m_lineBuf->paramText[idx] : 0;
	}
	UInt32&	Offset()	{ return m_lineBuf->lineOffset; }
	const char * Text()	{ return m_lineBuf->paramText; }
	const char * CurText() { return Text() + Offset(); }

	void	Message(UInt32 errorCode, ...);

	Token_Type		Parse();
	Token_Type		ParseSubExpression(UInt32 exprLen);
	Operator *		ParseOperator(bool bExpectBinaryOperator);
	ScriptToken	*	ParseOperand();
	bool			ParseFunctionCall(CommandInfo* cmdInfo);
	Token_Type		PopOperator(std::stack<Operator*> & ops, std::stack<Token_Type> & operands);

	UInt32	MatchOpenBracket(Operator* openBracOp);
	std::string GetCurToken();
	Script::VariableInfo* LookupVariable(const char* varName, Script::RefVariable* refVar = NULL);

public:
	ExpressionParser(ScriptBuffer* scriptBuf, ScriptLineBuffer* lineBuf) : m_scriptBuf(scriptBuf),
		m_lineBuf(lineBuf), m_len(strlen(m_lineBuf->paramText)), m_numArgsParsed(0)
		{ memset(m_argTypes, kTokenType_Invalid, sizeof(m_argTypes)); }

	bool			ParseArgs(ParamInfo* params, UInt32 numParams);
	bool			ParseUserFunctionCall();
	bool			ParseUserFunctionDefinition();
	Token_Type		ArgType(UInt32 idx) { return idx < kMaxArgs ? m_argTypes[idx] : kTokenType_Invalid; }
};

void ShowRuntimeError(Script* script, const char* fmt, ...);
bool PrecompileScript(ScriptBuffer* buf);

// OBSE analogue for Cmd_Default_Parse, accepts expressions as args
bool Cmd_Expression_Parse(UInt32 numParams, ParamInfo* paramInfo, ScriptLineBuffer* lineBuf, ScriptBuffer* scriptBuf);

extern Operator s_operators[];

