#pragma once

#include <vector>
#include <string>

class IDataStream;

std::string ReadString2(IDataStream * src);
std::string ReadString4(IDataStream * src);

struct StringTable
{
	bool	Read(IDataStream * src);
	void	Dump(void);

	const std::string &	Get(UInt32 idx);
	bool HasData(UInt32 idx);

	typedef std::vector <std::string>	Storage;
	Storage	m_data;
};

struct DebugInfo
{
	struct Info
	{
		UInt16	objName;
		UInt16	stateName;
		UInt16	fnName;
		UInt8	type;	// 0-3 valid

		std::vector <UInt16>	lineNumbers;	// one per instruction

		UInt32	GetNumInstructions()	{ return lineNumbers.size(); }
	};

	struct PropertyGroup
	{
		UInt16	objName;
		UInt16	groupName;
		UInt16	groupDoc;
		UInt32	userFlags;

		std::vector <UInt16>	names;

		UInt32	GetNumNames()	{ return names.size(); }
	};

	struct StructOrder
	{
		UInt16	objName;
		UInt16	orderName;

		std::vector <UInt16>	names;

		UInt32	GetNumNames()	{ return names.size(); }
	};

	UInt8	m_flags;
	time_t	m_sourceModificationTime;

	typedef std::vector <Info>	Storage;
	Storage	m_data;

	typedef std::vector <PropertyGroup>	PropertyGroupStorage;
	PropertyGroupStorage m_propGroup;

	typedef std::vector <StructOrder> StructOrderStorage;
	StructOrderStorage m_structOrder;

	bool	Read(IDataStream * src);
	bool	Read_Functions(IDataStream * src);
	bool	Read_PropertyGroups(IDataStream * src);
	bool	Read_StructOrder(IDataStream * src);
	void	Dump(StringTable * strTable);
};

struct UserFlagTable
{
	struct Info
	{
		UInt16	name;
		UInt8	idx;
	};

	typedef std::vector <Info>	Storage;
	Storage	m_data;

	bool	Read(IDataStream * src);
	void	Dump(StringTable * strTable);
};

struct VarValue
{
	enum
	{
		kType_Unk0 = 0,		// 0 null?
		kType_Identifier,	// 1 identifier
		kType_String,		// 2
		kType_Integer,		// 3
		kType_Float,		// 4
		kType_Bool,			// 5
	};

	UInt8	type;

	union
	{
		UInt16	stringRef;
		UInt16	stringRef2;
		SInt32	i;
		float	f;
		bool	b;
	} data;

	bool	Read(IDataStream * src);
	void	Dump(StringTable * strTable);
};

struct FunctionCode
{
	enum
	{
		kOp_Nop = 0,				// 0	0.6
		kOp_IAdd,
		kOp_FAdd,
		kOp_ISubtract,
		kOp_FSubtract,				// 4
		kOp_IMultiply,
		kOp_FMultiply,
		kOp_IDivide,
		kOp_FDivide,				// 8
		kOp_IMod,
		kOp_Not,
		kOp_INegate,
		kOp_FNegate,				// C
		kOp_Assign,
		kOp_Cast,
		kOp_CompareEQ,
		kOp_CompareLT,				// 10
		kOp_CompareLTE,
		kOp_CompareGT,
		kOp_CompareGTE,
		kOp_Jump,					// 14
		kOp_JumpT,
		kOp_JumpF,
		kOp_CallMethod,
		kOp_CallParent,				// 18
		kOp_CallStatic,
		kOp_Return,
		kOp_Strcat,					//		1.0
		kOp_PropGet,				// 1C	3.0
		kOp_PropSet,
		kOp_ArrayCreate,			//		3.1
		kOp_ArrayLength,
		kOp_ArrayGetElement,		// 20
		kOp_ArraySetElement,
		kOp_ArrayFindElement,		//		3.2	fallout4
		kOp_ArrayRFindElement,
		kOp_Is,						// 24	3.5
		kOp_StructCreate,			//		3.7
		kOp_StructGet,
		kOp_StructSet,
		kOp_ArrayFindStruct,		// 28	3.9
		kOp_ArrayRFindStruct,
		kOp_ArrayAddElements,
		kOp_ArrayInsertElement,
		kOp_ArrayRemoveLastElement,	// 2C
		kOp_ArrayRemoveElements,
		kOp_ArrayClearElements,

		kOp_Invalid,
	};

	struct Instruction
	{
		UInt8		op;

		typedef std::vector<VarValue>	VarTable;
		VarTable	args;
	};

	typedef std::vector <Instruction>	InstructionList;
	InstructionList	instructions;

	bool IsEmpty(void);

	static const char * GetOpName(UInt32 op);
	static const char * GetOpArgDesc(UInt32 op);

	bool	Read(IDataStream * src, UInt16 codeLen);
	void	Dump(StringTable * strTable);
};

struct FunctionInfo
{
	bool	valid;

	struct ParamInfo
	{
		UInt16	name;
		UInt16	type;

		bool	Read(IDataStream * src);
		void	Dump(StringTable * strTable);
	};

	UInt16	returnType;
	UInt16	docstring;
	UInt32	userFlags;
	UInt8	flags;

	typedef std::vector <ParamInfo>	ParamTable;
	ParamTable	params;
	ParamTable	locals;

	FunctionCode	code;

	bool	Read(IDataStream * src);
	void	Dump(StringTable * strTable);

	void	MarkInvalid(void);
};

struct ObjectTable
{
	struct Info
	{
		struct StructInfo
		{
			struct Member
			{
				UInt16		name;
				UInt16		typeName;
				UInt32		userFlags;
				VarValue	value;
				UInt8		constFlag;
				UInt16		docString;
			};

			UInt16	name;

			typedef std::vector <Member> MemberTable;
			MemberTable	members;
		};

		struct VarInfo
		{
			UInt16		name;
			UInt16		typeName;
			UInt32		userFlags;
			VarValue	value;
			UInt8		constFlag;
		};

		struct PropInfo
		{
			enum
			{
				kFlags_Read =		1 << 0,
				kFlags_Write =		1 << 1,
				kFlags_AutoVar =	1 << 2,
			};

			UInt16	name;
			UInt16	type;
			UInt16	docstring;
			UInt32	userFlags;
			UInt8	flags;	// 1 and 2 are read/write
			UInt16	autoVarName;

			FunctionInfo	readHandler;
			FunctionInfo	writeHandler;
		};

		struct StateInfo
		{
			struct StateFunction
			{
				UInt16			name;
				FunctionInfo	function;
			};

			UInt16	name;

			typedef std::vector <StateFunction>	FnTable;
			FnTable	functions;

			bool	Read(IDataStream * src);
			void	Dump(StringTable * strTable);
		};

		UInt16	name;

		UInt16	parentClassName;
		UInt16	docstring;
		UInt8	constFlag;
		UInt32	userFlags;
		UInt16	autoStateName;

		typedef std::vector <StructInfo>	StructTable;
		StructTable	structs;

		typedef std::vector <VarInfo>	VarTable;
		VarTable	variables;

		typedef std::vector <PropInfo>	PropTable;
		PropTable	properties;

		typedef std::vector <StateInfo>	StateTable;
		StateTable	states;
	};

	typedef std::vector <Info>	Storage;
	Storage	m_data;

	bool	Read(IDataStream * src);
	void	Dump(StringTable * strTable);
};

struct ScriptHeader
{
	enum
	{
		kSignature = 0xFA57C0DE,
		kVerMajor = 0x03,
		kVerMinor = 0x09,	// skyrim was 3.1

		kGameID_Skyrim = 0x0001,
		kGameID_Fallout4 = 0x0002,
	};

	UInt32	signature;	// 00	FA57C0DE
	UInt8	verMajor;	// 04	03
	UInt8	verMinor;	// 05	01
	UInt16	gameID;		// 06	0001
	time_t	buildTime;	// 08	time_t

	void	Swap(void);
	void	Dump(void);

	bool	Validate(void);
};

struct VMScript
{
	ScriptHeader	header;
	StringTable		stringTable;
	DebugInfo		debugInfo;
	UserFlagTable	userFlagTable;
	ObjectTable		objectTable;

	std::string	source;
	std::string	user;
	std::string	machine;

	bool 	Read(IDataStream * src);
	void	Dump(void);
};
