/*<<<***************************************************************************
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.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbConfig.h"
#include "xbConfigFile.h"
#include <winreg.h>
#include <shlobj.h>
#include "xbException.h"
#include "xbFileTools.h"
#include "xbDnItf.h"
#include "xbMsg.h"

#pragma warning(push)
#pragma warning(disable: 4311) //'cast de type' : troncation de pointeur de 'const void *' à 'ulong'
#pragma warning(disable: 4312) //'cast de type' : la conversion de 'LCID' en 'LPCVOID' d'une taille supérieure
#pragma warning(disable: 4355) //'this' : used in base member initializer list
/*
Doc branche xdcBase

	- Exception
	- Settings
		- PerfCounterBug = 1 -> pour les processeurs amd par ex

Config file format common.xdcfg + appli.xdcfg (ex: xdserver.xdcfg, xdwin.xdcfg) 
<xdcube>
	<exception help="Exception tracking options" defaultValue="..">
		<xbEsys stop="bool:true" trace="bool:true">
		</xbEsys>
		<exception:stop value="bool:true"/>
		<exception:trace>
			bool:true
		</exception:trace>
	</exception>
	<setting>
		<setting:PerfCounterBug value="long:1"/>
		<setting:Debug value="bool:true"/>
	</setting>
	<hotplug TraceLevel="long:0">
		<Socket.6>
			<xdsockws.dll enabled="bool:true">
				<ws priority="long:0" enabled="bool:true"/>
				<ssl priority="long:0" enabled="bool:true"/>
			</xdsockws.dll>
		</Socket.6>
	</hotplug>
</xdcube>
*/

xbString	xbCfgSlash(const xbString &path)
{
	return xbUrlSlash(path);
}

xbCfgEngine	g_xbCfgEngine("xdcBase Configuration Engine");

xbCfgEngine::xbCfgEngine(const char *pLabel,const xbEngines &engUsed)
	:xbEngine(xbEngineStart_Auto,pLabel,engUsed),m_th(this)
{
	m_utf8strict = 0;
	m_cFileChanged=0;
	m_bWriteInProgress = false;
}

xbCfgKey::xbCfgKey(xbCfgRoot mk,const xbString &keyname)
{
	m_prKey = g_xbCfgEngine.getKey(mk,keyname,&m_keyStaying);
}

xbCfgKey::xbCfgKey(const xbCfgKey &parent,const xbString &keyname)
{
	m_prKey = parent.getKey(keyname,&m_keyStaying);
}

xbCfgKey::xbCfgKey(const xbCfgKey &key)
{
	m_prKey = key.getKey(&m_keyStaying);
}

void xbCfgKey::Init(xbCfgRoot mk,const xbString &keyname)
{
	m_prKey = g_xbCfgEngine.getKey(mk,keyname,&m_keyStaying);
}

void xbCfgKey::Init(xbCfgKey &parent,const xbString &keyname)
{
	m_prKey = parent.getKey(keyname,&m_keyStaying);
}

void xbCfgKey::Init(xbCfgKey &key)
{
	m_prKey = key.getKey(&m_keyStaying);
}

void xbCfgKey::Init(IxbCfgKey *pKey,const xbString &keyname)
{
	m_prKey = pKey;
	m_keyStaying = keyname;
}

void xbCfgKey::Init(IxbCfgKey *pKey)
{
	m_prKey = pKey;
	m_keyStaying.Empty();
}

xbCfgKey::xbCfgKey()
{
}

xbCfgKey &xbCfgKey::operator=(const xbCfgKey &key)
{
	m_prKey = key.m_prKey;
	m_keyStaying = key.m_keyStaying;
	return *this;
}

void	xbCfgKey::Close()
{
	m_prKey.Empty();
	m_keyStaying.Empty();
}

xbCfgKey::~xbCfgKey()
{
	eProtect(Close());
}

xbVariant xbCfgKey::GetDefaultValue() const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::GetDefaultValue() : not linked to a config key !");

	xbVariant val;
	if (m_keyStaying.IsEmpty() || Exists())
		m_prKey->GetValue(NULL,&val);
	return val;
}

void	 xbCfgKey::SetDefaultValue(const xbVariant &val)
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::SetDefaultValue() : not linked to a config key !");
	if (m_keyStaying.IsValid())
		CreateKey();
	m_prKey->SetValue(NULL,val);
}

bool	 xbCfgKey::DefaultValueExists() const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::DefaultValueExists() : not linked to a config key !");
	if (m_keyStaying.IsValid() && !Exists())
		return false;
	return m_prKey->GetValue(NULL,NULL);
}

xbVariant xbCfgKey::GetValue(const char *valname) const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::GetValue("+xbString(valname)+") : not linked to a config key !");

	xbVariant val;
	if (m_keyStaying.IsEmpty() || Exists())
		m_prKey->GetValue(valname,&val);	
	return val;
}

void	 xbCfgKey::SetValue(const char *valname,const xbVariant &val,xbCfgValMode mode)
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::SetValue("+xbString(valname)+",...) : not linked to a config key !");

	if (m_keyStaying.IsValid())
		CreateKey();

	m_prKey->SetValue(valname,val,mode);	
}

bool	 xbCfgKey::ValueExists(const char *valname) const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::ValueExists() : not linked to a config key !");
	if (m_keyStaying.IsValid() && !Exists())
		return false;
	return m_prKey->GetValue(valname,NULL);
}

bool	 xbCfgKey::Exists() const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::Exists() : not linked to a config key !");
	if (m_keyStaying.IsEmpty())
		return true;
	xbString keyStaying;
	xbRefPtr<IxbCfgKey> prKey = m_prKey->GetKey(m_keyStaying, &keyStaying);
	if (prKey.IsEmpty())
		return false;
	((xbCfgKey*)this)->m_prKey = prKey;
	((xbCfgKey*)this)->m_keyStaying = keyStaying;
	return keyStaying.IsEmpty();
}

xbString xbCfgKey::GetName() const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::GetName() : not linked to a config key !");
	
	if (m_keyStaying.IsValid())
		return ::xbSubPathEnd(m_keyStaying);

	return m_prKey->GetName();
}

/*
xbString xbCfgKey::GetPath() const
{
	if (m_prKey.IsEmpty()) xbThrowEsys("xbCfgKey::GetPath() : not linked to a config key !");
	return m_prKey->GetPath();
}

xbCfgRoot	xbCfgKey::GetRoot() const
{
	if (m_prKey.IsEmpty()) xbThrowEsys("xbCfgKey::GetRootKey() : not linked to a config key !");
	return m_prKey->GetRoot();
}
*/

bool	xbCfgKey::DeleteValue(const char *valname,xbOpMode opMode)
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::DeleteValue() : not linked to a config key !");
	if (m_keyStaying.IsValid() && !Exists()) 
	{
		if (opMode == xbOp_Required)
			xbThrowEsys("xbCfgKey::DeleteValue() : config key doesn't exist !");
		return false;
	}
	return m_prKey->DeleteValue(valname,xbOp_Required);
}

bool	xbCfgKey::DeleteDefaultValue(xbOpMode opMode)
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::DeleteDefaultValue() : not linked to a config key !");
	if (m_keyStaying.IsValid() && !Exists()) 
	{
		if (opMode == xbOp_Required)
			xbThrowEsys("xbCfgKey::DeleteDefaultValue() : config key doesn't exist !");
		return false;
	}
	return m_prKey->DeleteValue(NULL,opMode);
}

bool	xbCfgKey::DeleteKey(bool bRecurse,xbOpMode opMode)
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::DeleteKey() : not linked to a config key !");
	
	if (m_keyStaying.IsValid() && !Exists()) 
	{
		if (opMode == xbOp_Required)
			xbThrowEsys("xbCfgKey::DeleteKey() : config key doesn't exist !");
		return false;
	}

	return m_prKey->Delete(bRecurse,opMode);
}

xbCfgKey	&xbCfgKey::CreateKey()
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::CreateKey() : not linked to a config key !");
	if (m_keyStaying.IsEmpty()) 
		return *this;
	xbRefPtr<IxbCfgKey> prKey = m_prKey->CreateKey(m_keyStaying);
	if (prKey.IsEmpty())
		xbThrowEsysFmt("xbCfgKey::CreateKey() : config key '%s' creation failed !",(LPCSTR)m_keyStaying);

	m_prKey = prKey;
	m_keyStaying.Empty();

	return *this;
}

bool xbCfgKey::DeleteKey(const char *keyname,bool bRecurse,xbOpMode opMode)
{
	return xbCfgKey(*this,keyname).DeleteKey(bRecurse,opMode);
}

IxbCfgKey *xbCfgKey::getKey(xbString *pKeyStaying) const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::CreateKey() : not linked to a config key !");

	if (!pKeyStaying)
		((xbCfgKey*)this)->CreateKey();
	else
		*pKeyStaying = m_keyStaying;

	return m_prKey;
}

IxbCfgKey *xbCfgKey::getKey(const char *pName,xbString *pKeyStaying) const
{
	if (m_prKey.IsEmpty()) 
		xbThrowEsys("xbCfgKey::CreateKey() : not linked to a config key !");

	if (m_keyStaying.IsValid())
	{
		if (pKeyStaying)
		{
			*pKeyStaying = m_keyStaying+"/"+xbString(pName);
			return m_prKey;
		}
		((xbCfgKey*)this)->CreateKey();
	}
	return m_prKey->GetKey(pName,pKeyStaying);
}

xbCfgEnumKey::xbCfgEnumKey(xbCfgKey &key)
{
	Init(key);
}

xbCfgEnumKey::xbCfgEnumKey(IxbCfgKey *pKey)
{
	m_prKey = pKey;
	m_pos = 0;
}

void xbCfgEnumKey::Init(xbCfgKey &key)
{
	m_prKey = key.getKey(NULL);
	m_pos = 0;
}

bool	xbCfgEnumKey::Next(xbString *pKeyName)
{
	if (m_prKey.IsEmpty()) 
		return false;

	return m_prKey->EnumKey(m_pos,pKeyName,NULL);
}

bool	xbCfgEnumKey::Next(xbCfgKey *pSubKey)
{
	if (m_prKey.IsEmpty()) 
		return false;

	xbRefPtr<IxbCfgKey> prSubKey;
	if (!m_prKey->EnumKey(m_pos,NULL,&prSubKey))
		return false;
	if (pSubKey)
		pSubKey->Init(prSubKey);
	return true;
}

bool	xbCfgEnumKey::Next()
{
	if (m_prKey.IsEmpty()) 
		return false;

	return m_prKey->EnumKey(m_pos,NULL,NULL);
}

void	xbCfgEnumKey::Reset()
{
	m_pos = 0;
}

void	xbCfgEnumKey::Close()
{
	m_prKey.Empty();
	m_pos = 0;
}

xbCfgEnumKey::~xbCfgEnumKey()
{
	eProtect(Close());
}

xbCfgEnumValue::xbCfgEnumValue(xbCfgKey &key)
{
	Init(key);
}

void xbCfgEnumValue::Init(xbCfgKey &key)
{
	m_prKey = key.getKey(NULL);
	m_pos = 0;
}

bool	xbCfgEnumValue::Next(xbString *pValName)
{
	if (m_prKey.IsEmpty())
		return false;
	return m_prKey->EnumValue(m_pos,pValName,NULL);
}

bool	xbCfgEnumValue::Next(xbString *pValName,xbVariant *pValue)
{
	if (m_prKey.IsEmpty())
		return false;
	return m_prKey->EnumValue(m_pos,pValName,pValue);
}

bool	xbCfgEnumValue::Next()
{
	if (m_prKey.IsEmpty())
		return false;
	return m_prKey->EnumValue(m_pos,NULL,NULL);
}

void	xbCfgEnumValue::Reset()
{
	m_pos = 0;
}

void	xbCfgEnumValue::Close()
{
	m_prKey.Empty();
	m_pos = 0;
}

xbCfgEnumValue::~xbCfgEnumValue()
{
	eProtect(Close());
}

void xbCfgPath::GetAbsPath(xbCfgEngine *pEngine,xbCfgRoot *pRoot,xbString *pPath,xbSectLock *pLckEngine) const
{
//	CheckReady();
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);
	xbString	path(m_path);
	xbCfgRoot	root(m_root);
	lck.Unlock();

	for(;;)
	{
		if (m_pCfgPath)
		{
			xbString	parentPath;
			m_pCfgPath->GetAbsPath(pEngine,&root,&parentPath,pLckEngine);
			if (parentPath.IsValid()) 
			{
				if (path.IsValid()) 
					path = parentPath+"/"+path;
				else
					path = parentPath;
			}
			break;
		}
		if (root != xbCfgExtern)
			break;
		root = xbCfgRoot(-m_id);
		if (m_externFilePath.IsValid())
		{
			path = m_externFileSubPath;
			break;
		}

		int pos = path.IndexOf("//",1);
//			if (pos <= 0)
//				xbThrowEsys("xbCfgPath("+GetName()+")::GetAbsPath() : invalid path("+path+") for external config file, syntax: configpath//");
		xbString sfile;
		if (pos < 0)
		{
			sfile = path;
			path.Empty();
		}
		else
		{
			sfile = path.Left(pos);
			path = path.Mid(pos+1);
		}
		pEngine->cfgFileOpen(root,sfile,pLckEngine);

		lck.Lock(&m_sectData);
		((xbCfgPath*)this)->m_externFilePath = path;
		((xbCfgPath*)this)->m_externFileSubPath = path;
		lck.Unlock();
		break;
	}

	if (pRoot) 
		*pRoot = root;
	if (pPath) 
		*pPath = path;
}

xbString xbCfgPath::GetPath() const
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	return m_path;
}

void xbCfgPath::SetPath(const xbString &path)
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	m_path = path;
}

xbCfgRoot	xbCfgPath::GetRoot() const
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	if (m_pCfgPath) 
		return *m_pCfgPath;
	return m_root;
}

void xbCfgPath::SetRoot(xbCfgRoot root)
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	if (int(root) < 0)
		m_pCfgPath = g_xbCfgEngine.GetCfgPath(root);
	else
		m_pCfgPath = NULL;
	m_root = root;
}

void xbCfgPath::SetRoot(xbCfgPath *pCfgPathRoot)
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	m_pCfgPath = pCfgPathRoot;
	m_root = pCfgPathRoot->GetRoot();
}

void xbCfgEngine::GetPath(xbCfgRoot root,LPCSTR pKey,xbCfgRoot *pRoot,xbString *pStr) const
{
	if (root >= 0)
	{
		if (pRoot)
			*pRoot = root;
		if (pStr)
		{
			if (pKey && *pKey)
				*pStr = pKey;
			else
				pStr->Empty();
		}
		return;
	}
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);
	int id = -int(root);
	if (id <= 0 || id > m_arrPath.Length)
		xbThrowEsys("xbCfgEngine::GetPath(root="+xbString(id)+") : bad config root id");

	xbCfgPath *pPath = m_arrPath.unsafeGetAt(id-1);
	if (!pPath)
		xbThrowEsys("xbCfgEngine::GetPath(root="+xbString(id)+") : bad config root id");

	xbString path;
	pPath->GetAbsPath((xbCfgEngine*)this,&root,&path,&lck);
	lck.Unlock();

	if (pRoot)
		*pRoot = root;
	if (pStr)
	{
		if (pKey && *pKey)
			*pStr = path+"/"+xbString(pKey);
		else
			*pStr = path;
	}
}

xbCfgPath *xbCfgEngine::GetCfgPath(xbCfgRoot root)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);
	int id = -int(root);
	if (id <= 0 || id > m_arrPath.Length
		|| !m_arrPath.unsafeGetAt(id-1))
		xbThrowEsys("xbCfgEngine::GetCfgPath(root="+xbString(id)+") : bad config root id");
	xbCfgPath *pPath = m_arrPath.unsafeGetAt(id-1);
	if (!pPath)
		xbThrow xbEappCfg("Config root id "+xbString(int(root))+" is invalid !");
	return pPath;
}

int xbCfgEngine::Add(xbCfgPath *pCfgPath)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);

	int no = 0;
	while(no < m_arrPath.Length)
	{
		if (!m_arrPath.unsafeGetAt(no))
		{
			m_arrPath.unsafeSetAt(no,pCfgPath);
			return no+1;
		}
		no++;
	}
	return m_arrPath.Add(pCfgPath)+1;
}

void xbCfgEngine::Remove(int id)
{
	xbLock lckStart(this);
	cfgFileClose(xbCfgRoot(-id));
	{
		xbSectLock lck(&m_sectData);
		m_arrPath.SetAt(id-1,NULL);
	}
}

void xbCfgEngine::InitCache()
{
	xbSectLock lck(&m_sectCache);

	m_arrLcid.Empty();
	m_arrLcidByCode.Empty();
	m_utf8strict = 0;
}
bool xbCfgEngine::utf8strict()
{
	if (m_utf8strict)
		return m_utf8strict > 0;
	m_utf8strict = GetSettingBoolToNum("utf8strict");
	return m_utf8strict > 0;
}

bool xbCfgEngine::GetRoot(const xbString &rootName,xbCfgRoot *pRoot,xbOpMode opm)
{
	xbLock lckStart(this);
	if (rootName.CompareNoCase("common") == 0)
	{
		if (pRoot)
			*pRoot = xbCfgCommon;
		return true;
	}
	if (rootName.CompareNoCase("appli") == 0)
	{
		if (pRoot)
			*pRoot = xbCfgAppli;
		return true;
	}
	if (rootName.CompareNoCase("commonUser") == 0)
	{
		if (pRoot)
			*pRoot = xbCfgCommonUser;
		return true;
	}
	if (rootName.CompareNoCase("appliUser") == 0)
	{
		if (pRoot)
			*pRoot = xbCfgAppliUser;
		return true;
	}
	if (rootName.CompareNoCase("extern") == 0)
	{
		if (pRoot)
			*pRoot = xbCfgExtern;
		return true;
	}

	{
		xbSectLock lck(&m_sectData);
		xbCfgPath** pptr = m_arrPath.Begin();
		xbCfgPath** ppend = m_arrPath.End();
		while(pptr < ppend)
		{
			if (*pptr && (*pptr)->m_pName && xbStricmp((*pptr)->m_pName,rootName) == 0)
			{
				if (pRoot)
					*pRoot = xbCfgRoot(-(*pptr)->m_id);
				return true;
			}
			pptr++;
		}
	}
	if (opm == xbOp_Required)
		xbThrow xbEappCfg("Config root '"+rootName+"' is unknown !");
	return false;
}

xbString xbCfgEngine::GetRootName(xbCfgRoot root)
{
	switch(root)
	{
	case xbCfgCommon: return "common";
	case xbCfgAppli: return "appli";
	case xbCfgCommonUser: return "commonUser";
	case xbCfgAppliUser: return "appliUser";
	case xbCfgExtern: return "extern";
	default:
		break;
	}
	return GetCfgPath(root)->GetName();
}

bool xbCfgEngine::GetSettingKey(LPCSTR pValName,xbCfgKey &key)
{
	key.Init(xbCfgAppli,"setting");
	if (key.ValueExists(pValName))
		return true;
	key.Init(xbCfgCommon,"setting");
	if (key.ValueExists(pValName))
		return true;
	return false;
}

bool xbCfgEngine::GetSettingBool(LPCSTR pValName)
{
	if (!isNowStarted())
		return false;

	eProtectBegin();
	xbCfgKey key;
	if (!GetSettingKey(pValName,key))
		return false;
	return key.Value[pValName].AsBool;
	eProtectEndTrace();
	return  false;
}

int xbCfgEngine::GetSettingBoolToNum(LPCSTR pValName)
{
	if (!isConfigReady())
		return 0;

	eProtectBegin();
	xbCfgKey key;
	if (!GetSettingKey(pValName,key))
		return -1;
	return key.Value[pValName].AsBool?1:-1;
	eProtectEndTrace();
	return  0;
}

xbString xbCfgEngine::GetSettingString(LPCSTR pValName)
{
	if (!isNowStarted())
		return xbString();

	eProtectBegin();
	xbCfgKey key;
	if (!GetSettingKey(pValName,key))
		return xbString();
	
	return key.Value[pValName].AsString;
	eProtectEndTrace();
	return xbString();
}

long xbCfgEngine::GetSettingLong(LPCSTR pValName)
{
	if (!isNowStarted())
		return 0;

	eProtectBegin();
	xbCfgKey key;
	if (!GetSettingKey(pValName,key))
		return 0;
	
	return key.Value[pValName].AsLong;
	eProtectEndTrace();
	return 0;
}

ulong xbCfgEngine::GetSettingUlong(LPCSTR pValName)
{
	if (!isNowStarted())
		return 0;

	eProtectBegin();
	xbCfgKey key;
	if (!GetSettingKey(pValName,key))
		return 0;
	
	return key.Value[pValName].AsUlong;
	eProtectEndTrace();
	return 0;
}

xbVariant xbCfgEngine::GetSettingValue(LPCSTR pValName)
{
	if (!isNowStarted())
		return xbVariant();
	xbCfgKey key;
	if (!GetSettingKey(pValName,key))
		return xbVariant();

	return key.Value[pValName];
}

void		xbCfgEngine::AddPlugTypeCb(IxbHotPlugTypeCb *pCb)
{
	xbLock lckStart(this);

	CbAddLock	lckCbAdd(this);

	xbSectLock lck(&m_sectData);
	addCb(pCb);
}

void xbCfgEngine::addCb(IxbHotPlugTypeCb *pCb)
{
	xbRdbgAssert(m_nLockCbAdd > 0);
	xbObsPtr<IxbHotPlugTypeCb>	**ptr = m_arrCb.Begin();
	xbObsPtr<IxbHotPlugTypeCb>	**pend = m_arrCb.End();
	xbObsPtr<IxbHotPlugTypeCb>	**pFree = NULL;
	while(ptr < pend)
	{
		if (!pFree && (*ptr)->IsEmpty()) 
			pFree = ptr;
		if ((**ptr) == pCb) return;
		ptr++;
	}
	if (pFree)
	{
		(**pFree) = pCb;
		return;
	}
	m_arrCb.Add(xbDnew xbObsPtr<IxbHotPlugTypeCb>(pCb));
}

void xbCfgEngine::lockCbCall() const
{
	if (!isNowStarted()) 
		xbThrow xbEsys("xbHotPlugType::lockCbCall() : destroyed plug");
	xbSectLock lck(&m_sectData);
	int cnt = 0;
	while(m_nLockCbAdd > 0)
	{
		if (++cnt > xbHOTPLUGCB_TRYCNT) 
			xbThrow xbEsys(xbHR_TIMEOUT,"xbHotPlugType::lockCbCall() : timeout, can't call callbacks !");
		lck.Unlock();
		if (xbDoEvents(NULL)) 
		{
			if (!isNowStarted()) 
				xbThrow xbEsys("xbHotPlugType::lockCbCall() : destroyed plug");
			lck.Lock(&m_sectData);
			if (m_nLockCbAdd <= 0) 
				break;
			lck.Unlock();
		}
		SleepEx(xbHOTPLUGCB_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
		if (!isNowStarted()) 
			xbThrow xbEsys("xbHotPlugType::lockCbCall() : destroyed plug");
		lck.Lock(&m_sectData);
	}
	m_nLockCbCall++;
}

void xbCfgEngine::unlockCbCall() const
{
	xbSectLock lck(&m_sectData);
	m_nLockCbCall--;
	xbAssert(m_nLockCbCall >= 0);
}

void xbCfgEngine::lockCbAdd() const
{
//	if (!IsNowStarted()) xbThrow xbEsys("xbHotPlugType::lockCbAdd() : destroyed plug");
	xbSectLock lck(&m_sectData);
	int cnt = 0;
	while(m_nLockCbCall > 0)
	{
		if (++cnt > xbHOTPLUGCB_TRYCNT) 
			xbThrow xbEsys(xbHR_TIMEOUT,"xbHotPlugType::lockCbCall() : timeout, can't call callbacks !");
		lck.Unlock();
		if (xbDoEvents(NULL)) 
		{
//			if (!IsNowStarted()) xbThrow xbEsys("xbHotPlugType::lockCbAdd() : destroyed plug");
			lck.Lock(&m_sectData);
			if (m_nLockCbCall <= 0) 
				break;
			lck.Unlock();
		}
		SleepEx(xbHOTPLUGCB_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
//		if (!IsNowStarted()) xbThrow xbEsys("xbHotPlugType::lockCbAdd() : destroyed plug");
		lck.Lock(&m_sectData);
	}
	m_nLockCbAdd++;
}

void xbCfgEngine::unlockCbAdd() const
{
	xbSectLock lck(&m_sectData);
	m_nLockCbAdd--;
	xbAssert(m_nLockCbAdd >= 0);
}

void xbCfgEngine::onStart() 
{
	m_utf8strict = 0;
	m_th.Create();
}

bool xbCfgEngine::onStop(bool) 
{
	bool bRet = m_th.AskDestroy("xbCfgEngine stopping");

	CbAddLock	lckCbAdd(this);
	xbSectLock lck(&m_sectData);

	m_arrCfgFile.Clear();
	m_arrPath.Clear(); 
	m_arrLcidByCode.Empty();
	m_arrLcid.Empty();
	m_arrCb.Clear();
	m_arrCfgCb.Clear();

	return bRet;
}

xbPlugUpdateCaps	xbCfgEngine::onPlugTypeGetPluginCaps(IxbHotPlugin *pPlugin) //called before internal update to see if update is possible and when.
{
	CbCallLock	lckCbCall(this);
	int ret = int(xbPlugUpdateNormal);

	for (int no = 0; no < m_arrCb.Length; no++)
	{
		xbRefPtr<IxbHotPlugTypeCb> ptr;
		if (!m_arrCb.unsafeGetAt(no)->GetPtr(&ptr)) 
			continue;

		int subret = -1;
		eProtectTrace( subret = ptr->OnGetPluginCaps(pPlugin) );

		if (subret > ret) 
			ret = subret;
	}

	return xbPlugUpdateCaps(ret);
}

void				xbCfgEngine::onPlugTypeUnloadPluginAsked(IxbHotPlugin*pPlugin,bool bForce) //called by module message or OnStartBinding
{
	CbCallLock	lckCbCall(this);
	for (int no = 0; no < m_arrCb.Length; no++)
	{
		xbRefPtr<IxbHotPlugTypeCb> ptr;
		if (!m_arrCb.unsafeGetAt(no)->GetPtr(&ptr)) 
			continue;

		eProtectTrace(ptr->OnUnloadPluginAsked(pPlugin,bForce));
	}
}

void				xbCfgEngine::onPlugTypeLoadPluginAsked(IxbHotPlugin*pPlugin) //called by module message or OnStopBinding / OnObjectAvailable
{
	CbCallLock	lckCbCall(this);
	for (int no = 0; no < m_arrCb.Length; no++)
	{
		xbRefPtr<IxbHotPlugTypeCb> ptr;
		if (!m_arrCb.unsafeGetAt(no)->GetPtr(&ptr)) 
			continue;

		eProtectTrace(ptr->OnLoadPluginAsked(pPlugin));
	}
}

void				xbCfgEngine::onPlugTypeProgress(IxbHotPlugin*pPlugin,double progress,
								xbPlugStatus status, const xbString &statusText)
{
	CbCallLock	lckCbCall(this);
	for (int no = 0; no < m_arrCb.Length; no++)
	{
		xbRefPtr<IxbHotPlugTypeCb> ptr;
		if (!m_arrCb.unsafeGetAt(no)->GetPtr(&ptr)) 
			continue;

		eProtectTrace( ptr->OnProgress(pPlugin,progress,status,statusText) );
	}
}

xbPlugUpdateCaps	xbCfgEngine::onPlugTypeError(IxbHotPlugin*pPlugin,bool bCanRetry, HRESULT hr, const xbString &errorText)
{
	CbCallLock	lckCbCall(this);
	int ret = int(xbPlugUpdateNormal);

	for (int no = 0; no < m_arrCb.Length; no++)
	{
		xbRefPtr<IxbHotPlugTypeCb> ptr;
		if (!m_arrCb.unsafeGetAt(no)->GetPtr(&ptr)) continue;

		int subret = -1;
		eProtectTrace( subret = ptr->OnError(pPlugin,bCanRetry,hr,errorText) );

		if (subret > ret) 
			ret = subret;
	}

	return xbPlugUpdateCaps(ret);
}

void				xbCfgEngine::onPlugTypeReloadConfig(sIxbHotPlugType *pPlugType)
{
	CbCallLock	lckCbCall(this);
	for (int no = 0; no < m_arrCb.Length; no++)
	{
		xbRefPtr<IxbHotPlugTypeCb> ptr;
		if (!m_arrCb.unsafeGetAt(no)->GetPtr(&ptr)) 
			continue;

		eProtectTrace(ptr->OnReloadConfig(pPlugType));
	}
}

void xbCfgChangedCb::Init(xbCfgRoot mk,const xbString &cfgPath)
{
	if (mk == m_cfgRoot && cfgPath == m_cfgPath)
		return;
	if (m_prCBobj.IsValid()) 
		Destroy();
	m_cfgPath = cfgPath;
	m_cfgRoot = mk;
}

void xbCfgChangedCb::Create()
{
	if (m_prCBobj.IsValid()) 
		return;

	xbCfgRoot root;
	xbString path;
	g_xbCfgEngine.GetPath(m_cfgRoot,m_cfgPath,&root,&path);

	CBobj *pPlugObj = xbDnew CBobj(this,root,path);
	try
	{
		pPlugObj->Create();
		
		m_prCBobj = pPlugObj;
		
		pPlugObj->Release();
	}
	catch(...)
	{
		pPlugObj->Release();
		throw;
	}

}

void xbCfgChangedCb::Destroy()
{
	if (m_prCBobj.IsEmpty()) 
		return;
	m_prCBobj->Destroy();
	m_prCBobj.Empty();
}

xbCfgChangedCb::operator IxbCfgChangedCb*()
{
	if (!IsValid())
		xbThrow xbEsys("xbCfgChangedCb::operator IxbCfgChangedCb*() called without callback or config root");
	Create();
	return m_prCBobj;
}

void xbCfgChangedCb::CBobj::OnConfigChanged(xbCfgRoot root,xbCfgKeyChangesInfo *pInfo)
{
	xbString cfgPath;
	if (!pInfo->Match(m_cfgRoot,m_cfgPath,root,&cfgPath))
		return;
	xbSectLock lck(&m_sectPtr);
	if (m_bDestruction) return;
	m_pParent->onConfigChanged(root,pInfo,cfgPath);
}

void xbCfgChangedCb::CBobj::OnConfigErrors(xbCfgRoot root,xbCfgKeyErrorsInfo *pInfo)
{
	xbString cfgPath;
	if (!pInfo->Match(m_cfgRoot,m_cfgPath,root,&cfgPath))
		return;
	xbSectLock lck(&m_sectPtr);
	if (m_bDestruction) return;
	m_pParent->onConfigError(root,pInfo,cfgPath);
}

void		xbCfgEngine::AddConfigChangedCb(IxbCfgChangedCb *pCb)
{
	xbLock lckStart(this);

	CbAddLock	lckCbAdd(this);

	xbSectLock lck(&m_sectData);
	addCfgCb(pCb);
}

void xbCfgEngine::addCfgCb(IxbCfgChangedCb *pCb)
{
	xbRdbgAssert(m_nLockCbAdd > 0);
	xbObsPtr<IxbCfgChangedCb>	**ptr = m_arrCfgCb.Begin();
	xbObsPtr<IxbCfgChangedCb>	**pend = m_arrCfgCb.End();
	xbObsPtr<IxbCfgChangedCb>	**pFree = NULL;
	while(ptr < pend)
	{
		if (!pFree && (*ptr)->IsEmpty()) 
			pFree = ptr;
		if ((**ptr) == pCb) 
			return;
		ptr++;
	}
	if (pFree)
	{
		(**pFree) = pCb;
		return;
	}
	m_arrCfgCb.Add(xbDnew xbObsPtr<IxbCfgChangedCb>(pCb));
}

void xbCfgEngine::onConfigChanged(xbCfgRoot root,xbCfgKeyChangesInfo *pInfo)
{
	CbCallLock	lckCbCall(this);
	for (int no = 0; no < m_arrCfgCb.Length; no++)
	{
		xbRefPtr<IxbCfgChangedCb> ptr;
		if (!m_arrCfgCb.unsafeGetAt(no)->GetPtr(&ptr)) 
			continue;

		eProtectTrace(ptr->OnConfigChanged(root,pInfo));
	}
}

void xbCfgEngine::onConfigErrors(xbCfgRoot root,xbCfgKeyErrorsInfo *pInfo)
{
	CbCallLock	lckCbCall(this);
	for (int no = 0; no < m_arrCfgCb.Length; no++)
	{
		xbRefPtr<IxbCfgChangedCb> ptr;
		if (!m_arrCfgCb.unsafeGetAt(no)->GetPtr(&ptr)) 
			continue;

		eProtectTrace(ptr->OnConfigErrors(root,pInfo));
	}
}

DWORD	xbCfgEngine::Thread::onThreadProc(sIxbThread *pTh)
{
	ulong cFileChanged=0;
	xbArray<HANDLE> arrEv;
	xbArray< xbRefPtr<IxbCfgKeyRoot> > arrFile;
	eProtectBegin();
	for(;;)
	{
		if (IsCloseAskedInt())
			break;

		if (arrEv.IsEmpty() || cFileChanged != m_pParent->m_cFileChanged)
			cFileChanged = eventArrayMake(arrEv,arrFile);

		int waitRet = ::xbWait(m_pParent->m_bWriteInProgress?xbXDCCFG_WRITETIMEOUT:xbXDCCFG_WAITTIMEOUT,arrEv.Begin(),arrEv.Length);
		if (waitRet > 1)
		{
			eProtectTrace(m_pParent->cfgFileCheckLoadWrite(pTh,arrFile.GetAt(waitRet-2)););
			continue;
		}

		eProtectTrace(m_pParent->cfgFileCheckLoadWrite(pTh););
	}
	eProtectEndTrace();
	eProtectTrace(m_pParent->cfgFileWriteEnd(pTh));
	return 0;
}

void xbCfgEngine::AskConfigUpdate() const
{
	((xbCfgEngine*)this)->m_th.wakeUp();
}

bool xbCfgEngine::cfgFileEnum(int &idx,xbRefPtr<IxbCfgKeyRoot> *pprKeyRoot,xbCfgRoot *pRoot,bool bRemove)
{
	if (pprKeyRoot)
		pprKeyRoot->Empty(); //To destroy pointed object if necessary out of lock scope

	xbSectLock lck(&m_sectData);
	while(idx < m_arrCfgFile.Length)
	{
		cfgFileEntry &entry = m_arrCfgFile.GetAt(idx++);
		if (entry.m_prKeyRoot.IsEmpty())
			continue;
		*pprKeyRoot = entry.m_prKeyRoot;
		if (bRemove)
		{
			entry.m_prKeyRoot.Empty();
			m_cFileChanged++;
			m_th.wakeUp();
		}
		if (pRoot)
			*pRoot = entry.m_root;
		return true;
	}
	return false;
}

bool xbCfgEngine::cfgFileGet(xbCfgRoot root,xbRefPtr<IxbCfgKeyRoot> *pprKeyRoot,xbCfgRoot *pRoot,bool bRemove)
{
	if (pprKeyRoot)
		pprKeyRoot->Empty(); //To destroy pointed object if necessary out of lock scope

	xbSectLock lck(&m_sectData);
	int idx = -((int)root) + _xbCfgIndexLimit;
	if (idx >= m_arrCfgFile.Length)
		return false;

	cfgFileEntry &entry = m_arrCfgFile.GetAt(idx);
	if (entry.m_prKeyRoot.IsEmpty())
		return false;
	*pprKeyRoot = entry.m_prKeyRoot;
	if (bRemove)
	{
		entry.m_prKeyRoot.Empty();
		m_cFileChanged++;
		m_th.wakeUp();
	}
	if (pRoot)
		*pRoot = entry.m_root;
	return true;
}

void xbCfgEngine::cfgFileCheckLoadWrite(sIxbThread *pTh)
{
	m_bWriteInProgress = false;
	bool bWriteInProgress = false;
	int idx = 0;
	xbRefPtr<IxbCfgKeyRoot> prKeyRoot;
	xbCfgRoot root;
	while(cfgFileEnum(idx,&prKeyRoot,&root,false)
		&& !m_th.shouldStop())
	{
		xbCfgKeyChangesInfo info;
		prKeyRoot->CheckLoadWrite(&info);
		
		if (m_th.shouldStop())
			break;
		
		if (info.HasChanges())
			onConfigChanged(root,&info);
		if (info.m_errInfo.HasErrors())
			onConfigErrors(root,&info.m_errInfo);
		if (info.HasWrites())
			bWriteInProgress = true;
	}

	if (bWriteInProgress)
		m_bWriteInProgress = true;
}

void xbCfgEngine::cfgFileCheckLoadWrite(sIxbThread *pTh, IxbCfgKeyRoot *pCfgFile)
{
	pCfgFile->FileChangeNotifRestart();

	xbCfgKeyChangesInfo info;
	pCfgFile->CheckLoadWrite(&info);
	
	if (m_th.shouldStop())
		return;

	xbCfgRoot root = pCfgFile->GetCfgRoot();
	if (info.HasChanges())
		onConfigChanged(root,&info);
	if (info.m_errInfo.HasErrors())
		onConfigErrors(root,&info.m_errInfo);
	if (info.HasWrites())
		m_bWriteInProgress = true;
}

void xbCfgEngine::cfgFileWriteEnd(sIxbThread *pTh)
{
	int idx = 0;
	xbSectLock lck(&m_sectData);
	xbRefPtr<IxbCfgKeyRoot> prKeyRoot;
	while(cfgFileEnum(idx,&prKeyRoot,NULL,true))
	{
		prKeyRoot->Destroy();
		lck.Unlock();
		prKeyRoot.Empty();
	}
}

void xbCfgEngine::cfgFileOpen(xbCfgRoot root,LPCSTR pFilePath,xbSectLock *pLckEngine)
{
	xbSectLock _lck;
	if (!pLckEngine)
		pLckEngine = &_lck;
	if (!pLckEngine->IsLocked())
		pLckEngine->Lock(&m_sectData);

	int idx = -((int)root) + _xbCfgIndexLimit;
	if (idx < m_arrCfgFile.Length)
	{
		cfgFileEntry &entry = m_arrCfgFile.GetAt(idx);
		if (entry.m_prKeyRoot.IsValid())
			return;
	}
	//pLckEngine->Unlock();

	xbString filePath;
	if (pFilePath && *pFilePath)
		filePath = pFilePath;
	else
		filePath = cfgFileDefaultPathGet(root);

	xbCfgKeyChangesInfo info;
	info.m_bLogChanges = false;
	xbConfigFileInit fileInit(this,root, GetExpandedString(::xbPathSlash(filePath)), &info);

	xbRefPtr<IxbCfgKeyRoot> prKeyRoot;
	xbConfigFile::CreateObject(&prKeyRoot,&fileInit);

	//pLckEngine->Lock(&m_sectData);
	if (idx < m_arrCfgFile.Length)
	{
		cfgFileEntry &entry = m_arrCfgFile.GetAt(idx);
		if (entry.m_prKeyRoot.IsValid())
			return;
	}
	else
	{
		m_arrCfgFile.Length = idx+1;
	}
	cfgFileEntry &entry = m_arrCfgFile.GetAt(idx);
	entry.m_prKeyRoot = prKeyRoot;
	entry.m_root = root;
	m_cFileChanged++;
	m_th.wakeUp();

	if (info.m_errInfo.HasErrors())
	{
		pLckEngine->Unlock();
		onConfigErrors(root,&info.m_errInfo);
//		pLckEngine->Lock(&m_sectData);
	}
}

void xbCfgEngine::cfgFileClose(xbCfgRoot root)
{
	xbRefPtr<IxbCfgKeyRoot> prKeyRoot;
	if (!cfgFileGet(root,&prKeyRoot,NULL,true))
		return;
	prKeyRoot->Destroy();
}

xbString xbCfgEngine::cfgFileDefaultPathGet(xbCfgRoot root)
{
	switch(root)
	{
	case xbCfgCommon: 
		return cfgFileLibPath("common");
	case xbCfgAppli:
		return cfgFileLibPath(::xbGetFileName());
	case xbCfgCommonUser:
		return cfgFileUserPath("common");
	case xbCfgAppliUser:
		return cfgFileUserPath(::xbGetFileName());
	}

	xbThrowEsys("xbCfgEngine::cfgFileDefaultPathGet("+xbString((int)root)+") : not linked to a config key !");
	return xbString();
}

xbString xbCfgEngine::cfgFileLibPath(xbString fname)
{
	return xbString(xbXDCLIB_PATH xbPath_SLASH xbXDCCONFIG_DIR xbPath_SLASH)+fname+xbXDCCFG_EXT;
}

xbString xbCfgEngine::cfgFileUserPath(xbString fname)
{
	return xbString(xbXDCAPP_PATH xbPath_SLASH xbXDCCONFIG_DIR xbPath_SLASH)+fname+xbXDCCFG_EXT;
}

xbRefPtr<IxbCfgKeyRoot> xbCfgEngine::getKeyRoot(xbCfgRoot root)
{
	xbLock lckStart(this);

	if (isInConfigLoad())
		xbThrowEsys("xbCfgEngine::getKeyRoot() : recursive call !");

	xbSectLock lck(&m_sectData);
	int idx = -((int)root) + _xbCfgIndexLimit;
	if (idx >= m_arrCfgFile.Length)
	{
		cfgFileOpen(root,NULL,&lck);
		if (!lck.IsLocked())
			lck.Lock(&m_sectData);
	}

	cfgFileEntry &entry = m_arrCfgFile.GetAt(idx);
	if (entry.m_prKeyRoot.IsEmpty())
	{
		cfgFileOpen(root,NULL,&lck);
		if (!lck.IsLocked())
			lck.Lock(&m_sectData);
	}
	return entry.m_prKeyRoot;
}

xbRefPtr<IxbCfgKey> xbCfgEngine::getKey(xbCfgRoot root,const xbString &keyname,xbString *pKeyStaying)
{
	return getKeyRoot(root)->GetKey(keyname,pKeyStaying);
}

ulong xbCfgEngine::Thread::eventArrayMake(xbArray<HANDLE> &arrEv,xbArray< xbRefPtr<IxbCfgKeyRoot> > &arrFile)
{
	if (arrEv.Length < 1)
		arrEv.Length = 1;
	arrEv.SetAt(0,m_eventPtr->GetHandle());
	int idx=0;
	ulong ret = m_pParent->cfgFileEventArrayMake(idx,arrEv,arrFile);
	if (idx+1 < arrEv.Length)
		arrEv.Length = idx+1;
	if (idx < arrFile.Length)
		arrFile.Length = idx;

	return ret;
}

ulong xbCfgEngine::cfgFileEventArrayMake(int &idx,xbArray<HANDLE> &arrEv,xbArray< xbRefPtr<IxbCfgKeyRoot> > &arrFile)
{
	ulong ret;
	{
		xbSectLock lck(&m_sectData);
		ret = m_cFileChanged;
	}
	int cfgidx = 0;
	xbRefPtr<IxbCfgKeyRoot> prKeyRoot;
	while(cfgFileEnum(cfgidx,&prKeyRoot,NULL,false))
	{
		eProtectBegin();
		HANDLE hEv = prKeyRoot->FileChangeNotifStart();
		if (idx >= arrFile.Length)
			arrFile.Length=idx+1;
		if (idx+1 >= arrEv.Length)
			arrEv.Length=idx+2;
		arrFile.SetAt(idx,prKeyRoot);
		arrEv.SetAt(idx+1,hEv);
		idx++;
		eProtectEndTrace();
	}
	return ret;
}

void xbCfgEngine::onConfigFileInit(xbCfgRoot root,IxbCfgKeyRoot *pRoot)
{
	//TODO
	switch(root)
	{
	case xbCfgCommon:
		{
/*<xdcube>
	<exception help="Exception tracking options" defaultValue="..">
		<xbEsys stop="bool:true" trace="bool:true">
		</xbEsys>
		<exception:stop value="bool:true"/>
		<exception:trace>
			bool:true
		</exception:trace>
	</exception>
	<setting>
		<setting:PerfCounterBug value="long:1"/>
		<setting:Debug value="bool:true"/>
	</setting>
	<hotplug TraceLevel="long:0">
		<Socket.6>
			<xdsockws.dll enabled="bool:true">
				<ws priority="long:0" enabled="bool:true"/>
				<ssl priority="long:0" enabled="bool:true"/>
			</xdsockws.dll>
		</Socket.6>
	</hotplug>
</xdcube>*/
			xbRefPtr<IxbCfgKey> prEroot(pRoot->CreateKey("exception"));
#if defined(_DEBUG) || defined(_RELDBG)
			prEroot->SetValue(".stop",false);
#endif
			prEroot->SetValue(".log",false);
			prEroot->SetValue(".debug",false);

			xbRefPtr<IxbCfgKey> prSettRoot(pRoot->CreateKey("setting"));
			prSettRoot->SetValue("perfCounterBug",false,xbCfgVal_AsSectionValue);
			prSettRoot->SetValue("utf8strict",false,xbCfgVal_AsSectionValue);
			prSettRoot->SetValue("cppDebug",false,xbCfgVal_AsSectionValue);
			prSettRoot->SetValue("threadTrace",false,xbCfgVal_AsSectionValue);
			

			xbRefPtr<IxbCfgKey> prHotPlugRoot(pRoot->CreateKey("hotplug"));
			prHotPlugRoot->SetValue("traceLevel",0);

			xbRefPtr<IxbCfgKey> prColorRoot(pRoot->CreateKey("UI/color"));
//			prHotPlugRoot->SetValue("traceLevel",0);
		}
		break;
	case xbCfgAppli:
		{
			xbRefPtr<IxbCfgKey> prEroot(pRoot->CreateKey("exception"));
#if defined(_DEBUG) || defined(_RELDBG)
			prEroot->SetValue(".stop",false);
#endif
			prEroot->SetValue(".log",false);
			prEroot->SetValue(".debug",false);

			xbRefPtr<IxbCfgKey> prSettRoot(pRoot->CreateKey("setting"));
		}
	case xbCfgAppliUser:
		{
			xbRefPtr<IxbCfgKey> prColorRoot(pRoot->CreateKey("UI/color"));
		}
	default:
		break;
	}
}

bool xbCfgKeyErrorsInfo::Match(xbCfgRoot rootWatched,const xbString &pathWatched,xbCfgRoot root,xbString *pPath) const
{
	if (rootWatched != xbCfgUndefined && rootWatched != root)
		return false;

//	if (pathWatched.IsEmpty())
//		return false;

	if (pPath)
		*pPath = pathWatched;
	return true;
}

bool xbCfgKeyChangesInfo::Match(xbCfgRoot rootWatched,const xbString &pathWatched,xbCfgRoot root,xbString *pPath) const
{
	if (rootWatched != xbCfgUndefined && rootWatched != root)
		return false;

	if (pathWatched.IsEmpty())
	{
		if (pPath)
			pPath->Empty();
		return true;
	}

	xbString path(pathWatched);
	for(;;)
	{
		const xbString *pFnd = m_arrPathChanged.SortedFind(&path,::xbFindStringNC);
		if (pFnd)
		{
			if (pPath)
				*pPath = path;
			return true;
		}
		if (path.IsEmpty())
			break;
		
		::xbSubPathEnd(path,&path);
	}

	return false;
}

void xbCfgKeyChangesInfo::pathChanged(const xbString &path)
{
	if (!m_bLogChanges)
		return;

	int pos;
	xbString *pFnd = m_arrPathChanged.SortedFind(&path,::xbFindStringNC,&pos);
	xbRdbgAssert(pFnd == NULL);
	if (pFnd)
		return;
	m_arrPathChanged.Insert(pos,path);
}

bool xbCfgKeyChangesInfo::HasPathChanged(const xbString &pathWatched) const
{
	if (pathWatched.IsEmpty())
		return m_arrPathChanged.IsValid();
	xbString path(pathWatched);
	for(;;)
	{
		const xbString *pFnd = m_arrPathChanged.SortedFind(&path,::xbFindStringNC);
		if (pFnd)
			return true;

		if (path.IsEmpty())
			break;
		
		::xbSubPathEnd(path,&path);
	}

	return false;
}

#pragma warning(pop)
