#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;
struct FunctionInfo;
struct FunctionContext;
class FunctionCaller;

#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,
	kOBSEParamType_Pair	=	1 << kTokenType_Pair,
};

// 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;	}
};

#define OBSE_EXPR_MAX_ARGS 20		// max # of args we'll accept to a commmand

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

	enum { kMaxArgs = OBSE_EXPR_MAX_ARGS };

	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();
};

bool BasicTokenToElem(ScriptToken* token, ArrayElement& elem, ExpressionEvaluator* context);

class ExpressionParser
{
	enum { kMaxArgs = OBSE_EXPR_MAX_ARGS };

	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,
		kError_ExpectedStringLiteral,

		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(Operator* curOp = NULL);
	ScriptToken *	PeekOperand(UInt32& outReadLen);
	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();
	ScriptToken	*	ParseOperand(bool (* pred)(ScriptToken* operand));
	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[];

