#ifndef _xbCONFIG_
#define _xbCONFIG_
/*<<<***************************************************************************
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: xbConfig
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifndef _xbVariant_
#	include "xbVariant.h"
#endif

#ifndef _xbENGINE_
#	include "xbEngine.h"
#endif

#ifndef _xbSTRING_
#	include "xbString.h"
#endif

#ifndef _xbPTR_
#	include "xbPtr.h"
#endif

#ifndef _xbHOTPLUGITF_
#	include "xbHotPlugItf.h"
#endif

#ifndef _xbTHREAD_
#	include "xbThread.h"
#endif

#define xbXDCDATA_PATH "%xdcprogramdata%"
#define xbXDCLIB_PATH "%xdclibdir%"
#define xbXDCAPP_PATH "%xdcappdir%"
#define xbXDCCONFIG_DIR "config"

#define xbXDCCFG_EXT ".xdcfg"

#define xbXDCCFG_WAITTIMEOUT 60000
#define xbXDCCFG_WRITETIMEOUT 5000

enum xbCfgRoot
{
	xbCfgUndefined=0,
	xbCfgCommon=1,
	xbCfgAppli=2,
	xbCfgCommonUser=3,
	xbCfgAppliUser=4,
	xbCfgExtern=5,
	_xbCfgIndexLimit=5
};

class xbCfgEnumKey;
class xbCfgEnumValue;

#define xbCfgKeyRight_READ			0x00000001
#define xbCfgKeyRight_WRITE			0x00000002
#define xbCfgKeyRight_CREATEKEY		0x00000004
#define xbCfgKeyRight_DELETEKEY		0x00000008
#define xbCfgKeyRight_CREATEVAL		0x00000010
#define xbCfgKeyRight_DELETEVAL		0x00000020
#define xbCfgKeyRight_ALL			0x0000003F

enum xbCfgValMode
{
	xbCfgVal_Auto,
	xbCfgVal_AsTag,
	xbCfgVal_AsSection,
	xbCfgVal_AsSectionOneLine,
	xbCfgVal_AsSectionValue,
	xbCfgVal_AsSectionNoEncode,
	xbCfgVal_AsSectionOneLineNoEncode
};
interface __declspec(uuid("{64BB6F81-3E8E-4d16-BC02-A050356F263C}")) 
	xbEXPORT IxbCfgKey:public IxbObject
{
	virtual bool GetValue(const char *pName, xbVariant *pValue) const = 0;
	virtual void SetValue(const char *pName, const xbVariant &value,xbCfgValMode mode = xbCfgVal_Auto) = 0;
	virtual bool EnumValue(int &idx,xbString *pName, xbVariant *pValue) const = 0;
	virtual bool DeleteValue(const char *pName,xbOpMode opMode = xbOp_Optional) = 0;

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

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

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

	virtual void Destroy() = 0;
};

struct xbCfgKeyChangesInfo;
struct xbCfgKeyErrorsInfo;
interface __declspec(uuid("{5F9EB8EB-8374-4a58-9E85-BA9A21F5939B}")) 
	xbEXPORT IxbCfgKeyRoot:public IxbCfgKey
{
	virtual void CheckLoadWrite(xbCfgKeyChangesInfo *pInfo) = 0;
	virtual xbCfgRoot GetCfgRoot() const = 0;
	virtual xbString GetCfgFilePath() const = 0;
	virtual HANDLE FileChangeNotifStart() = 0;
	virtual void FileChangeNotifRestart() = 0;
	virtual void FileChangeNotifClose() = 0;
};

class xbCfgChangedCb;
xbDclCb(xbEXPORT,xbCFGCHANGED,(xbCfgChangedCb &cbObj,xbCfgRoot root,xbCfgKeyChangesInfo *pInfo,const xbString &cfgKeyPath),(cbObj,root,pInfo,cfgKeyPath));
xbDclCb(xbEXPORT,xbCFGERRORS,(xbCfgChangedCb &cbObj,xbCfgRoot root,xbCfgKeyErrorsInfo *pInfo,const xbString &cfgKeyPath),(cbObj,root,pInfo,cfgKeyPath));

interface __declspec(uuid("{F10BC805-5E97-4f1d-A5D3-0A85A4824331}")) 
	xbEXPORT IxbCfgChangedCb:public IxbObject
{
	virtual void OnConfigChanged(xbCfgRoot root,xbCfgKeyChangesInfo *pInfo) = 0;
	virtual void OnConfigErrors(xbCfgRoot root,xbCfgKeyErrorsInfo *pInfo) = 0;
};

class xbEXPORT xbCfgKey
{
public:
	xbCfgKey(xbCfgRoot mk,const xbString &keyname);
	xbCfgKey(IxbCfgKey *pKey, bool bRdOnly,xbCfgRoot mk,const xbString &keyname);
	xbCfgKey(const xbCfgKey &parent,const xbString &keyname);
	xbCfgKey(const xbCfgKey &key);
	xbCfgKey();

	xbCfgKey &operator=(const xbCfgKey &key);

	void Init(xbCfgRoot mk,const xbString &keyname);
	void Init(xbCfgKey &parent,const xbString &keyname);
	void Init(xbCfgKey &key);
	
	bool	  DefaultValueExists() const;
	xbVariant GetDefaultValue() const;
	void	 SetDefaultValue(const xbVariant &val);
	xbPropertyRW(xbVariant,DefaultValue);
	
	bool	  ValueExists(const char *valname) const;
	xbVariant GetValue(const char *valname) const;
	void	  SetValue(const char *valname,const xbVariant &val,xbCfgValMode mode = xbCfgVal_Auto);
	xbArrayPropertyRW(xbVariant,const char *,Value);

	bool		DeleteValue(const char *valname,xbOpMode opMode = xbOp_Optional);
	bool		DeleteDefaultValue(xbOpMode opMode = xbOp_Optional);
	bool		DeleteKey(bool bRecurse = false,xbOpMode opMode = xbOp_Optional);
	bool		DeleteKey(const char *keyname,bool bRecurse = false,xbOpMode opMode = xbOp_Optional);

	bool		Exists() const;
	xbCfgKey	&CreateKey();

	xbString	GetName() const;
	xbPropertyR(xbString,Name);

/*	xbString	GetPath() const;
	xbPropertyR(xbString,Path);

	xbCfgRoot	GetRoot() const;
	xbPropertyR(xbCfgRoot,Root);
*/

	void	Close();
	virtual ~xbCfgKey();


	bool PathFromStr(const xbString &str,xbOpMode opm =xbOp_Required);
	xbString PathToStr() const;
protected:
	void Init(IxbCfgKey *pKey,const xbString &keyname);
	void Init(IxbCfgKey *pKey);

	IxbCfgKey		*getKey(xbString *pKeyStaying) const;
	IxbCfgKey		*getKey(const char *keyName,xbString *pKeyStaying) const;

	friend			xbCfgEnumKey;
	friend			xbCfgEnumValue;
protected:
	xbRefPtr<IxbCfgKey>	m_prKey;
	xbString			m_keyStaying;
};

class xbEXPORT xbCfgEnumKey
{
public:
	xbCfgEnumKey(xbCfgKey &key);
	void Init(xbCfgKey &key);
	bool	Next(xbString *pKeyName);
	bool	Next(xbCfgKey *pSubKey);
	bool	Next();
	void	Reset();
	void	Close();
	virtual ~xbCfgEnumKey();
	void setPosition(int pos) {m_pos = pos;}
	int getPosition() const {return m_pos;}
protected:
	xbCfgEnumKey(IxbCfgKey *pKey);
	int			m_pos;
protected:
	xbRefPtr<IxbCfgKey>	m_prKey;
};

class xbEXPORT xbCfgEnumValue
{
public:
	xbCfgEnumValue(xbCfgKey &key);
	void Init(xbCfgKey &key);
	bool	Next(xbString *pValName);
	bool	Next(xbString *pValName,xbVariant *pvalue);
	bool	Next();
	void	Reset();
	void	Close();
	virtual ~xbCfgEnumValue();
	void setPosition(int pos) {m_pos = pos;}
	int getPosition() const {return m_pos;}
protected:
	int			m_pos;
protected:
	xbRefPtr<IxbCfgKey>	m_prKey;
};

class xbCfgPath;
class xbEXPORT __declspec(uuid("{11CBDDB3-98E3-4931-B3D3-572420E72913}")) 
	xbCfgEngine:public xbEngine
{
public:
	xbCfgEngine(const char *pLabel,const xbEngines &engUsed = xbEngines());
	virtual ~xbCfgEngine()
	{
		Destroy();
	}
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbCfgEngine);
		return xbEngine::GetInterface(riid,ppInterf);
	}

	//Config key tools
	void GetPath(xbCfgRoot root,LPCSTR pPath,xbCfgRoot *pRoot,xbString *pStr) const;
	xbCfgPath *GetCfgPath(xbCfgRoot root);
	bool GetRoot(const xbString &rootName,xbCfgRoot *pRoot,xbOpMode opm =xbOp_Required);
	xbString GetRootName(xbCfgRoot root);
	int Add(xbCfgPath *pCfgPath);
	void Remove(int id);

	//Charset tools
	xbString GetCharsetFromCP(ulong cp,xbOpMode opm =xbOp_Required) const;
	ulong GetCPfromCharset(const xbString &charset,xbOpMode opm =xbOp_Required) const;
	ulong GetRealCP(ulong cp,xbOpMode opm =xbOp_Required) const;
	bool IsValidCP(ulong cp,bool bInstalled = true) const;
	bool IsValidCharset(const xbString &charset,bool bInstalled = true) const;
	bool EnumCP(int &idx, ulong *cp, xbString *charset,bool bInstalled = true);

	//LCID tools
	xbString GetNameFromLCID(LCID lcid,xbOpMode opm =xbOp_Required) const;
	xbString GetLabelFromLCID(LCID lcid,xbOpMode opm =xbOp_Required) const;
	LCID GetLCIDfromName(const xbString &lang,xbOpMode opm =xbOp_Required) const;
	LCID GetRealLCID(LCID lcid,xbOpMode opm =xbOp_Required) const;
	bool IsValidLCID(LCID lcid,bool bInstalled = true) const;
	bool IsValidLCID(const xbString &lcidName,bool bInstalled = true) const;
	bool EnumLCID(int &idx, ulong *lcid, xbString *lcidName, xbString *lcidLabel,bool bInstalled = true);
	
	//Content types tools
	xbString GetContentTypeFromPath(const xbString &path,xbOpMode opm =xbOp_Required) const;
	xbString GetContentTypeFromExt(const xbString &ext,xbOpMode opm =xbOp_Required) const;
	xbString GetExtFromContentType(const xbString &ct,xbOpMode opm =xbOp_Required) const;
	bool IsValidContentType(const xbString &ct) const;
	bool IsValidExtension(const xbString &ext) const;
	bool EnumContentType(int &idx, xbString *ext, xbString *ct);

	//Time-zone tools
	bool GetTimeZoneFromName(const xbString &name, TIME_ZONE_INFORMATION *pTZ, xbString *pLabel, xbOpMode opm =xbOp_Required) const;
	bool EnumTimeZone(int &idx, xbString *pName, TIME_ZONE_INFORMATION *pTZ, xbString *pLabel);
	bool IsValidTimeZone(const xbString &name) const;
	bool GetCurrentTimeZone(xbString *pName, TIME_ZONE_INFORMATION *pTZ, xbString *pLabel, xbOpMode opm =xbOp_Required) const;
	bool GetAcceptedLanguages(xbString *pLng, xbOpMode opm =xbOp_Required) const;
//	bool GetAcceptedLanguages(xbArray<xbString> *pArrLng, xbOpMode opm =xbOp_Required) const;

	//Path tools
	xbString GetExpandedString(const xbString &str);

	//tools cache
	void InitCache();

	//Main settings / simple config tools
	bool GetSettingKey(LPCSTR pValName,xbCfgKey &key);
	bool GetSettingBool(LPCSTR pValName);
	int GetSettingBoolToNum(LPCSTR pValName);
	xbString GetSettingString(LPCSTR pValName);
	long GetSettingLong(LPCSTR pValName);
	ulong GetSettingUlong(LPCSTR pValName);
	xbVariant GetSettingValue(LPCSTR pValName);

	virtual bool isLockedByMe() const {return m_sectCache.IsLockedByMe()||m_sectData.IsLockedByMe();}

	bool isInConfigLoad() const {return m_sectData.IsLockedByMe();}
	bool isConfigReady() const {return isNowStarted() && !m_sectData.IsLockedByMe();}

	bool utf8strict();

	xbRefPtr<IxbCfgKey> getKey(xbCfgRoot root,const xbString &keyname,xbString *pKeyStaying);
	xbRefPtr<IxbCfgKeyRoot> getKeyRoot(xbCfgRoot root);

	void AddPlugTypeCb(IxbHotPlugTypeCb *pCb);
	void AddConfigChangedCb(IxbCfgChangedCb *pCb);
	void AskConfigUpdate() const;

public:
	//Events called by config loader
	void onConfigChanged(xbCfgRoot root,xbCfgKeyChangesInfo *pInfo);
	void onConfigErrors(xbCfgRoot root,xbCfgKeyErrorsInfo *pInfo);

	//Events called by plugins
	xbPlugUpdateCaps	onPlugTypeGetPluginCaps(IxbHotPlugin*); //called before internal update to see if update is possible and when.
	void				onPlugTypeUnloadPluginAsked(IxbHotPlugin*,bool bForce); //called by module message or OnStartBinding
	void				onPlugTypeLoadPluginAsked(IxbHotPlugin*); //called by module message or OnStopBinding / OnObjectAvailable
	void				onPlugTypeProgress(IxbHotPlugin*,double progress,
									xbPlugStatus status, const xbString &statusText);
	xbPlugUpdateCaps	onPlugTypeError(IxbHotPlugin*,bool bCanRetry, HRESULT hr, const xbString &errorText);
	void				onPlugTypeReloadConfig(sIxbHotPlugType *pPlugType);
protected:
	virtual void onStart();
	virtual bool onStop(bool);

	mutable xbSectShare	m_sectData;
	struct cfgFileEntry
	{
		xbCfgRoot				m_root;
		xbRefPtr<IxbCfgKeyRoot>	m_prKeyRoot;
	};
	xbArray<cfgFileEntry>	m_arrCfgFile;
	ulong					m_cFileChanged;
	bool					m_bWriteInProgress;

	xbArray<xbCfgPath*>	m_arrPath;

	mutable xbSectShare	m_sectCache;
	struct LCIDitem
	{
		LCIDitem()
		{
		}
		LCIDitem(LCID lcid,const xbString &name,const xbString &label)
			:m_lcid(lcid),m_name(name),m_label(label)
		{
		}
		LCID		m_lcid;
		xbString	m_name,m_label;
	};
	xbPtrArray<LCIDitem>		m_arrLcid;
	xbPtrArray<LCIDitem,false>	m_arrLcidByCode;
	void LCIDload();
	static int	lcidFindNameCB(LCIDitem* const &pItem1,const void *_pItem2);
	static int	lcidFindCodeCB(LCIDitem* const &pItem1,const void *_pItem2);
	int							m_utf8strict;


	mutable long							m_nLockCbCall,m_nLockCbAdd;
	xbPtrArray< xbObsPtr<IxbCfgChangedCb> >	m_arrCfgCb;
	xbPtrArray< xbObsPtr<IxbHotPlugTypeCb> >	m_arrCb;

	void lockCbCall() const;
	void unlockCbCall() const;

	void lockCbAdd() const;
	void unlockCbAdd() const;

	void addCb(IxbHotPlugTypeCb *pCb);
	void addCfgCb(IxbCfgChangedCb *pCb);


	class xbEXPORT  Thread:public xbTThreadBase<xbThreadCb>
	{
	public:
		Thread(xbCfgEngine *pParent)
			:xbTThreadBase<xbThreadCb>(&pParent->m_sectData,xbString(pParent->Label)+" Thread")
		{
			m_pParent = pParent;
		}
		virtual DWORD	onThreadProc(sIxbThread *pTh);
		void wakeUp() {m_eventPtr->Pulse();}
		ulong eventArrayMake(xbArray<HANDLE> &arrEv,xbArray< xbRefPtr<IxbCfgKeyRoot> > &arrFile);
		inline bool shouldStop() const
		{
			return IsCloseAskedInt();
		}
	protected:
		xbCfgEngine	*m_pParent;
	};
	friend Thread;

	Thread m_th;

	void cfgFileCheckLoadWrite(sIxbThread *pTh, IxbCfgKeyRoot *pCfgFile);
	void cfgFileCheckLoadWrite(sIxbThread *pTh);
	void cfgFileWriteEnd(sIxbThread *pTh);
	void cfgFileOpen(xbCfgRoot root,LPCSTR pFilePath,xbSectLock *pLckEngine);
	void cfgFileClose(xbCfgRoot root);
	bool cfgFileEnum(int &idx,xbRefPtr<IxbCfgKeyRoot> *pprKeyRoot,xbCfgRoot *pRoot,bool bRemove);
	bool cfgFileGet(xbCfgRoot root,xbRefPtr<IxbCfgKeyRoot> *pprKeyRoot,xbCfgRoot *pRoot,bool bRemove);

	ulong cfgFileEventArrayMake(int &idx,xbArray<HANDLE> &arrEv,xbArray< xbRefPtr<IxbCfgKeyRoot> > &arrFile);
public:
	xbString cfgFileDefaultPathGet(xbCfgRoot root);
	xbString cfgFileLibPath(xbString fname);
	xbString cfgFileUserPath(xbString fname);

	void onConfigFileInit(xbCfgRoot root,IxbCfgKeyRoot *pRoot);
public:
	class CbCallLock
	{
	public:
		CbCallLock()
		{
			m_pObj = NULL;
		}
		CbCallLock(const xbCfgEngine *pObj)
		{
			m_pObj = NULL;
			if (pObj) Lock(pObj);
		}
		~CbCallLock()
		{
			eProtect(Unlock());
		}
		void Lock(const xbCfgEngine *pObj)
		{
			if (m_pObj) Unlock();
			pObj->lockCbCall();
			m_pObj = pObj;
		}
		void Unlock()
		{
			if (!m_pObj) return;
			const xbCfgEngine	*pObj = m_pObj;
			m_pObj = NULL;
			pObj->unlockCbCall();
		}
	protected:
		const xbCfgEngine	*m_pObj;
	};
	class CbAddLock
	{
	public:
		CbAddLock()
		{
			m_pObj = NULL;
		}
		CbAddLock(const xbCfgEngine *pObj)
		{
			m_pObj = NULL;
			if (pObj) Lock(pObj);
		}
		~CbAddLock()
		{
			eProtect(Unlock());
		}
		void Lock(const xbCfgEngine *pObj)
		{
			if (m_pObj) Unlock();
			pObj->lockCbAdd();
			m_pObj = pObj;
		}
		void Unlock()
		{
			if (!m_pObj) return;
			const xbCfgEngine	*pObj = m_pObj;
			m_pObj = NULL;
			pObj->unlockCbAdd();
		}
	protected:
		const xbCfgEngine	*m_pObj;
	};

	friend CbCallLock;
	friend CbAddLock;
	friend xbCfgPath;
};

extern xbCfgEngine	xbEXPORT g_xbCfgEngine;

class xbEXPORT __declspec(uuid("{D40DFD5E-A423-40de-9D2E-AFAFCC8D293A}"))
	xbCfgPath:public xbEngine
{
public:
	xbCfgPath(const char *pName,const char *pLabel,xbCfgRoot root,const char *pPath)
		:xbEngine(/*xbEngineStart_Auto*/ xbEngineStart_OnUse,pLabel,xbEngines(&g_xbCfgEngine))
	{
		m_pName = pName;
		if (int(root) < 0)
			m_pCfgPath = g_xbCfgEngine.GetCfgPath(root);
		else
			m_pCfgPath = NULL;
		m_root = root;
		m_path = pPath;
		m_id = 0;
	}
	xbCfgPath(const char *pName,const char *pLabel,xbCfgPath *pCfgPathRoot,const char *pPath)
		:xbEngine(/*xbEngineStart_Auto*/ xbEngineStart_OnUse,pLabel,xbEngines(&g_xbCfgEngine,pCfgPathRoot))
	{
		m_pName = pName;
		m_pCfgPath = pCfgPathRoot;
		m_root = xbCfgCommonUser;
		m_path = pPath;
		m_id = 0;
	}
	virtual ~xbCfgPath()
	{
		Destroy();
	}
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbCfgPath);
		return xbEngine::GetInterface(riid,ppInterf);
	}
	void GetAbsPath(xbCfgEngine *pEngine,xbCfgRoot *pRoot,xbString *pPath,xbSectLock *pLckEngine) const;
	xbString GetPath() const;
	void SetPath(const xbString &path);
	xbPropertyRW(xbString,Path);

	xbCfgRoot	GetRoot() const;
	void SetRoot(xbCfgRoot root);
	void SetRoot(xbCfgPath *pCfgPathRoot);
	xbPropertyRW(xbCfgRoot,Root);

	xbString GetName() {CheckReady();return m_pName;}
	xbPropertyR(xbString,Name);

	operator xbCfgRoot()
	{
		xbLock lckStart(this);
		return xbCfgRoot(-m_id);
	};

	virtual bool isLockedByMe() const {return m_sectData.IsLockedByMe();}
	mutable xbSectShare	m_sectData;
protected:
	virtual void onStart() 
	{
		m_id = g_xbCfgEngine.Add(this);
	}
	virtual bool onStop(bool) 
	{
		g_xbCfgEngine.Remove(m_id);
		m_id = 0;
		xbSectLock lck(&m_sectData);
		m_externFilePath.Empty();
		m_externFileSubPath.Empty();
		return true;
	}

	friend xbCfgEngine;

	int			m_id;
	xbCfgPath	*m_pCfgPath;
	xbCfgRoot	m_root;
	xbString	m_path;
	xbString	m_externFilePath;
	xbString	m_externFileSubPath;

	const char	*m_pName;
};

class xbEXPORT xbCfgChangedCb
{
public:
	xbCfgChangedCb(const xbCFGCHANGEDCB &cb)
	{
		m_cb = cb;
		m_cfgRoot = xbCfgUndefined;
	}
	xbCfgChangedCb(xbCfgRoot mk,const xbString &cfgPath,const xbCFGCHANGEDCB &cb)
	{
		m_cfgPath = cfgPath;
		m_cfgRoot = mk;
		m_cb = cb;
	}
	xbCfgChangedCb(xbCfgRoot mk,const xbCFGCHANGEDCB &cb)
	{
		m_cfgRoot = mk;
		m_cb = cb;
	}
	xbCfgChangedCb(const xbCFGERRORSCB &cb)
	{
		m_errCb = cb;
		m_cfgRoot = xbCfgUndefined;
	}
	xbCfgChangedCb(xbCfgRoot mk,const xbString &cfgPath,const xbCFGERRORSCB &cb)
	{
		m_cfgPath = cfgPath;
		m_cfgRoot = mk;
		m_errCb = cb;
	}
	xbCfgChangedCb(xbCfgRoot mk,const xbCFGERRORSCB &cb)
	{
		m_cfgRoot = mk;
		m_errCb = cb;
	}
	xbCfgChangedCb(const xbCFGCHANGEDCB &cb,const xbCFGERRORSCB &errcb)
	{
		m_cb = cb;
		m_errCb = errcb;
		m_cfgRoot = xbCfgUndefined;
	}
	xbCfgChangedCb(xbCfgRoot mk,const xbString &cfgPath,const xbCFGCHANGEDCB &cb,const xbCFGERRORSCB &errcb)
	{
		m_cfgPath = cfgPath;
		m_cfgRoot = mk;
		m_cb = cb;
		m_errCb = errcb;
	}
	xbCfgChangedCb(xbCfgRoot mk,const xbCFGCHANGEDCB &cb,const xbCFGERRORSCB &errcb)
	{
		m_cfgRoot = mk;
		m_cb = cb;
		m_errCb = errcb;
	}
	virtual ~xbCfgChangedCb()
	{
		eProtectTrace(Destroy());
	}
	void Init(xbCfgRoot mk,const xbString &cfgPath);
	void Create();
	void Destroy();
	void AskConfigUpdate() const {g_xbCfgEngine.AskConfigUpdate();}
	void Register() {g_xbCfgEngine.AddConfigChangedCb(*this);}
	operator IxbCfgChangedCb*();
	xbCfgRoot	GetConfigRoot() const
	{
		return m_cfgRoot;
	}
	xbString	GetConfigPath() const
	{
		return m_cfgPath;
	}
	virtual void onConfigChanged(xbCfgRoot root,xbCfgKeyChangesInfo *pInfo,const xbString &cfgKeyPath)
	{
		if (m_cb.IsEmpty())
			return;
		m_cb.Call(*this,root,pInfo,cfgKeyPath);
	}
	virtual void onConfigError(xbCfgRoot root,xbCfgKeyErrorsInfo *pInfo,const xbString &cfgKeyPath)
	{
		if (m_errCb.IsEmpty())
			return;

		m_errCb.Call(*this,root,pInfo,cfgKeyPath);
	}
	virtual bool IsValid() const
	{
		return (m_cb.IsValid() || m_errCb.IsValid())/*&& m_cfgRoot != xbCfgUndefined*/;
	}

	class __declspec(uuid("{2B48D80D-BF34-4e7b-B680-2B9D806B3DEB}")) 
		CBobj:public xbObject,public IxbCfgChangedCb
	{
	public:
		CBobj(xbCfgChangedCb *pParent,xbCfgRoot mk,const xbString &cfgPath)
		{
			m_cfgPath = cfgPath;
			m_cfgRoot = mk;
			m_pParent = pParent;
		}
		virtual ~CBobj()
		{
			xbAssert(m_pParent == NULL);
			eProtectTrace(Destroy());
		}

		xbImplObject();

		virtual bool GetInterface(REFIID riid, void ** ppInterf)
		{
			xbImplInterface(CBobj);
			xbImplInterface(IxbCfgChangedCb);
			return xbObject::GetInterface(riid,ppInterf);
		}

		// IxbCfgChangedCb methods
		virtual xbCfgRoot	GetConfigRoot() const
		{
			return m_cfgRoot;
		}
		virtual xbString	GetConfigPath() const
		{
			return m_cfgPath;
		}
		virtual void OnConfigChanged(xbCfgRoot root,xbCfgKeyChangesInfo *pInfo);
		virtual void OnConfigErrors(xbCfgRoot root,xbCfgKeyErrorsInfo *pInfo);
	protected:
		virtual void onBeforeDestruction(bool /*bInDestroy*/) 
		{
			xbSectLock lck(&m_sectPtr);
			m_pParent = NULL;
			m_bDestruction = true;
		}
		xbCfgChangedCb		*m_pParent;
		xbCfgRoot			m_cfgRoot;
		xbString			m_cfgPath;

		friend xbCfgChangedCb;
	};

protected:
	xbRefPtr<CBobj>		m_prCBobj;
	xbCFGCHANGEDCB		m_cb;
	xbCFGERRORSCB		m_errCb;
	xbCfgRoot			m_cfgRoot;
	xbString			m_cfgPath;
};

struct xbEXPORT xbCfgKeyError
{
	xbString	m_msg,m_ctx;
	int			m_line,m_col;

	xbCfgKeyError(const xbString &msg,int line,int col)
		:m_msg(msg),m_line(line),m_col(col)
	{
	}
	xbCfgKeyError()
	{
	}
};

struct xbEXPORT xbCfgKeyErrorsInfo
{
	xbCfgKeyErrorsInfo()
	{
	}

	bool HasErrors() const {return m_arrErr.IsValid();}
	bool Match(xbCfgRoot rootWatched,const xbString &pathWatched,xbCfgRoot root,xbString *pPath) const;

	xbArray<xbCfgKeyError> m_arrErr;

	void errorAdd(const xbString &msg,int line,int col)
	{
		m_arrErr.Add(xbCfgKeyError(msg,line,col));
	}
};

struct xbEXPORT xbCfgKeyChangesInfo
{
	xbCfgKeyChangesInfo()
	{
		m_bWriteWaiting = false;
		m_bLogChanges = true;
	}

	bool HasChanges() const {return m_arrPathChanged.IsValid();}
	bool HasWrites() const {return m_bWriteWaiting;}
	bool Match(xbCfgRoot rootWatched,const xbString &pathWatched,xbCfgRoot root,xbString *pPath) const;
	bool HasPathChanged(const xbString &path) const;

	void errorAdd(const xbString &msg,int line,int col)
	{
		m_errInfo.errorAdd(msg,line,col);
	}
	void pathChanged(const xbString &path);
	xbCfgKeyErrorsInfo	m_errInfo;
	bool				m_bLogChanges,m_bWriteWaiting;
	xbArray<xbString>	m_arrPathChanged;
};

xbString	xbEXPORT xbCfgSlash(const xbString &path);

#endif
