/* maximum sizes */
#define BUFLEN				1024*1024				// 1 MiB, initial read buffer size
#define MAXCONTAINERS	2048

/* states */
#define XMIINS_READ_HEADER				1
#define XMIINS_READ_RUN_HEADER		2
#define XMIINS_READ_GLOBAL_BLOCK		3
#define XMIINS_READ_CONTAINER_BLOCK	4

/* error codes */
#define XMIINS_ERR_NONE						0
#define XMIINS_ERR_EXCEPTION				1		// C++ exception raised (XMill or otherwise)
#define XMIINS_ERR_GLOBALSIZE				2		// large global block size mismatch
#define XMIINS_ERR_CONTAINERSIZE			3		// large container block size mismatch
#define XMIINS_ERR_BUFFERTOOSMALL		4		// internal error: read buffer too small
#define XMIINS_ERR_TOOMANYCONTAINERS	5		// internal error: MAXCONTAINERS too small
#define XMIINS_ERR_GLOBALNOTFOUND		6		// large global block not found
#define XMIINS_ERR_CONTAINERNOTFOUND	7		// large container block not found
#define XMIINS_ERR_STATE					8		// internal error: illegal state
#define XMIINS_ERR_XMLFILE					9		// XML file, not XMI
#define XMIINS_ERR_MAGIC					10		// incorrect XMill magic
#define XMIINS_ERR_VERSION					11		// unknown XMill version
#define XMIINS_ERR_TOTALSIZE				12		// sizes mismatch
#define XMIINS_ERR_GLOBALCONTAINER		13		// the special container block 0 has more than 3 containers
#define XMIINS_ERR_ZERO						14		// no Bytes decompressed
#define XMIINS_ERR_UNKNOWNLABEL			15		// label id out of range
#define XMIINS_ERR_DATA_INDEX_UNKNOWN	16		// data container index out of range
#define XMIINS_ERR_CONTAINER_INDEX_UNKNOWN	17
															// container index out of range
#define XMIINS_ERR_NUM_CONTAINERS_MISMATCH	18
															// # containers does not match the # that the associated path expression expects
#define XMIINS_ERR_OPENCLOSE_MISMATCH	19		// open and close XML tags don't match
#define XMIINS_ERR_SPECIALREF				20		// the special container is referenced for normal data
#define XMIINS_ERR_FILENOTFOUND			21		// XMI file could not be opened
#define XMIINS_ERR_XMISIZEMISMATCH		22		// XMI file size is not equal to processed size
#define XMIINS_ERR_SEMCOMPUNKNOWN		23		// semantic compressor unknown
#define XMIINS_ERR_PATHEXPR_PARSE		24    // path expression parse error
#define XMIINS_ERR_INTERNAL				25		// internal error aka detected a bug
#define XMIINS_ERR_DATASIZE				26		// wrong container data size
#define XMIINS_ERR_OR						27		// 'or' compressor choice value out of range
#define XMIINS_ERR_E							28		// 'e' compressor dictionary index out of range
//#define XMIINS_ERR_

/* reporting levels */
#define XMIINS_REPORT_NONE			0
#define XMIINS_REPORT_ERRORS		1
#define XMIINS_REPORT_WARNINGS	2
#define XMIINS_REPORT_MININFO		3
#define XMIINS_REPORT_SOMEINFO	4
#define XMIINS_REPORT_INFO			5
#define XMIINS_REPORT_MOREINFO	6
#define XMIINS_REPORT_CONTENTS	7
#define XMIINS_REPORT_ALL			8
#define XMIINS_REPORT_DEBUG		9
#define XMIINS_REPORT_VERYMUCH	10			// kludge. never use, please!

/* semantic compressor types */
#define XMIINS_SC_UNKNOWN			-1
/* basic */
#define XMIINS_SC_T					0			// text
#define XMIINS_SC_E					1			// dictionary
#define XMIINS_SC_U					2			// uint32
#define XMIINS_SC_I					3			// sint32
#define XMIINS_SC_RL					4			// runlength
#define XMIINS_SC_U8					5			// byte
#define XMIINS_SC_DI					11			// delta sint32
/* basic, added for v0.8beta */
#define XMIINS_SC_BASEX				6			// baseX
#define XMIINS_SC_BASE2				7			// binary
#define XMIINS_SC_BASE16			8			// hex
#define XMIINS_SC_BASE22			9			// case-sensitive hex
#define XMIINS_SC_BASE64			10			// base64
/* non-data */
#define XMIINS_SC_C					16			// constant
#define XMIINS_SC_P					17			// print
/* combining */
#define XMIINS_SC_OR					12			// or
#define XMIINS_SC_SEQ				13			// sequence
#define XMIINS_SC_REP				14			// repeat
#define XMIINS_SC_SEQCOMB			15			// combined sequence
/* mixed subtype for seqcomb */
#define XMIINS_SC_MIXED 			18			// mixed

#define XMIINS_SC_T_STR				"t"
#define XMIINS_SC_E_STR				"e"
#define XMIINS_SC_U_STR				"u"
#define XMIINS_SC_I_STR				"i"
#define XMIINS_SC_RL_STR			"rl"
#define XMIINS_SC_U8_STR			"u8"
#define XMIINS_SC_DI_STR			"di"
#define XMIINS_SC_BASEX_STR		"basex"
#define XMIINS_SC_BASE2_STR		"b"
#define XMIINS_SC_BASE16_STR		"x"
#define XMIINS_SC_BASE22_STR		"X"
#define XMIINS_SC_BASE64_STR		"base64"
#define XMIINS_SC_P_STR				"p"
#define XMIINS_SC_OR_STR			"or"
#define XMIINS_SC_SEQ_STR			"seq"
#define XMIINS_SC_REP_STR			"rep"
#define XMIINS_SC_SEQCOMB_STR		"seqcomb"

#define MAXSUBS						1024

class XMIInspect;

/* I intended this to be a tree, but I ended up only using the subs attribute in the top element.. 
 */
class ExprInfo
{
	int numcontainers;
	int containertype;
	ExprInfo **subs;
	int numsubs, usedsubs;
	int eindex, eindextotal;
	char *constant_or_param;

public:
	ExprInfo();
	~ExprInfo();

	ExprInfo** createSubs();
	ExprInfo** getSubs(int offset = 0);

	void setCorP(char *s, int len = 0, bool isconstant = false);
	char* getCorP();

	int getUsedSubs();
	int getUsedSubs(int subnum);
	void setUsedSubs(int num);

   ExprInfo* getTranslatedExpr(int contnum, int *subnump);
	int getTranslatedUsedSubs(int contnum);
	int getTranslatedContainers(int contnum, ExprInfo **exprs, int maxexprs);

	void calcEnumIndexes(XMIInspect *inspect, int pathexprnum, int &cureindex);
	int getEnumIndex(int blocknum, int contnum, int numprec);

	ExprInfo* getSubContainer(int sub);

	void setNumContainers(int numcontainers, bool takemax = false);
   int getNumContainers();

	int getSubContainerType(int container);
	void setSubContainerType(int container, int type);

	int getContainerType();
	void setContainerType (int type);

	int getTranslatedContainerType(int contnum);

	static int token2type(char *dum);
	static char* type2text(int type);
	static bool isCombining(int type);
	static int numContainers(int type);
};

class XMIInspect
{
	friend class ExprInfo;

	int state;
	int level;

	/* global header data, data sizes */
	long version, magic, datasize, numblocks, numglobals, curglobal, curcontainer, curblock, indexnr, numbuffers,
		compsize, totalcompsize, totaluncompsize, totalrunsize, runsize,
		headersize, totalheadersize, numheaders, totaldatasize, globalsfound, numlargeglobals,
		bytesleft, smallglobalsize, globalheadersize, preheadersize, globaldatasize, xmifilesize;
	char *gpc;
	/* arrays to store global & run header data */
	long *statesize, *numcontainers, *containersize, *index, *pathindex;// *dictionarysizes;
	char **labels, **exprs;
	int numlabels, numexprs;
	/* # containers */
	int expectedcontainers, foundcontainers;
	/* path expression info */
	ExprInfo **exprinfo;
	/* XML element & data counts */
	int numopen, numclose, numdata, numspecial, *numcontdata, numwhite, *numrealcontdata;

	/* uncompress buffer */
	unsigned char *buffer, *curptr;
	unsigned long buflen, len;
	bool realloc;

	/* private methods */
	char* printlstring(unsigned char *&curptr, bool sign = false, int level = XMIINS_REPORT_CONTENTS);
	void printslist(unsigned char *&curptr, bool sign = false, char ***array = NULL, int *numstrings = NULL, bool extendlist = false);

	int guessGPC(Input *input, char *&gpc, char &gpctype, unsigned char &idx);

	int readGlobalHeader();
	int readRunHeader();

	int readSmallGlobalBlock();
	int readLargeGlobalBlock();

	int readContainerInfo();
	int readSmallContainerBlock();
	int readLargeContainerBlock();
	int inspectContainerBlock(int block, int container, bool issmall = false);
	int processContainerData(int block, int container, int size, int type, int subnum, int eindex, int *count);

	int buildExprInfo(ExprInfo *info, char *expr, ExprInfo **infos, int *numsubsused, 
						   int *charsused = NULL, int *numcont = NULL, bool overlayed = false);
	int buildExprInfo(char **exprs, int numexprs);
	int matchEnumIndexes();

	int numReferringBlocks(int pathexprnum);
	int numPrecedingBlocks(int blocknum, int pathexprnum);

	int endThisRun();
	int endReport(Input *input);

	void newBuffer(long size);

public:
	/* (de)constructors */
	XMIInspect(int level);
	~XMIInspect();

	/* internal data reset */
	void reset();

	/* main method */
	int inspectFile(char *xmifile);
};
