#ifndef _MPFS2dynVar_
#define _MPFS2dynVar_
/*<<<NL>>>*/

#include <xbPtr.h>

//Exception xbEapp: an exception which can be used by final applications
xbEdclStart(eMpfs2,xbEadv,{B42BCF89-5D31-4705-9150-A13BCDF9AF92},xbNOEXPORT)
	virtual xbMBiconType GetMBoxIcon() const {return xbMBicon_Stop;}
	virtual const char *GetMBoxTitle() const {return "MPFS2 error";} 
xbEdclEnd(eMpfs2);

enum MPFSvarType
{
	MPFSvarType_ForSubst,
	MPFSvarType_ForQueryIf,
	MPFSvarType_ForQueryWhile,
	MPFSvarType_ForContentType
};

class FileLoc;
interface IDynamicVariableParserUI
{
	virtual void infoLog(const xbString &log) = 0;
	virtual void dynaVarStart(bool bHttpPrintIdxLoaded) = 0;
	virtual bool confirmDynaVarAdd(const xbString &varName, MPFSvarType varType,const FileLoc &fileLoc) = 0;
	virtual bool confirmDynaVarDel(const xbString &varName, MPFSvarType varType) = 0;
	virtual uint32 getTabSize() const = 0;
	virtual xbString HTTPPrint_getHeaderAdd() const = 0;
	virtual xbString HTTPPrint_TokenArgType_Number() const = 0;
	virtual xbString HTTPPrint_TokenArgType_String() const = 0;
};

enum MPFStokenVersion
{
	MPFStokenVersion_2,
	MPFStokenVersion_2m,
};

class FileLoc
{
public:
	FileLoc(const xbString &fname,uint32 line = 1,uint32 col = 1)
		:m_fname(fname),m_line(line),m_col(col)
	{
	}
	FileLoc()
		:m_line(0),m_col(0)
	{
	}
	FileLoc(const FileLoc &floc)
		:m_fname(floc.m_fname),m_line(floc.m_line),m_col(floc.m_col)
	{
	}
	const FileLoc& operator=(const FileLoc &floc)
	{
		m_fname = floc.m_fname;
		m_line = floc.m_line;
		m_col = floc.m_col;
		return *this;
	}


	xbString m_fname;
	uint32 m_line,m_col;

	xbString locationStr() const;
	xbString locationStr(const xbString &msg) const;
};

class MPFSFileRecord;
class DynamicVariable;
class DynamicSubProto;
class DynamicVariableParser:public xbBase
{
private:
	IDynamicVariableParserUI *m_pUI;

	//uint32 offSetCounter;
	//uint32 parseItrtn;
	//uint32 tempFileRcrdLen;
	
    xbPtrArray<DynamicVariable> vars;
	bool						bRequireMPFS2m;
//    private Regex parser = new Regex(@"~(inc:[A-Za-z0-9\ \.-_\\/]{1,60}|[A-Za-z0-9_]{0,40}(\([A-Za-z0-9_,\ ]*\))?)~");
//    private ASCIIEncoding ascii = new ASCIIEncoding();
    xbString projectDir,sourceDir;

    // String Constants
    static const char* HTTPPRINT_H_HEADER;
    static const char* HTTPPRINT_H_SUBSTSTART;
    static const char* HTTPPRINT_H_SUBSTEND;
    static const char* HTTPPRINT_H_QUERYIFSTART;
    static const char* HTTPPRINT_H_QUERYIFEND;
    static const char* HTTPPRINT_H_QUERYWHILESTART;
    static const char* HTTPPRINT_H_QUERYWHILEEND;
    static const char* HTTPPRINT_H_CONTENTTYPESTART;
    static const char* HTTPPRINT_H_CONTENTTYPEEND;
	static const char* HTTPPRINT_H_FOOTER;

public:
	DynamicVariableParser();
	void Init(IDynamicVariableParserUI *pUI,const xbString &_projectDir,const xbString &_sourceDir);

    /// <summary>
    /// Parses and indexes a file for dynamic variables / version MPFS2
    /// </summary>
    /// <param name="file">The MPFSFileRecord to parse</param>
    /// <returns>An MPFSFileRecord of indexes, or null if no variables were found</returns>
    MPFSFileRecord *MPFSparse(MPFSFileRecord &file);

    /// <summary>
    /// Parses and indexes a file for dynamic variables / version MPFS2m
    /// </summary>
    /// <param name="file">The MPFSFileRecord to parse</param>
	void MPFS2mParse(MPFSFileRecord &file);

    /// <summary>
    /// Writes out HTTPPrint.h and HTTPPrint.idx if necessary
    /// </summary>
    /// <returns>TRUE if the files were written, FALSE if no changes are needed</returns>
    bool WriteIndices(MPFStokenVersion tokVer);

	/// <summary>
    /// Specifies the path for the image file, along with any associated index files
    /// </summary>
    xbString GetProjectDir() const
    {
        return projectDir;
    }
    void SetProjectDir(const xbString &projDir)
    {
        projectDir = projDir;
    }
	xbPropertyRW(xbString,ProjectDir);

	/// <summary>
    /// Specifies the path for the web pages
    /// </summary>
    xbString GetSourceDir() const
    {
        return sourceDir;
    }
    void SetSourceDir(const xbString &srcDir)
    {
        sourceDir = srcDir;
    }
	xbPropertyRW(xbString,SourceDir);

	int MPFS2mContentTypeAdd(const xbString &fileName,const xbString &sct,const FileLoc &fileLoc);

    /// <summary>
    /// Dump the list of used tokens and their locations in sources
    /// </summary>
    void ListUsedTokens(bool bFull);
private:
	//Private Methods

	/// <summary>
	/// Find and parse token like ~sub(1,...)~
    /// </summary>
	bool MPFStokenParse(const byte **ppData,const byte *pDataEnd,const byte **pDataTokenStart,xbString *pToken,FileLoc *pFileLoc,FileLoc *pTokenLoc,MPFStokenVersion tokVer);
	void MPFStokenVerify(const xbString &tok,MPFStokenVersion tokVer);

	/// <summary>
    /// Finds the index of a dynamic variable, or creates a new one
    /// </summary>
    /// <param name="name"></param>
    /// <returns>The index of the dynamic variable</returns>
    int UseVar(const xbString &name,const FileLoc &fileLoc, MPFSvarType varType);
	int GetVarIndex(const xbString &name, MPFSvarType varType);

	//////////////////////////////////////////////////////////////////////////
	//MPFS2m extensions methods
	//////////////////////////////////////////////////////////////////////////
	/* 
	MPFS2m dynamic file format (unlike classical MPFS2, there is no index file, but a special file format):
	
	[0000]   opCode: WORD < 0x8000 -> opCode=lgData: data block lengh (see MPFS2mOutDataWr function)
	.... datas to output
	[lgData] Next data block or command (command if opCode >= 0x8000)
	...

	By convention, all jumps are relative to the start of statement/opcode.

	high byte opCode list:
#define MPFS2m_VARSUBST		0x80	//Classical MPFS2 variable substitution
#define MPFS2m_DIE			0x81	//Exit dynamic file parse 
#define MPFS2m_TILDE		0x82	//Output a ~
#define MPFS2m_VOID			0x83	//No output (ex: to force file to be not cacheable by browser)
#define MPFS2m_IF			0x84	//if expr statement
#define MPFS2m_WHILEFIRST	0x85	//while expr statement / first call/loop + jump if not forward
#define MPFS2m_WHILENEXT	0x86	//while expr statement / next call/loop + jump if backward
#define MPFS2m_JUMPFWD		0x87	//Jump forward
	*/

	/// <summary>
    /// Write a data block
    /// </summary>
	void MPFS2mOutDataWr(xbData *poutData,const byte *pDataWr,uint32 lg);

	/// <summary>
	/// Signal a block end (wend or endif)
    /// </summary>
	void condPop(const xbString &stype,const FileLoc &fileLoc,xbData *pOutData);

	/// <summary>
	/// Signal the start of a else block
    /// </summary>
	void condElse(const FileLoc &fileLoc,xbData *pOutData);

	/// <summary>
	/// Signal the start of an if or while block
    /// </summary>
	void condPush(const xbString &stype,const xbString &squery,const FileLoc &fileLoc,xbData *pOutData);

	/// <summary>
	/// Signal the start of an elseif block
    /// </summary>
	void condElseIf(const xbString &squery,const FileLoc &fileLoc,xbData *pOutData);

	/// <summary>
	/// Check that all blocks are closed
    /// </summary>
	void condEnd(xbData *pOutData);

	/// <summary>
	/// Place a jump address to solve
    /// </summary>
	void condIfNotJumpToSolveAt(int pos);

	class condLevel
	{
	public:
		condLevel()
		{
			m_dataOutStart = -1;
			m_ifNotJumpToSolve = -1;
			m_bElseOk = false;
			m_bElseIfOk = false;
		}
		int			m_dataOutStart;
		xbString	m_stype,m_squery;
		FileLoc		m_fileLoc;
		bool		m_bElseOk,m_bElseIfOk;

		int			m_ifNotJumpToSolve;
		xbArray<int> m_arrEndJumpToSolve;
	};
	xbArray<condLevel> m_condLevel;
	void condJumpToEndToSolve(condLevel &lvl,xbData *pOutData);
	void condIfNotJumpSolveHere(condLevel &lvl,xbData *pOutData);
	void condJumpToEndSolveHere(condLevel &lvl,xbData *pOutData);

	static int DynamicSubProtoSortCb(DynamicSubProto* const &pProto1,DynamicSubProto* const &pProto2);
};

class DynamicVariable:public xbBase
{
public :
    /// <summary>
    /// Gets or sets the name of this DynamicVariable
    /// </summary>
    xbString Name;

	/// <summary>
    /// Indicates if this specific instance was previously used
    /// </summary>
    bool WasUsed;

	/// <summary>
    /// Indicates how many times this instance is used
    /// </summary>
    int Count;

	/// <summary>
    /// Indicates the index for big switches
    /// </summary>
    int Index;

	/// <summary>
    /// List of files and positions where variable is used
    /// </summary>
	xbString fileLocations;


	/// <summary>
	/// MPFS2m extension, varType = MPFSvarType_ForQuery[...] -> call made for a if or a while statement
    /// </summary>
	MPFSvarType varType;

//	long offsetCntr;

    // Constructor
    DynamicVariable(xbString name)
    {
		Index = -1;
		name.Trim();
		varType = name.StartWith("?")?MPFSvarType_ForQueryIf:MPFSvarType_ForSubst;
		if (varType == MPFSvarType_ForQueryIf)
		{
			if (name.StartWith("?C?"))
			{
				name = name.Mid(3);
				varType = MPFSvarType_ForContentType;
			}
			else if (name.StartWith("?W?"))
			{
				name = name.Mid(3);
				varType = MPFSvarType_ForQueryWhile;
			}
			else if (name.StartWith("?I?"))
			{
				name = name.Mid(3);
			}
			else
				name = name.Mid(1);
		}
        WasUsed = name.StartWith("+");
		if (WasUsed)
			name = name.Mid(1);
        Name = name;
        Count = 0;
//		offsetCntr = 0;
    }
	void AddFileLocation(const FileLoc &fileLoc)
	{
		Count++;

		xbString floc(fileLoc.locationStr(Name /*"used here"*/));
/*		xbString floc("- "+fileName);
		if (fileLine != 0)
			floc += "("+xbString(fileLine)+")";
		else 
			floc += " ";*/

		if (fileLocations.IsValid())
		{
			if (fileLocations.IndexOf(floc)>=0)
				return;

			fileLocations+="\n";
		}

		fileLocations += floc;
	}
};
#endif
