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

#include "xbConfigFile.h"
#include "xbConfigFileParser.h"
#include "xbFileTools.h"
#include "xbMsg.h"

#pragma warning(push)
#pragma warning(disable: 4311)
void xbConfigNodeBase::onBeforeDestruction(bool /*bInDestroy*/)
{
}

int		xbConfigNode::keyFindCb(const xbRefPtr<xbConfigNodeBase> &prNode,const void *pStr)
{
	return prNode->CompareName((const char*)pStr);
}

int xbConfigNode::valueFindCb(ValueInfo* const &pTag,const void *pStr)
{
	return pTag->m_name.CompareNC((const char*)pStr);
}

int		xbConfigNode::keyOrderFindCb(xbConfigNodeBase* const &pNode,const void *_pKey)
{
	const xbConfigNodeBase *pKeyFnd = (const xbConfigNodeBase*)_pKey;
	if (pNode->GetConfigOrder() != pKeyFnd->GetConfigOrder())
	{
		if (pNode->GetConfigOrder() > pKeyFnd->GetConfigOrder())
			return 1;
		return -1;
	}
	int ret = pNode->CompareName(pKeyFnd->GetName());
	if (ret != 0)
		return ret;
	if (pNode != pKeyFnd)
	{
		if (pNode > pKeyFnd)
			return 1;
		return -1;
	}
	return 0;
}

int xbConfigNode::valueOrderFindCb(ValueInfo* const &pTag,const void *_pVal)
{
	const ValueInfo *pValFnd = (const ValueInfo*)_pVal;
	if (pTag->asTagConst() != pValFnd->asTagConst())
	{
		if (pTag->asTagConst())
			return -1;
		return 1;
	}
	if (pTag->m_order != pValFnd->m_order)
	{
		if (pTag->m_order > pValFnd->m_order)
			return 1;
		return -1;
	}
	int ret = pTag->m_name.CompareNC(pValFnd->m_name);
	if (ret != 0)
		return ret;
	if (pTag != pValFnd)
	{
		if (pTag > pValFnd)
			return 1;
		return -1;
	}
	return 0;
}

xbConfigNode::xbConfigNode()
{
	m_pParent = NULL;
	m_rights = xbCfgKeyRight_ALL;
	m_bWriteAsked = false;
}

xbConfigNode::~xbConfigNode() 
{
	eProtectTrace(Destroy());
}

void xbConfigNode::onBeforeDestruction(bool bInDestroy)
{
	eProtectBegin();
	xbArray< xbRefPtr<xbConfigNodeBase> > arrKey;
	{
		xbSectLock lck(&m_sectData);
		arrKey.Attach(&m_arrKey);
		m_arrKeyOrdered.Empty();
		m_arrValue.Empty();
		m_arrValueOrdered.Empty();
		m_pParent = NULL;
	}
	xbRefPtr<xbConfigNodeBase> *pptr = arrKey.Begin();
	xbRefPtr<xbConfigNodeBase> *ppend = arrKey.End();
	while(pptr < ppend)
	{
		(*pptr)->Destroy();
		pptr++;
	}
	eProtectEndTrace();

	xbConfigNodeBase::onBeforeDestruction(bInDestroy);
}

void xbConfigNode::checkRights(ulong attr) const
{
	if ((m_rights & attr) == attr)
		return;
	xbThrowEsysFmt("Access forbidden to config key '%s'",(LPCSTR)GetPath());
}

bool xbConfigNode::GetValue(const char *pName, xbVariant *pValue) const
{
	checkRights(xbCfgKeyRight_READ);

	xbSectLock lck(&m_sectData);
	ValueInfo *pValInfo = ((xbConfigNode*)this)->execValueFind(pName);
	if (!pValInfo)
		return false;

	if (pValue)
		*pValue = pValInfo->m_value;

	return true;
}

void xbConfigNode::SetValue(const char *pName, const xbVariant &value,xbCfgValMode mode)
{
	checkRights(xbCfgKeyRight_WRITE);

	if (!value.AsXmlStringWsupported())
		xbThrowEsysFmt("Variant Type(%d) not supported by config system",(int)value.GetType());

retry:
	xbSectLock lck(&m_sectData);
	int pos;
	ValueInfo *pValInfo = execValueFind(pName,&pos);

	if (!pValInfo)
	{
		checkRights(xbCfgKeyRight_CREATEVAL);
		if (askWriteAccess(&lck))
			goto retry;

		pValInfo = xbDnew ValueInfo();
		if (pName && *pName)
			pValInfo->m_name = execValueName(pName);
		pValInfo->m_value = value;
		pValInfo->m_mode = mode;
		pValInfo->m_modeCalc = mode;
		pValInfo->m_order = getNextOrder();
		pValInfo->m_bToWrite = true;
		m_arrValue.Insert(pos,pValInfo);
		valueOrderAdd(pValInfo);
		m_bToWrite = true;
		return;
	}

	if (pValInfo->m_value != value)
	{
		if (askWriteAccess(&lck))
			goto retry;
		pValInfo->m_value = value;
		pValInfo->m_bToWrite = true;
		m_bToWrite = true;
	}
	if (mode != xbCfgVal_Auto && pValInfo->m_mode != mode)
	{
		if (askWriteAccess(&lck))
			goto retry;

		valueOrderDel(pValInfo);
		pValInfo->m_mode = mode;
		pValInfo->m_modeCalc = mode;
		valueOrderAdd(pValInfo);
		pValInfo->m_bToWrite = true;
		m_bToWrite = true;
	}
}

bool xbConfigNode::EnumValue(int &idx,xbString *pName, xbVariant *pValue) const
{
	checkRights(xbCfgKeyRight_READ);

	xbSectLock lck(&m_sectData);
	if (idx >= m_arrValueOrdered.Length)
		return false;
	ValueInfo *pValInfo = m_arrValueOrdered.GetAt(idx);
	if (pName)
		*pName = pValInfo->m_name;
	if (pValue)
		*pValue = pValInfo->m_value;
	idx++;
	return true;
}

bool xbConfigNode::DeleteValue(const char *pName,xbOpMode opMode)
{
	checkRights(xbCfgKeyRight_DELETEVAL);
retry:
	xbSectLock lck(&m_sectData);
	int pos;
	ValueInfo *pValInfo = execValueFind(pName,&pos);

	if (!pValInfo)
	{
		if (opMode == xbOp_Required)
			xbThrowEsys("xbCfgKey::DeleteValue() : config value doesn't exist !");
		return false;
	}

	if (askWriteAccess(&lck))
		goto retry;

	valueOrderDel(pValInfo);
	m_arrValue.Delete(pos);

	return true;
}

IxbCfgKey *xbConfigNode::GetKey(const char *pName,xbString *pKeyStaying) const
{
	xbSectLock lck(&m_sectData);
	xbString sstaying;
	xbString sname(::xbSubPath(pName,&sstaying));

	xbRefPtr<xbConfigNodeBase> *pprKey = ((xbConfigNode*)this)->execKeyFind(sname);
	if (!pprKey)
	{
		if (!pKeyStaying)
			return NULL;
		*pKeyStaying = pName;
		return (IxbCfgKey*)this;
	}
	xbRefQIPtr<IxbCfgKey> prKey(*pprKey);
	if (sstaying.IsEmpty())
	{
		if (pKeyStaying)
			pKeyStaying->Empty();
		return prKey;
	}

	return prKey->GetKey(sstaying, pKeyStaying);
}

IxbCfgKey *xbConfigNode::CreateKey(const char *pName)
{
	xbString sstaying;
	xbString sname(::xbSubPath(pName,&sstaying));

retry:
	xbSectLock lck(&m_sectData);

	int pos;
	xbRefPtr<xbConfigNodeBase> *pprKey = execKeyFind(sname,&pos);
	if (!pprKey)
	{
		checkRights(xbCfgKeyRight_CREATEKEY);
		if (askWriteAccess(&lck))
			goto retry;

		xbRefPtr<xbConfigNode> prNode;
		xbConfigNode::CreateObject(&prNode,this);

		prNode->writeSet();
		prNode->SetName(execKeyName(sname));
		prNode->SetConfigOrder(getNextOrder());

		m_arrKey.Insert(pos,(xbConfigNodeBase*)prNode);
		keyOrderAdd(prNode);

		if (sstaying.IsEmpty())
			return prNode;

		return prNode->CreateKey(sstaying);
	}

	xbRefQIPtr<IxbCfgKey> prKey(*pprKey);
	if (sstaying.IsEmpty())
		return prKey;

	return prKey->CreateKey(sstaying);
}

bool xbConfigNode::EnumKey(int &idx,xbString *pName, xbRefPtr<IxbCfgKey> *pprKey) const
{
	checkRights(xbCfgKeyRight_READ);

retry:
	xbSectLock lck(&m_sectData);
	if (idx >= m_arrKeyOrdered.Length)
		return false;
	xbConfigNodeBase*const pKeyFnd = m_arrKeyOrdered.GetAt(idx);
	xbRefQIPtr<IxbCfgKey> prCfgKey(pKeyFnd, xbOp_Optional);
	if (prCfgKey.IsEmpty())
	{
		idx++;
		goto retry;
	}

	if (pName)
		*pName = prCfgKey->GetName();
	if (pprKey)
		*pprKey = prCfgKey;
	idx++;
	return true;
}

bool xbConfigNode::Delete(bool bRecurse,xbOpMode opMode)
{
	checkRights(xbCfgKeyRight_DELETEKEY);
	xbSectLock lck(&m_sectData);
	if (!m_pParent)
	{
		if (IsInDestruction())
			return true;

		if (opMode == xbOp_Required)
			::xbThrowEsysFmt("xbConfigNode::Delete() failed on key '%s'",(LPCSTR)GetPath());
		return false;
	}
	if (!bRecurse && m_arrKey.IsValid())
	{
		if (opMode == xbOp_Required)
			::xbThrowEsysFmt("xbConfigNode::Delete() failed on key '%s', key has children",(LPCSTR)GetPath());
		return false;
	}
	xbString name(m_name);
	xbRefPtr<xbConfigNode> prParentNode(m_pParent);
	lck.Unlock();

	return prParentNode->deleteChildKey(this,name,bRecurse,opMode);
}

bool xbConfigNode::deleteChildKey(xbConfigNode *pChild,const xbString &childName,bool bRecurse,xbOpMode opMode)
{
retry:
	xbSectLock lck(&m_sectData);

	int pos;
	xbRefPtr<xbConfigNodeBase> *pprKey = keyFind(childName,&pos,NULL,false);
	if (!pprKey)
	{
		if (opMode == xbOp_Required)
			::xbThrowEsysFmt("xbConfigNode::Delete() failed on key '%s/%s', key not found",(LPCSTR)GetPath(),(LPCSTR)childName);
		return false;
	}
	xbRefPtr<xbConfigNodeBase> *pEnd = m_arrKey.End();
	while(pprKey < pEnd)
	{
		if (keyFindCb(*pprKey,childName) > 0)
			break;

		if (pprKey->IsEqualObject((IxbCfgKey*)pChild))
		{
			if (askWriteAccess(&lck))
				goto retry;

			if (!bRecurse && pChild->m_arrKey.IsValid())
			{
				if (opMode == xbOp_Required)
					::xbThrowEsysFmt("xbConfigNode::Delete() failed on key '%s/%s', key has children",(LPCSTR)GetPath(),(LPCSTR)childName);
				return false;
			}

			m_arrKey.Delete(pos);
			keyOrderDel(pChild);
			lck.Unlock();

			pChild->Destroy();

			return true;
		}

		pos++;
		pprKey++;
	}
	if (opMode == xbOp_Required)
		::xbThrowEsysFmt("xbConfigNode::Delete() failed on key '%s/%s', key not found",(LPCSTR)GetPath(),(LPCSTR)childName);
	return false;
}

xbString xbConfigNode::GetPath() const
{
	if (!m_pParent)
		return m_name;
	xbString path(m_pParent->GetPath());
	if (path.IsEmpty())
		return m_name;
	return path+"/"+m_name;
}

void xbConfigNode::SetRights(ulong attr)
{
	m_rights = attr;
}

ulong xbConfigNode::GetRights() const
{
	return m_rights;
}

xbConfigFile::xbConfigFile()
{
	m_bInError = false;
	m_hNotif = INVALID_HANDLE_VALUE;
	m_root = xbCfgUndefined;
	m_bomType = xbBOM_none;
	m_IOlockThId = 0;
	m_bWriteAccessOk = false;
	m_tiWriteAccessOk = 0;
}

xbConfigFile::~xbConfigFile() 
{
	eProtectTrace(Destroy());
}

bool xbConfigFile::GetValue(const char *pName, xbVariant *pValue) const
{
	return xbConfigNode::GetValue(pName,pValue);
}

void xbConfigFile::SetValue(const char *pName, const xbVariant &value,xbCfgValMode mode)
{
	xbConfigNode::SetValue(pName,value,mode);
}

bool xbConfigFile::EnumValue(int &idx,xbString *pName, xbVariant *pValue) const
{
	return xbConfigNode::EnumValue(idx,pName,pValue);
}

bool xbConfigFile::DeleteValue(const char *pName,xbOpMode opMode)
{
	return xbConfigNode::DeleteValue(pName,opMode);
}

IxbCfgKey *xbConfigFile::GetKey(const char *pName,xbString *pKeyStaying) const
{
	return xbConfigNode::GetKey(pName,pKeyStaying);
}

IxbCfgKey *xbConfigFile::CreateKey(const char *pName)
{
	return xbConfigNode::CreateKey(pName);
}

bool xbConfigFile::EnumKey(int &idx,xbString *pName, xbRefPtr<IxbCfgKey> *pprKey) const
{
	return xbConfigNode::EnumKey(idx,pName,pprKey);
}

bool xbConfigFile::Delete(bool bRecurse,xbOpMode opMode)
{
	return xbConfigNode::Delete(bRecurse,opMode);
}

xbString xbConfigFile::GetName() const
{
	return xbConfigNode::GetName();
}

void xbConfigFile::SetRights(ulong attr)
{
	xbConfigNode::SetRights(attr);
}

ulong xbConfigFile::GetRights() const
{
	return xbConfigNode::GetRights();
}

xbString xbConfigFile::dirCreate()
{
	xbString sdir = ::xbGetFileDir(m_filePath,false);
	if (!::xbDirExists(sdir))
		::xbMakeDir(sdir,true);
	return sdir;
}

HANDLE xbConfigFile::FileChangeNotifStart()
{
	HANDLE hEv = m_hNotif;
	if (hEv != INVALID_HANDLE_VALUE )
		return hEv;
	xbString sdir = dirCreate();
	hEv = ::FindFirstChangeNotification(sdir,FALSE,FILE_NOTIFY_CHANGE_LAST_WRITE);
	if (hEv == INVALID_HANDLE_VALUE)
		xbThrowEsysFmt(xbHR_LAST,"xbConfigFile(%s)::FileChangeNotifStart failed",(LPCSTR)m_filePath);
	m_hNotif = hEv;
	return hEv;
}

void xbConfigFile::FileChangeNotifRestart()
{
	HANDLE hEv = m_hNotif;
	if (hEv == INVALID_HANDLE_VALUE )
		return;
	if (!::FindNextChangeNotification(hEv))
		xbThrowEsysFmt(xbHR_LAST,"xbConfigFile(%s)::FileChangeNotifRestart failed",(LPCSTR)m_filePath);
}

void xbConfigFile::FileChangeNotifClose()
{
	HANDLE hEv = m_hNotif;
	if (hEv == INVALID_HANDLE_VALUE )
		return;
	m_hNotif = INVALID_HANDLE_VALUE;
	if (!::FindCloseChangeNotification(hEv))
		xbThrowEsysFmt(xbHR_LAST,"xbConfigFile(%s)::FileChangeNotifClose failed",(LPCSTR)m_filePath);
}

void xbConfigFile::Create(xbConfigFileInit *pInit)
{
	xbConfigNode::Create(NULL);
	m_root = pInit->m_root;
	m_filePath = pInit->m_filePath;
	if (!cfgOpenStart(pInit->m_pInfo)
		&& !pInit->m_pInfo->m_bWriteWaiting
		&& pInit->m_pEngine
		&& !m_bInError)
		pInit->m_pEngine->onConfigFileInit(m_root,this);
}

bool xbConfigFile::cfgOpenStart(xbCfgKeyChangesInfo *pInfo)
{
	//First file open
	CheckLoadWrite(pInfo);
	return m_arrKey.IsValid();
}

void xbConfigFile::CheckLoadWrite(xbCfgKeyChangesInfo *pInfo)
{
	IOlock iolck;
	if (!iolck.Lock(this,xbOp_Optional))
	{
		pInfo->m_bWriteWaiting = true;
		return;
	}

	if (!cfgFileReadAccessOpen())
	{
		pInfo->m_bWriteWaiting = true;
		return;
	}

	xbDateTime lastWr;
	m_fstm.GetFileTime(NULL,NULL,&lastWr);
	if (m_dtLastWr == lastWr)
	{
		if (cfgFileWriteInProgress(pInfo))
			return;
		m_fstm.Close();
		return;
	}

	if (m_bWriteAsked)
		pInfo->m_bWriteWaiting = true;

	m_dtLastWr = lastWr;

	try
	{
		m_bomType = xbBOM_none;
		m_fstm.Seek(0);
		m_fstm.ReadTextBOM(&m_bomType);
		m_fstm.SetAppTextCP(m_fstm.GetStreamTextCP());

		xbConfigFileParser parser(m_fstm.GetStreamTextCP(),pInfo,&m_arrNodeRsrv);

		if (!parser.ParseStream(m_fstm.GetPhysicalName(),&m_fstm))
		{
			m_fstm.Close();
			m_bInError = true;
			return;
		}
		m_fstm.Close();

		parser.cfgFileLoad(this);
		m_bInError = false;
	}
	catch(...)
	{
		//Ne devrait pas arriver...
		m_fstm.Close();
		m_bInError = true;
		m_dtLastWr.Empty();
		throw;
	}
}

void xbConfigComment::cfgFileLoad(xbConfigLoadCtx *pCtx,xbConfigXmlNode *pXml)
{
//	xbSectLock lck(&m_sectData);
	pXml->GetComment(&m_comment);
}

void xbConfigNode::cfgFileLoad(xbConfigLoadCtx *pCtx,xbConfigXmlNode *pXml)
{
	xbConfigLoadCtx ctx(pCtx,m_name);
	{
		xbSectLock lck(&m_sectData);
		xmlValueUsedStart();
		int idx = 0;
		xbString tagName;
		xbVariant tagValue;
		int order;
		while(pXml->EnumTagAndDetachVal(idx, &tagName, &tagValue, &order))
		{
			xmlValueSet(tagName,tagValue,order);
		}
		xmlValueUsedEnd(false);
		if (m_bChanged)
			ctx.change();
	}

	xmlKeyUsedStart();

	{
		int idx = 0;
		xbRefPtr<xbConfigXmlNode> prXmlNode;
		while(pXml->EnumSection(idx,&prXmlNode))
		{
			switch(prXmlNode->GetType())
			{
			case xbConfigXmlNode_Key:
				{
					xbRefPtr<xbConfigNode> prNode;
					if (!xmlKeyReuse(prXmlNode->GetName(),&prNode,(int)idx))
						ctx.change();

					prNode->SetConfigLfLength(prXmlNode->GetLfLength());
					prNode->cfgFileLoad(&ctx,prXmlNode);
				}
				break;
			case xbConfigXmlNode_Value:
				{
					xbVariant tagValue;
					prXmlNode->GetValueAndDetach(&tagValue);
					xmlValueSet(prXmlNode->GetName(),tagValue,(int)idx,prXmlNode->GetValMode(),prXmlNode->GetLfLength());
				}
				break;
			case xbConfigXmlNode_Comment:
				{
					xbRefPtr<xbConfigComment> prNode;
					xmlKeyCommentReuse(&prNode,(int)idx);

					prNode->SetConfigLfLength(prXmlNode->GetLfLength());
					prNode->cfgFileLoad(&ctx,prXmlNode);
				}
				break;
			default:
				xbThrowEsys("Internal pb: xbConfigNode::cfgFileLoad("+ctx.m_keyPath+"): unexpected xml node type");
			}
		}
	}


	xmlKeyUsedEnd();
	xmlValueUsedEnd(true);

	{
		xbSectLock lck(&m_sectData);
		if (m_bChanged)
		{
			m_bChanged = false;
			ctx.change();
			lck.Unlock();
			SignalPtr(xbPTRSIG_ITEMUPDATE);
		}
	}

	try
	{
		ctx.changeEnd();
	}
	catch(...)
	{
		m_bChanged = true;
		throw;
	}
}

void xbConfigNode::xmlValueUsedStart()
{
	xbSectLock lck(&m_sectData);
	ValueInfo **pptr = m_arrValue.Begin();
	ValueInfo **ppend = m_arrValue.End();
	while(pptr < ppend)
	{
		(*pptr)->m_bUsed = (*pptr)->m_bToWrite;
		pptr++;
	}
}

void xbConfigNode::xmlValueSet(const char *pName, xbVariant &value, int order, xbCfgValMode mode, int cLf)
{
	if (pName && xbStrcmp(pName,"$") == 0)
		pName = NULL;
	xbSectLock lck(&m_sectData);
	int pos;
	ValueInfo **ppValue = valueFind(pName,&pos,NULL,true);
	if (ppValue)
	{
		ValueInfo *pValue = *ppValue;
		pValue->m_bUsed = true;
		if (pValue->m_value != value)
		{
			m_bChanged = true;
			pValue->m_value.Attach(value.IOaddr());
		}
		if (pValue->m_order != order || pValue->m_mode != mode)
		{
			valueOrderDel(pValue);
			pValue->m_order = order;
			pValue->m_mode = mode;
			pValue->m_modeCalc = mode;
			valueOrderAdd(pValue);
		}
		pValue->m_cLf = cLf;
		return;
	}

	ValueInfo *pValue = xbDnew ValueInfo();
	if (pName && *pName)
		pValue->m_name = pName;
	pValue->m_value = value;
	pValue->m_order = order;
	pValue->m_mode = mode;
	pValue->m_modeCalc = mode;
	pValue->m_cLf = cLf;

	m_arrValue.Insert(pos,pValue);
	valueOrderAdd(pValue);

	m_bChanged = true;
}

void xbConfigNode::xmlValueUsedEnd(bool bAll)
{
	xbSectLock lck(&m_sectData);
	ValueInfo **pptr = m_arrValue.Begin();
	ValueInfo **ppend = m_arrValue.End();
	while(pptr < ppend)
	{
		if ((*pptr)->m_bUsed)
		{
			pptr++;
			continue;
		}
		if (!bAll && !(*pptr)->asTagConst())
		{
			pptr++;
			continue;
		}
		valueOrderDel(*pptr);
		int pos = (int)((xbNptr(pptr) - xbNptr(m_arrValue.Begin())) / sizeof(ValueInfo*));
		m_arrValue.Delete(pos);
		pptr = m_arrValue.Begin() + pos;
		ppend = m_arrValue.End();
		m_bChanged = true;
	}
}

void xbConfigNode::xmlKeyUsedStart()
{
	xbSectLock lck(&m_sectData);
	xbRefPtr<xbConfigNodeBase> *pprPtr = m_arrKey.Begin();
	xbRefPtr<xbConfigNodeBase> *pprEnd = m_arrKey.End();
	while(pprPtr < pprEnd)
	{
		(*pprPtr)->usedStart();
		pprPtr++;
	}
}

void xbConfigNode::xmlKeyUsedEnd()
{
	xbArray< xbRefPtr<xbConfigNodeBase> > arrToDel;
	{
		xbSectLock lck(&m_sectData);
		xbRefPtr<xbConfigNodeBase> *pprPtr = m_arrKey.Begin();
		xbRefPtr<xbConfigNodeBase> *pprEnd = m_arrKey.End();
		while(pprPtr < pprEnd)
		{
			if ((*pprPtr)->usedEnd())
			{
				pprPtr++;
				continue;
			}
			arrToDel.Add(*pprPtr);
			keyOrderDel(*pprPtr);
			int pos = (int)((xbNptr(pprPtr) - xbNptr(m_arrKey.Begin())) / sizeof(xbRefPtr<xbConfigNodeBase>));
			m_arrKey.Delete(pos);
			pprPtr = m_arrKey.Begin() + pos;
			pprEnd = m_arrKey.End();
			m_bChanged = true;
		}
	}
	xbRefPtr<xbConfigNodeBase> *pprPtr = arrToDel.Begin();
	xbRefPtr<xbConfigNodeBase> *pprEnd = arrToDel.End();
	while(pprPtr < pprEnd)
	{
		(*pprPtr)->Destroy();
		pprPtr++;
	}
}

bool xbConfigNode::xmlKeyReuse(const xbString &name, xbRefPtr<xbConfigNode> *pprNode,int order)
{
	xbSectLock lck(&m_sectData);
	int pos;
	xbRefPtr<xbConfigNodeBase> *pprKey = keyFind(name,&pos,NULL,true);
	if (pprKey)
	{
		(*pprKey)->usedSet();

		if ((*pprKey)->GetConfigOrder() != order)
		{
			keyOrderDel(*pprKey);
			(*pprKey)->SetConfigOrder(order);
			keyOrderAdd(*pprKey);
		}
		if (pprNode)
			*pprNode = xbRefQIPtr<xbConfigNode>(*pprKey);
		return true;
	}
	xbRefPtr<xbConfigNode> prNode;
	xbConfigNode::CreateObject(&prNode,this);
	prNode->SetName(name);
	prNode->SetConfigOrder(order);

	m_arrKey.Insert(pos,(xbConfigNodeBase*)prNode);
	keyOrderAdd((xbConfigNodeBase*)prNode);

	m_bChanged = true;
	if (pprNode)
		*pprNode = prNode;
	return false;
}

void xbConfigNode::xmlKeyCommentReuse(xbRefPtr<xbConfigComment> *pprNode,int order)
{
	xbSectLock lck(&m_sectData);
	int pos;
	xbRefPtr<xbConfigNodeBase> *pprKey = keyFind(xbConfigComment_NAME,&pos,NULL,true);
	if (pprKey)
	{
		(*pprKey)->usedSet();

		if ((*pprKey)->GetConfigOrder() != order)
		{
			keyOrderDel(*pprKey);
			(*pprKey)->SetConfigOrder(order);
			keyOrderAdd(*pprKey);
		}
		if (pprNode)
			*pprNode = xbRefQIPtr<xbConfigComment>(*pprKey);
		return;
	}
	xbRefPtr<xbConfigComment> prNode;
	xbConfigComment::CreateObject(&prNode);
	prNode->SetConfigOrder(order);

	m_arrKey.Insert(pos,(xbConfigNodeBase*)prNode);
	keyOrderAdd((xbConfigNodeBase*)prNode);

	if (pprNode)
		*pprNode = prNode;
//	m_bChanged = true;
}

xbConfigNode::ValueInfo **xbConfigNode::valueFind(const char *pName,int *pPos,bool *pbValsWithSameName,bool bNotUsed)
{
	int pos;
	ValueInfo **pInfo = m_arrValue.SortedFind(pName,valueFindCb,&pos);
	if (!pInfo) 
	{
		if (pPos)
			*pPos = pos;
		return NULL;
	}
	int cnt = 0;

	while(pos > 0)
	{
		if (valueFindCb(*(pInfo-1),pName) < 0)
			break;
		pInfo--;
		pos--;
	}

	if (pbValsWithSameName || bNotUsed)
	{
		if (pbValsWithSameName)
			*pbValsWithSameName = false;
		ValueInfo **pEnd = m_arrValue.End();
		ValueInfo **pInfoNext = pInfo+(bNotUsed?0:1);
		while(pInfoNext < pEnd)
		{
			if (pInfoNext>pInfo)
			{
				if (valueFindCb(*pInfoNext,pName) > 0)
					break;

				if (pbValsWithSameName)
					*pbValsWithSameName = true;

				if (!bNotUsed)
					break;
			}

			//Here bNotUsed = true 
			if (!(*pInfoNext)->m_bUsed)
			{
				if (pPos)
					*pPos = pos;
				return pInfoNext;				
			}
			pos++;
			pInfoNext++;
		}

		if (bNotUsed)
		{
			if (pPos)
				*pPos = pos;
			return NULL;
		}
	}

	if (pPos)
		*pPos = pos;
	return pInfo;
}

xbRefPtr<xbConfigNodeBase> *xbConfigNode::keyFind(const char *pName,int *pPos,bool *pbKeysWithSameName,bool bNotUsed)
{
	int pos;
	xbRefPtr<xbConfigNodeBase> *pprKey = m_arrKey.SortedFind(pName,keyFindCb,&pos);
	if (!pprKey) 
	{
		if (pPos)
			*pPos = pos;
		return NULL;
	}

	int cnt = 0;

	while(pos > 0)
	{
		if (keyFindCb(*(pprKey-1),pName) < 0)
			break;
		pprKey--;
		pos--;
	}

	if (pbKeysWithSameName || bNotUsed)
	{
		if (pbKeysWithSameName)
			*pbKeysWithSameName = false;
		xbRefPtr<xbConfigNodeBase> *pEnd = m_arrKey.End();
		xbRefPtr<xbConfigNodeBase> *pprKeyNext = pprKey+(bNotUsed?0:1);
		while(pprKeyNext < pEnd)
		{
			if (pprKeyNext>pprKey)
			{
				if (keyFindCb(*pprKeyNext,pName) > 0)
					break;

				if (pbKeysWithSameName)
					*pbKeysWithSameName = true;

				if (!bNotUsed)
					break;
			}

			//Here bNotUsed = true 
			if (!(*pprKeyNext)->isUsed())
			{
				if (pPos)
					*pPos = pos;
				return pprKeyNext;				
			}
			pos++;
			pprKeyNext++;
		}

		if (bNotUsed)
		{
			if (pPos)
				*pPos = pos;
			return NULL;
		}
	}

	if (pPos)
		*pPos = pos;
	return pprKey;
}

void xbConfigNode::valueOrderAdd(ValueInfo* pVal)
{
	int pos;
	ValueInfo **pInfo = m_arrValueOrdered.SortedFind(pVal,valueOrderFindCb,&pos);
	xbRdbgAssert(pInfo == NULL);
	if (pInfo)
		return;
	m_arrValueOrdered.Insert(pos,pVal);
}

void xbConfigNode::valueOrderDel(ValueInfo* pVal)
{
	int pos;
	ValueInfo **pInfo = m_arrValueOrdered.SortedFind(pVal,valueOrderFindCb,&pos);
	xbRdbgAssert(pInfo != NULL);
	if (!pInfo)
		return;
	m_arrValueOrdered.Delete(pos);
}

void xbConfigNode::keyOrderAdd(xbConfigNodeBase* pKey)
{
	int pos;
	xbConfigNodeBase **pFnd = m_arrKeyOrdered.SortedFind(pKey,keyOrderFindCb,&pos);
	xbRdbgAssert(pFnd == NULL);
	if (pFnd)
		return;
	m_arrKeyOrdered.Insert(pos,pKey);
}

void xbConfigNode::keyOrderDel(xbConfigNodeBase* pKey)
{
	int pos;
	xbConfigNodeBase **pFnd = m_arrKeyOrdered.SortedFind(pKey,keyOrderFindCb,&pos);
	xbRdbgAssert(pFnd != NULL);
	if (!pFnd)
		return;
	m_arrKeyOrdered.Delete(pos);
}

void xbConfigLoadCtx::changeEnd()
{
	if (!m_bChg)
		return;
	if (m_pParent && m_pParent->m_bChg)
		return;
	m_pChgInfo->pathChanged(m_keyPath);
}

xbConfigNode::ValueInfo *xbConfigNode::execValueFind(const char *pName,int *pPos)
{
	xbString sname;
	int nameOrder;
	nameParse(pName,&sname,&nameOrder);
	if (!exeValueNameIsValid(sname))
		xbThrowEsysFmt("Config value name '%s' contains invalid characters or is reserved",pName);

	int pos;
	bool bValsWithSameName=false;
	ValueInfo **ppInfo = valueFind(sname,&pos,nameOrder<0?&bValsWithSameName:NULL,false);
	if (!ppInfo)
	{
		if (pPos)
			*pPos = pos;
		return NULL;
	}

	if (bValsWithSameName)
		xbThrowEsysFmt("More than one value with name '%s' found on key '%s'",(LPCSTR)sname,(LPCSTR)GetPath());

	ValueInfo **ppEnd = m_arrValue.End();
	while(nameOrder > 0
		&& ppInfo < ppEnd)
	{
		if (valueFindCb(*ppInfo,sname) > 0)
			break;
		nameOrder--;
		pos++;
		ppInfo++;
	}

	if (pPos)
		*pPos = pos;
	if (nameOrder > 0)
		return NULL;
	return *ppInfo;	
}

xbString xbConfigNode::execValueName(const xbString &name)
{
	xbString sname;
	int nameOrder;
	nameParse(name,&sname,&nameOrder);
	if (!exeValueNameIsValid(sname))
		xbThrowEsysFmt("Config value name '%s' contains invalid characters or is reserved",(LPCSTR)name);
	return sname;
}

xbRefPtr<xbConfigNodeBase> *xbConfigNode::execKeyFind(const char *pName,int *pPos)
{
	xbString sname;
	int nameOrder;
	nameParse(pName,&sname,&nameOrder);
	if (!exeKeyNameIsValid(sname))
		xbThrowEsysFmt("Config key name '%s' contains invalid characters or is reserved",pName);

	int pos;
	bool bKeysWithSameName=false;
	xbRefPtr<xbConfigNodeBase> *ppInfo = keyFind(sname,&pos,nameOrder<0?&bKeysWithSameName:NULL,false);
	if (!ppInfo)
	{
		if (pPos)
			*pPos = pos;
		return NULL;
	}

	if (bKeysWithSameName)
		xbThrowEsysFmt("More than one key with name '%s' found on path '%s'",(LPCSTR)sname,(LPCSTR)GetPath());

	xbRefPtr<xbConfigNodeBase> *ppEnd = m_arrKey.End();
	while(nameOrder > 0
		&& ppInfo < ppEnd)
	{
		if (keyFindCb(*ppInfo,sname) > 0)
			break;
		nameOrder--;
		pos++;
		ppInfo++;
	}

	if (pPos)
		*pPos = pos;
	if (nameOrder > 0)
		return NULL;
	return ppInfo;	
}

xbString xbConfigNode::execKeyName(const xbString &name)
{
	xbString sname;
	int nameOrder;
	nameParse(name,&sname,&nameOrder);
	if (!exeKeyNameIsValid(sname))
		xbThrowEsysFmt("Config key name '%s' contains invalid characters or is reserved",(LPCSTR)name);
	return sname;
}

void xbConfigNode::nameParse(LPCSTR pName,xbString *pNamePart,int *pNameOrder)
{
	if (!pName || !*pName)
	{
		if (pNamePart)
			pNamePart->Empty();
		if (pNameOrder)
			*pNameOrder = -1;
		return;
	}
	xbString sname(pName);
	int pos = sname.LastIndexOf(':');
	if (pos < 0)
	{
		if (pNamePart)
			*pNamePart = sname;
		if (pNameOrder)
			*pNameOrder = -1;
		return;
	}
	int nb;
	const char *pStop;
	if (::xbStrToInt(sname.Mid(pos+1),&nb,0,&pStop)<=0
		|| *pStop != 0
		|| nb < 0)
	{
		if (pNamePart)
			*pNamePart = sname;
		if (pNameOrder)
			*pNameOrder = -1;
		return;
	}
	if (pNamePart)
		*pNamePart = sname.Left(pos);
	if (pNameOrder)
		*pNameOrder = nb;
}

bool xbConfigNode::_nameIsValid(LPCSTR pName)
{
	while(*pName)
	{
		if (*pName <= 32 || *pName >= 127
			|| ::xbStrchr("/\\:?!$*<>\"'{}",*pName) != NULL)
			return false;
		pName++;
	}
	return true;
}

bool xbConfigNode::exeValueNameIsValid(LPCSTR pName)
{
	if (!pName || !*pName)
		return true;
	if (!_nameIsValid(pName))
		return false;
	return true;
}

bool xbConfigNode::exeKeyNameIsValid(LPCSTR pName)
{
	if (!pName || !*pName)
		return false;
	if (!_nameIsValid(pName))
		return false;
	return true;
}

bool xbConfigNode::xmlValueNameIsValid(LPCSTR pName)
{
	if (!pName || !*pName)
		return false;
	if (xbStrcmp(pName,"$")==0)
		return true;
	if (!_nameIsValid(pName))
		return false;
	return true;
}

bool xbConfigNode::xmlKeyNameIsValid(LPCSTR pName)
{
	if (!pName || !*pName)
		return false;
	if (!_nameIsValid(pName))
		return false;
	return true;
}

int xbConfigNode::getNextOrder()
{
	xbSectLock lck(&m_sectData);
	int order = 0;
	if (m_arrKeyOrdered.IsValid())
		order = m_arrKeyOrdered.Last()->GetConfigOrder();

	if (m_arrValueOrdered.IsValid())
	{
		int orderv = m_arrValueOrdered.Last()->m_order;
		if (orderv > order)
			order = orderv;
	}

	return order+1;
}

bool xbConfigNode::askWriteAccess(xbSectLock *pLck)
{
	if (m_bWriteAsked)
		return false;
	if (!m_pParent)
		xbThrowEsys("xbConfigNode::askWriteAccess(): node in destruction");

	bool bRet = m_pParent->askWriteAccess(pLck);
	
	m_bWriteAsked = true;
	return bRet;
}

bool xbConfigFile::askWriteAccess(xbSectLock *pLck)
{
	xbRdbgAssert(m_pParent == NULL);

	if (m_bWriteAsked)
		return false;
	
	pLck->Unlock();

	{
		xbSectLock lck(&m_sectData);
		if (m_bWriteAccessOk)
		{
			m_bWriteAsked = true;
			return true;
		}
	}

	IOlock iolck(this);

	cfgFileWriteAccessOpen();
	
	m_bWriteAsked = true;
	return true;
}

bool xbConfigFile::cfgFileReadAccessOpen()
{
	{
		xbSectLock lck(&m_sectData);
		if (!m_bWriteAccessOk && m_fstm.IsOpened())
			return true;
		if (m_bWriteAccessOk && m_fstm.IsOpened() && m_bWriteAsked)
			return true;
		m_bWriteAccessOk = false;
	}

	m_fstm.Close();
	dirCreate();

	for(int cTry = 3;;cTry--)
	{
		try
		{
			m_fstm.Open(m_filePath,xbOpenAlways,xbAccessR,xbWorkSharedR);
			break;
		}
		catch(xbEstream *pE)
		{
			::xbRdbgTraceException(pE);

			if (cTry < 0)
				return false;
		}

		::xbSleep(300);
	}
	return true;
}

void xbConfigFile::cfgFileWriteAccessOpen()
{
	{
		xbSectLock lck(&m_sectData);
		if (m_bWriteAccessOk && m_fstm.IsOpened())
			return;
	}

	m_fstm.Close();
	dirCreate();

	for(int cTry = 10;;cTry--)
	{
		try
		{
			m_fstm.Open(m_filePath,xbOpenAlways,xbAccessRW,xbWorkSharedR);
			break;
		}
		catch(xbEstream *pE)
		{
			if (cTry < 0)
				throw;

			::xbRdbgTraceException(pE);
		}

		::xbSleep(300);
	}

	{
		xbSectLock lck(&m_sectData);
		m_bWriteAccessOk = true;
		m_tiWriteAccessOk = 0;
	}
}

bool xbConfigFile::_IOlock(xbOpMode opm)
{
	//if (IsInDestruction()) 
	//	xbThrow xbEsys("xbConfigFile::_IOlock() : destroyed config");

	xbSectLock lck(&m_sectData);
	int cnt = 0;
	while(m_IOlockThId)
	{
		if (++cnt > xbCONFIGIOLOCK_TRYCNT) 
			xbThrow xbEsys(xbHR_TIMEOUT,"xbHotPlugType::lockCbCall() : timeout, can't call callbacks !");
		if (opm == xbOp_Optional && cnt > 3)
			return false;
		lck.Unlock();
		if (::xbDoEvents(NULL)) 
		{
			//if (IsInDestruction()) 
			//	xbThrow xbEsys("xbConfigFile::_IOlock() : destroyed config");
			lck.Lock(&m_sectData);
			if (m_IOlockThId) 
				break;
			lck.Unlock();
		}
		SleepEx(xbCONFIGIOLOCK_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
		//if (IsInDestruction()) 
		//	xbThrow xbEsys("xbConfigFile::_IOlock() : destroyed config");
		lck.Lock(&m_sectData);
	}
	m_IOlockThId=::GetCurrentThreadId();
	return true;
}

void xbConfigFile::_IOunlock()
{
	xbSectLock lck(&m_sectData);
	xbRdbgAssert(m_IOlockThId == ::GetCurrentThreadId());
	m_IOlockThId=0;
}

void xbConfigFile::onBeforeDestruction(bool bInDestroy)
{
	eProtectTrace(FileChangeNotifClose());
	
	eProtectBegin();
	IOlock iolck(this);
	cfgFileWriteInProgress(NULL);
	eProtectEndTrace();

	eProtectBegin();
	m_arrNodeRsrv.Empty();
	eProtectEndTrace();
	xbConfigNode::onBeforeDestruction(bInDestroy);
}

bool xbConfigFile::cfgFileWriteInProgress(xbCfgKeyChangesInfo *pInfo)
{
	xbRdbgAssert(m_IOlockThId == ::GetCurrentThreadId());
	xbSectLock lck(&m_sectData);
	if (/*!m_bWriteAsked &&*/ !m_bWriteAccessOk)
		return false;
	ulong ti = ::GetTickCount();
	if (pInfo && (!m_tiWriteAccessOk || ::xbDiffTime(ti,m_tiWriteAccessOk)<xbXDCCFG_WRITETIMEOUT))
	{
		if (!m_tiWriteAccessOk)
			m_tiWriteAccessOk = ti;
		return true;
	}
	m_bWriteAsked = false;
	lck.Unlock();

	m_fstm.Seek(0);
	m_fstm.WriteTextBOM(m_bomType);

	xbConfigWriteCtx writeCtx(&m_fstm,pInfo);
	cfgFileWrite(&writeCtx,"xdcube");

	m_fstm.SetEof();

	xbDateTime lastWr;
	m_fstm.GetFileTime(NULL,NULL,&lastWr);
	xbRdbgAssert(m_dtLastWr != lastWr);
	m_dtLastWr = lastWr;

	lck.Lock(&m_sectData);
	m_bWriteAccessOk = m_bWriteAsked;
	m_tiWriteAccessOk = 0;
	return m_bWriteAsked;
}

void xbConfigComment::cfgFileWrite(xbConfigWriteCtx *pCtx,LPCSTR pName)
{
	xbConfigWriteCtx writeCtx(pCtx,xbString());

	if (m_cLf)
		writeCtx.writeClf(m_cLf);

	writeCtx.writeLine(L"<"+m_comment+L">");
}

struct xbConfigXmlItem
{
	int							cLf;
	xbRefPtr<xbConfigNodeBase>	prNode;
	xbStringW					line;
	xbConfigXmlItem(int lf, xbConfigNodeBase *pNode)
	{
		cLf = lf;
		prNode = pNode;
	}
	xbConfigXmlItem(int lf, const xbStringW &ln)
	{
		cLf = lf;
		line = ln;
	}
	xbConfigXmlItem()
	{
		cLf = 0;
	}
};

void xbConfigNode::cfgFileWrite(xbConfigWriteCtx *pCtx,LPCSTR pName)
{
	xbConfigWriteCtx writeCtx(pCtx,m_name);
	xbStringW sectStart,sectEnd;
	xbArray<xbConfigXmlItem> arrItem;
	{
		xbSectLock lck(&m_sectData);
		if (!pName)
			pName = m_name;
		xbStringW wname(pName);
		sectStart = L"<"+wname;
		sectEnd = L"</"+wname+L">";
		ValueInfo **ppVal = m_arrValueOrdered.Begin();
		ValueInfo **ppValEnd = m_arrValueOrdered.End();

		xbConfigNodeBase **ppKey = m_arrKeyOrdered.Begin();
		xbConfigNodeBase **ppKeyEnd = m_arrKeyOrdered.End();

		int curLineLength = sectStart.Length,sectClf = 0;
		while(ppVal < ppValEnd
			|| ppKey < ppKeyEnd)
		{
			if (ppVal < ppValEnd)
			{
				if (ppKey >= ppKeyEnd 
					|| (*ppVal)->asTagCalc()
					|| (*ppVal)->m_order < (*ppKey)->GetConfigOrder())
				{
					(*ppVal)->m_bToWrite = false;
					if ((*ppVal)->asTagCalc())
						sectStart += (*ppVal)->toXmlTagString(pCtx,curLineLength,sectClf);
					else
						arrItem.Add(xbConfigXmlItem((*ppVal)->m_cLf, (*ppVal)->toXmlSectionString(this,pCtx)));
					ppVal++;
					continue;
				}
			}
			
			xbRdbgAssert(ppKey < ppKeyEnd);
			
			arrItem.Add(xbConfigXmlItem((*ppKey)->GetConfigLfLength(), *ppKey));
			ppKey++;
		}

		if (m_bToWrite)
			writeCtx.change();
		m_bWriteAsked = false;
		m_bToWrite = false;
	}
	sectStart+=L">";
	//if (m_cLf)
	//	writeCtx.writeClf(m_cLf);
	writeCtx.writeLine(sectStart);
	xbConfigXmlItem *pItem = arrItem.Begin();	
	xbConfigXmlItem *pItemEnd = arrItem.End();	
	while(pItem < pItemEnd)
	{
		if (pItem->cLf)
			writeCtx.writeClf(pItem->cLf);
		if (pItem->prNode.IsValid())
			pItem->prNode->cfgFileWrite(&writeCtx);
		else
			writeCtx.writeLine(pItem->line,1);
		pItem++;
	}
	writeCtx.writeLine(sectEnd);
	writeCtx.changeEnd();
}

void xbConfigWriteCtx::changeEnd()
{
	if (!m_bChg)
		return;
	if (m_pParent && m_pParent->m_bChg)
		return;
	if (m_pChgInfo)
		m_pChgInfo->pathChanged(m_keyPath);
}


bool			xbConfigNode::ValueInfo::asTagCalc() const
{
	if (m_modeCalc != xbCfgVal_Auto)
		return m_modeCalc == xbCfgVal_AsTag;
	if (m_value.IsString())
	{
		xbStringW str = m_value.AsStringW;
		if (str.IndexOf(L'\n')>=0)
		{
			((ValueInfo*)this)->m_modeCalc = xbCfgVal_AsSection;
			return false;
		}
		if (str.IndexOfChars(L"<>'\"")>=0)
		{
			((ValueInfo*)this)->m_modeCalc = xbCfgVal_AsSectionOneLine;
			return false;
		}

		((ValueInfo*)this)->m_modeCalc = xbCfgVal_AsTag;
		return true;
	}

	((ValueInfo*)this)->m_modeCalc = xbCfgVal_AsTag;
	return true;
}

xbStringW		xbConfigNode::ValueInfo::toXmlTagString(xbConfigWriteCtx *pCtx,int &curLineLength,int &sectClf)
{
	xbStringW ret = xbStringW(m_name.IsEmpty()?"$":m_name)+L"="+pCtx->valueEscape(m_value.AsXmlStringW);
	if (curLineLength > 0
		&& curLineLength+ret.Length+1 > pCtx->lineWidthMax(sectClf>0?1:0))
	{
		curLineLength = ret.Length+1;
		return L"\n" + pCtx->lineIndent(1) + ret;
	}

	curLineLength += ret.Length+1;
	return L" " + ret;
}

xbStringW		xbConfigNode::ValueInfo::toXmlSectionString(xbConfigNode *pParent,xbConfigWriteCtx *pCtx)
{
	xbStringW tagId = xbStringW(pParent->m_name)+L":"+xbStringW(m_name.IsEmpty()?"$":m_name);
	xbStringW ret = L"<"+tagId;

	switch(m_modeCalc)
	{
	case xbCfgVal_AsSection:
		ret += L">\n";

		ret += pCtx->lineIndent(1)+pCtx->valueHtmlEscape(m_value.AsXmlStringW);

		ret += L"\n"+pCtx->lineIndent(0)+L"</"+tagId+L">";
		break;
	case xbCfgVal_AsSectionOneLine:
		ret += L">";

		ret += pCtx->valueHtmlEscape(m_value.AsXmlStringW);

		ret += L"</"+tagId+L">";
		break;
	case xbCfgVal_AsSectionValue:
		ret += L" value="+pCtx->valueEscape(m_value.AsXmlStringW)+L"/>";
		break;
	case xbCfgVal_AsSectionNoEncode:
		ret += L">\n";

		ret += pCtx->lineIndent(1)+m_value.AsXmlStringW;

		ret += L"\n"+pCtx->lineIndent(0)+L"</"+tagId+L">";
		break;
	case xbCfgVal_AsSectionOneLineNoEncode:
		ret += L" noescape>"+pCtx->lineIndent(1);

		ret += m_value.AsXmlStringW;

		ret += pCtx->lineIndent(0)+L"</"+tagId+L">";
		break;
	default:
		xbThrowEsys("xbConfigNode::ValueInfo::toXmlSectionString() : internal pb");
	}

	return ret;
}

void xbConfigWriteCtx::writeClf(int cLf)
{
	if (cLf>0)
		m_pStm->WriteStringW(xbStringPadW(cLf,L'\n'));
}

xbStringW xbConfigWriteCtx::valueEscape(const xbStringW &str)
{
	return L'"'+::xbEscape(str,NULL,NULL,0,true)+L'"';
}

xbStringW xbConfigWriteCtx::valueHtmlEscape(const xbStringW &str)
{
	return ::xbHtmlEscape(str);
}

int xbConfigWriteCtx::lineWidthMax(int cAddIndent)
{
	int lg = lineSize() - (m_level+cAddIndent) * tabSize();
	if (lg < lineSizeMin())
		lg = lineSizeMin();
	return lg;
}

xbStringW xbConfigWriteCtx::lineIndent(int cAddIndent)
{
	return xbStringPadW((cAddIndent+1),L'\1');
}

void xbConfigWriteCtx::writeLine(const xbStringW &str,int cAddIndent)
{
	int pos = 0;
	int cTmpAddIndent = 0;
	xbStringW ln;
	while((pos = str.GetToken(pos,L"\n",&ln)) >= 0)
	{
		int ptab;
		while((ptab = ln.IndexOf(L'\1'))>=0)
		{
			int cnt = 0;
			while(ln.GetAt(ptab) == L'\1')
			{
				cnt++;
				ln.Delete(ptab);
			}
			cTmpAddIndent = cnt-1;
		}
//		ln.Trim();

		m_pStm->WriteStringW(xbStringPadW(m_level+cAddIndent+cTmpAddIndent,L'\t')+ln,true);
	}
}

#pragma warning(pop)
