#ifndef _xbCONFIGFILE_
#define _xbCONFIGFILE_
/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbConfigFile
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifndef _xbCONFIG_
#	include "xbConfig.h"
#endif

#ifndef _xbSTREAM_
#	include "xbStream.h"
#endif

#define xbCONFIGIOLOCK_TRYCNT		20
#define xbCONFIGIOLOCK_TRYSLEEP		200

struct xbConfigLoadCtx
{
	xbConfigLoadCtx(xbCfgKeyChangesInfo *pChgInfo)
	{
		m_bChg = false;
		m_pParent = NULL;
		m_pChgInfo = pChgInfo;
	}
	xbConfigLoadCtx(xbConfigLoadCtx *pParent,const xbString &keyName)
	{
		m_bChg = pParent->m_bChg;
		if (pParent->m_keyPath.IsValid())
			m_keyPath = pParent->m_keyPath + "/" + keyName;
		else
			m_keyPath = keyName;
		m_pParent = pParent;
		m_pChgInfo = pParent->m_pChgInfo;

	}
	void change() {m_bChg = true;}
	void changeEnd();
	xbString	m_keyPath;
	bool		m_bChg;
	xbConfigLoadCtx *m_pParent;
	xbCfgKeyChangesInfo *m_pChgInfo;
};

struct xbConfigWriteCtx
{
	xbConfigWriteCtx(IxbOutputStream *pStm,xbCfgKeyChangesInfo *pChgInfo)
	{
		m_bChg = false;
		m_pParent = NULL;
		m_pChgInfo = pChgInfo;
		m_pStm = pStm;
		m_level = -1;
	}
	xbConfigWriteCtx(xbConfigWriteCtx *pParent,const xbString &keyName)
	{
		m_bChg = pParent->m_bChg;
		if (pParent->m_keyPath.IsValid())
			m_keyPath = pParent->m_keyPath + "/" + keyName;
		else
			m_keyPath = keyName;
		m_pParent = pParent;
		m_pChgInfo = pParent->m_pChgInfo;
		m_pStm = pParent->m_pStm;
		m_level = pParent->m_level+1;
	}
	void change() {m_bChg = true;}
	void changeEnd();

	void writeClf(int cLf);
	void writeLine(const xbStringW &str,int cAddIndent = 0);

	xbStringW valueEscape(const xbStringW &str);
	xbStringW valueHtmlEscape(const xbStringW &str);
	int lineWidthMax(int cAddIndent = 0);
	xbStringW lineIndent(int cAddIndent = 0);

	int tabSize() const {return 4;}
	int lineSize() const {return 100;}
	int lineSizeMin() const {return 35;}

	xbString			m_keyPath;
	bool				m_bChg;
	xbConfigWriteCtx	*m_pParent;
	xbCfgKeyChangesInfo *m_pChgInfo;
	IxbOutputStream		*m_pStm;
	int					m_level;
};
class xbConfigXmlNode;
class xbEXPORT __declspec(uuid("{489EDA45-8E83-41f9-9C65-1AFFD2A94BBA}"))
	xbConfigNodeBase:public xbObject
{
public:
	xbConfigNodeBase()
	{
		m_bUsed = m_bChanged = true;
		m_bToWrite = false;
		m_cLf = 0;
	}
	virtual ~xbConfigNodeBase()
	{
	}

	xbImplObject();

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbConfigNodeBase);

		return xbObject::GetInterface(riid,ppInterf);
	}

	virtual void Destroy()
	{
		xbObject::Destroy();
	}
	virtual xbString GetName() const = 0;
	virtual int CompareName(const char* pStr) = 0;
	virtual void cfgFileLoad(xbConfigLoadCtx *pCtx,xbConfigXmlNode *pXml) = 0;
	virtual void cfgFileWrite(xbConfigWriteCtx *pCtx,LPCSTR pName = NULL) = 0;
	int	GetConfigLfLength() const {return m_cLf;}
	void SetConfigLfLength(int val) {m_cLf = val;}
	int	GetConfigOrder() const {return m_cfgOrder;}
	void SetConfigOrder(int val) {m_cfgOrder = val;}

	void usedStart()
	{
		m_bUsed = (m_bChanged || m_bToWrite);
	}
	bool usedEnd()
	{
		return m_bUsed;
	}
	void usedSet()
	{
		m_bUsed = true;
	}
	bool isUsed() const {return m_bUsed;}
	void writeSet() 
	{
		m_bToWrite = true;
	}
	void writeUnset() 
	{
		m_bToWrite = false;
	}
	bool isToWrite() const {return m_bToWrite;}
protected:
	virtual void onBeforeDestruction(bool /*bInDestroy*/);
	bool	m_bUsed;
	bool	m_bChanged;
	bool    m_bToWrite;
	int		m_cLf;  //line feed count between this section and the previous for file regeneration to keep approx presentation
	int		m_cfgOrder;
};

#define xbConfigComment_NAME "$comment$"
class xbEXPORT __declspec(uuid("{67DB6B8F-50C4-4684-9C22-D1D9C52CA579}"))
	xbConfigComment:public xbConfigNodeBase
{
public:
	xbImplCreateObject(xbConfigComment,xbConfigComment);

	xbConfigComment()
	{
	}
	virtual ~xbConfigComment()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbConfigComment);

		return xbConfigNodeBase::GetInterface(riid,ppInterf);
	}
	virtual xbString GetName() const {return xbConfigComment_NAME;}
	virtual int CompareName(const char* pStr)
	{
		return xbStricmp(xbConfigComment_NAME,pStr);
//		return xbString().CompareNC(*pStr);
	}
	virtual void cfgFileLoad(xbConfigLoadCtx *pCtx,xbConfigXmlNode *pXml);
	virtual void cfgFileWrite(xbConfigWriteCtx *pCtx,LPCSTR pName = NULL);
protected:
	xbStringW m_comment;
};

class xbEXPORT __declspec(uuid("{1E04DD8F-4828-45bf-BB63-C36FE2C4C108}"))
	xbConfigNode:public xbConfigNodeBase,
	public IxbCfgKey
{
public:
	xbImplCreateObjectWithArg(xbConfigNode,xbConfigNode,xbConfigNode*);

	xbConfigNode();
	virtual ~xbConfigNode() ;

	xbImplObject();

	void Create(xbConfigNode* pParent)
	{
		m_pParent = pParent;
		xbConfigNodeBase::Create();
	}

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(IxbCfgKey);
		xbImplInterface(xbConfigNode);

		return xbConfigNodeBase::GetInterface(riid,ppInterf);
	}

	//IxbCfgKey methods
	virtual bool GetValue(const char *pName, xbVariant *pValue) const;
	virtual void SetValue(const char *pName, const xbVariant &value,xbCfgValMode mode = xbCfgVal_Auto);
	virtual bool EnumValue(int &idx,xbString *pName, xbVariant *pValue) const;
	virtual bool DeleteValue(const char *pName,xbOpMode opMode = xbOp_Optional);

	virtual IxbCfgKey *GetKey(const char *pName,xbString *pKeyStaying) const;
	virtual IxbCfgKey *CreateKey(const char *pName);
	virtual bool EnumKey(int &idx,xbString *pName, xbRefPtr<IxbCfgKey> *pprKey) const;

	virtual bool Delete(bool bRecurse = false,xbOpMode opMode = xbOp_Optional);
	virtual xbString GetName() const {return m_name;}

	virtual void SetRights(ulong attr);
	virtual ulong GetRights() const;

	virtual void Destroy()
	{
		xbConfigNodeBase::Destroy();
	}

	//Implementation
	virtual int CompareName(const char* pStr)
	{
		return m_name.CompareNC(pStr);
	}
	virtual void cfgFileLoad(xbConfigLoadCtx *pCtx,xbConfigXmlNode *pXml);
	void SetName(const xbString &name)
	{
		m_name = name;
	}
	void checkRights(ulong attr) const;
	virtual bool askWriteAccess(xbSectLock *pLck);
	bool deleteChildKey(xbConfigNode *pChild,const xbString &childName,bool bRecurse,xbOpMode opMode);
	xbString GetPath() const;

	virtual void cfgFileWrite(xbConfigWriteCtx *pCtx,LPCSTR pName = NULL);
protected:
	virtual void onBeforeDestruction(bool /*bInDestroy*/);

	mutable xbSectShare						m_sectData;
	xbConfigNode							*m_pParent;
	xbString								m_name;	

	xbArray< xbRefPtr<xbConfigNodeBase> >	m_arrKey;
	xbArray< xbConfigNodeBase* >			m_arrKeyOrdered;

	bool									m_bWriteAsked;

	class ValueInfo:public xbBase
	{
	public:
		ValueInfo()
		{
			m_bUsed = true;
			m_bToWrite = false;
			m_order = -1;
			m_cLf = 0;
			m_mode = xbCfgVal_Auto;
			m_modeCalc = xbCfgVal_Auto;
		}
		xbString		m_name;
		xbVariant		m_value;
		xbCfgValMode	m_mode;
		xbCfgValMode	m_modeCalc;
		int				m_order;
		int				m_cLf;
		bool			m_bUsed;
		bool			m_bToWrite;

		bool			asTagConst() const
		{
			return m_mode == xbCfgVal_Auto
				|| m_mode == xbCfgVal_AsTag;
		}
		bool			asTagCalc() const;
		xbStringW		toXmlTagString(xbConfigWriteCtx *pCtx,int &curLineLength,int &sectClf);
		xbStringW		toXmlSectionString(xbConfigNode *pParent,xbConfigWriteCtx *pCtx);

		xbDclNoCopyForObject(ValueInfo);
	};

	xbPtrArray< ValueInfo >					m_arrValue;
	xbArray< ValueInfo* >					m_arrValueOrdered;

	ulong									m_rights;

	static int keyFindCb(const xbRefPtr<xbConfigNodeBase> &prNode,const void *pStr);
	static int valueFindCb(ValueInfo* const &pTag,const void *pStr);
	static int keyOrderFindCb(xbConfigNodeBase* const &pNode,const void *pKey);
	static int valueOrderFindCb(ValueInfo* const &pTag,const void *pInfo);

	void xmlValueUsedStart();
	void xmlValueSet(const char *pName, xbVariant &value, int order, xbCfgValMode mode = xbCfgVal_Auto, int cLf = 0);
	void xmlValueUsedEnd(bool bAll);
	void valueOrderAdd(ValueInfo* pVal);
	void valueOrderDel(ValueInfo* pVal);
	ValueInfo **valueFind(const char *pName,int *pPos,bool *pbValsWithSameName,bool bNotUsed);
	ValueInfo *execValueFind(const char *pName,int *pPos = NULL);
	xbString execValueName(const xbString &name);

	void xmlKeyUsedStart();
	void xmlKeyUsedEnd();
	bool xmlKeyReuse(const xbString &name, xbRefPtr<xbConfigNode> *pprNode,int order);
	void xmlKeyCommentReuse(xbRefPtr<xbConfigComment> *pprNode,int order);
	void keyOrderAdd(xbConfigNodeBase* pKey);
	void keyOrderDel(xbConfigNodeBase* pKey);
	xbRefPtr<xbConfigNodeBase> *keyFind(const char *pName,int *pPos,bool *pbKeysWithSameName,bool bNotUsed);
	xbRefPtr<xbConfigNodeBase> *execKeyFind(const char *pName,int *pPos = NULL);
	xbString execKeyName(const xbString &name);

	int getNextOrder();
public:
	static void nameParse(LPCSTR pName,xbString *pNamePart,int *pNameOrder);
	static bool _nameIsValid(LPCSTR pName);
	static bool xmlValueNameIsValid(LPCSTR pName);
	static bool xmlKeyNameIsValid(LPCSTR pName);
	static bool exeValueNameIsValid(LPCSTR pName);
	static bool exeKeyNameIsValid(LPCSTR pName);

};

struct xbEXPORT xbConfigFileInit
{
	xbConfigFileInit(xbCfgEngine *pEngine,xbCfgRoot root,const xbString &filePath,xbCfgKeyChangesInfo *pInfo)
	{
		m_pEngine = pEngine;
		m_root = root;
		m_filePath = filePath;
		m_pInfo = pInfo;
	}
	xbCfgEngine *m_pEngine;
	xbCfgRoot	m_root;
	xbString	m_filePath;
	xbCfgKeyChangesInfo *m_pInfo;
};

class xbEXPORT __declspec(uuid("{989EBEC6-720F-4063-AEEE-B447D6423272}"))
	xbConfigFile:public xbConfigNode,
	public IxbCfgKeyRoot
{
public:
	xbImplCreateObjectWithArg(xbConfigFile,IxbCfgKeyRoot,xbConfigFileInit *);

	xbConfigFile();
	virtual ~xbConfigFile() ;

	xbImplObject();

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(IxbCfgKeyRoot);
		xbImplInterface(xbConfigFile);

		return xbConfigNode::GetInterface(riid,ppInterf);
	}

	//IxbCfgKeyRoot methods
	virtual void CheckLoadWrite(xbCfgKeyChangesInfo *pInfo);
	virtual xbCfgRoot GetCfgRoot() const {return m_root;}
	virtual xbString GetCfgFilePath() const {return m_filePath;}
	virtual HANDLE FileChangeNotifStart();
	virtual void FileChangeNotifRestart();
	virtual void FileChangeNotifClose();

	//IxbCfgKey methods
	virtual bool GetValue(const char *pName, xbVariant *pValue) const;
	virtual void SetValue(const char *pName, const xbVariant &value,xbCfgValMode mode = xbCfgVal_Auto);
	virtual bool EnumValue(int &idx,xbString *pName, xbVariant *pValue) const;
	virtual bool DeleteValue(const char *pName,xbOpMode opMode = xbOp_Optional);

	virtual IxbCfgKey *GetKey(const char *pName,xbString *pKeyStaying) const;
	virtual IxbCfgKey *CreateKey(const char *pName);
	virtual bool EnumKey(int &idx,xbString *pName, xbRefPtr<IxbCfgKey> *pprKey) const;

	virtual bool Delete(bool bRecurse = false,xbOpMode opMode = xbOp_Optional);
	virtual xbString GetName() const;

	virtual void SetRights(ulong attr);
	virtual ulong GetRights() const;

	virtual void Destroy()
	{
		xbConfigNode::Destroy();
	}

	virtual bool askWriteAccess(xbSectLock *pLck);
protected:
	void Create(xbConfigFileInit *pInit);
	virtual void onBeforeDestruction(bool /*bInDestroy*/);
	bool cfgOpenStart(xbCfgKeyChangesInfo *pInfo);
	bool cfgFileReadAccessOpen();
	void cfgFileWriteAccessOpen();
	bool cfgFileWriteInProgress(xbCfgKeyChangesInfo *pInfo);
	xbString dirCreate();

	xbCfgRoot		m_root;
	xbString		m_filePath;
	HANDLE			m_hNotif;

	xbArray< xbRefPtr<IxbObject> >	m_arrNodeRsrv;
	xbFileStream	m_fstm;
	bool			m_bInError;
	xbDateTime		m_dtLastWr;
	xbTextBOMtype	m_bomType;

	ulong			m_tiWriteAccessOk;
	bool			m_bWriteAccessOk;

	class IOlock
	{
	public:
		IOlock()
		{
			m_pObj = NULL;
		}
		IOlock(xbConfigFile *pObj,xbOpMode opm =xbOp_Required)
		{
			m_pObj = NULL;
			if (pObj) Lock(pObj,opm);
		}
		~IOlock()
		{
			eProtect(Unlock());
		}
		bool Lock(xbConfigFile *pObj,xbOpMode opm =xbOp_Required)
		{
			if (m_pObj) 
				Unlock();
			if (!pObj->_IOlock(opm))
				return false;
			m_pObj = pObj;
			return true;
		}
		void Unlock()
		{
			if (!m_pObj) return;
			xbConfigFile	*pObj = m_pObj;
			m_pObj = NULL;
			pObj->_IOunlock();
		}
	protected:
		xbConfigFile	*m_pObj;
	};

	friend IOlock;

	bool _IOlock(xbOpMode opm);
	void _IOunlock();
	ulong m_IOlockThId;
};

#endif
