/*<<<***************************************************************************
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: xbStat
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbStat.h"
#include "xbDnItf.h"

#pragma warning(push)
#pragma warning(disable: 4355) //'this' : utilisé dans la liste des initialiseurs membre de base

//marche pas xbRegPath	xbRegXdcStat("EproStat","xdcBase Stat Configuration Path",xbRegXdcMachine,"Stat");

/*************************************************************************************
Default stat descriptors
**************************************************************************************/
/*const xbStatUnit xbStatUnit_ByteTput[]=
{
	{"TB/s","Tera Byte per second","Tera Bytes per second",1024ULL*1024*1024*1024,0},
	{"GB/s","Giga Byte per second","Giga Bytes per second",1024ULL*1024*1024,0},
	{"MB/s","Mega Byte per second","Mega Bytes per second",1024ULL*1024,0},
	{"KB/s","Kilo Byte per second","Kilo Bytes per second",1024ULL,0},
	{"B/s","Byte per second","Bytes per second",0,0}
};
*/
extern int	g_xbPerfProcBug;

const xbStatUnit xbStatUnit_BytePerSec[]=
{
	{"TB","Tera Byte","Tera Bytes",1024ULL*1024*1024*1024,xbSTATUNIT_PERSEC},
	{"GB","Giga Byte","Giga Bytes",1024ULL*1024*1024,0},
	{"MB","Mega Byte","Mega Bytes",1024ULL*1024,0},
	{"KB","Kilo Byte","Kilo Bytes",1024ULL,0},
	{"B","Byte","Bytes",0,0}
};

const xbStatUnit xbStatUnit_Byte[]=
{
	{"TB","Tera Byte","Tera Bytes",1024ULL*1024*1024*1024,0},
	{"GB","Giga Byte","Giga Bytes",1024ULL*1024*1024,0},
	{"MB","Mega Byte","Mega Bytes",1024ULL*1024,0},
	{"KB","Kilo Byte","Kilo Bytes",1024ULL,0},
	{"B","Byte","Bytes",0,0}
};

const xbStatUnit xbStatUnit_Percent[]=
{
	{"%",NULL,NULL,0,0}
};

const xbStatUnit xbStatUnit_OpCount[]=
{
	{"Top","Tera operation","Tera operations",1000ULL*1000*1000*1000,0},
	{"Gop","Giga operation","Giga operations",1000ULL*1000*1000,0},
	{"Mop","Mega operation","Mega operations",1000ULL*1000,0},
	{"Kop","Kilo operation","Kilo operations",1000ULL,0},
	{"op","Operation","Operations",0,0}
};

const xbStatUnit xbStatUnit_OpCountPerSec[]=
{
	{"Top","Tera operation","Tera operations",1000ULL*1000*1000*1000,xbSTATUNIT_PERSEC},
	{"Gop","Giga operation","Giga operations",1000ULL*1000*1000,0},
	{"Mop","Mega operation","Mega operations",1000ULL*1000,0},
	{"Kop","Kilo operation","Kilo operations",1000ULL,0},
	{"op","Operation","Operations",0,0}
};

const xbStatUnit xbStatUnit_MicroSec[]=
{
	{"h","hour","hours",1000ULL*1000*60*60,0},
	{"min","minute","minutes",1000ULL*1000*60,0},
	{"s","second","seconds",1000ULL*1000,0},
	{"ms","millisecond","milliseconds",1000ULL,0},
	{"µs","microsecond","microseconds",0,0}
};

const xbStatProp xbStatPropList_LockTimeInstant[]=
{
	{"WaitTime","Wait time",xbStatUnit_Percent,xbStatPropId_UnitTimePercent,0},
	{"PeakMax","Peak max",xbStatUnit_Percent,xbStatPropId_UnitTimePercentPeakMax,0},
	{"PeakMin","Peak min",xbStatUnit_Percent,xbStatPropId_UnitTimePercentPeakMin,0},
	{"WaitCount","Wait count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_LockTimeAverage[]=
{
	{"WaitTime","Wait time",xbStatUnit_Percent,xbStatPropId_UnitTimePercent,0},
	{"WaitCount","Wait count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{"WaitTimePerCall","Wait time/op",xbStatUnit_MicroSec,xbStatPropId_cUnitPerCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_LockTimeSum[]=
{
	{"WaitTime","Wait time",xbStatUnit_MicroSec,xbStatPropId_cUnit,0},
	{"PeakMax","Peak max",xbStatUnit_MicroSec,xbStatPropId_cUnitPeakMax,0},
	{"PeakMin","Peak min",xbStatUnit_MicroSec,xbStatPropId_cUnitPeakMin,0},
	{"WaitCount","Wait count",xbStatUnit_OpCount,xbStatPropId_cCall,0},
	{"StatTime","Stat time",xbStatUnit_MicroSec,xbStatPropId_cTiStat,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatType xbStatTypeList_LockTime[]=
{
	{xbStatPropType_InstantPerSec,"Instant/seconds",xbStatPropList_LockTimeInstant},
//	{xbStatPropType_InstantPerMin,"Instant/seconds",xbStatPropList_LockTimeInstant},
	{xbStatPropType_Average,"Average",xbStatPropList_LockTimeAverage},
	{xbStatPropType_Sum,"Sum",xbStatPropList_LockTimeSum},
	{xbStatPropType_eol,NULL,NULL}
};

const xbStatDesc xbStatDesc_LockTime=
{
	"Lock times", xbStatTypeList_LockTime, 0
};

const xbStatProp xbStatPropList_ByteTputSimpleInstant[]=
{
	{"Throughput","Throughput",xbStatUnit_BytePerSec,xbStatPropId_cUnit,0},
	{"PeakMax","Peak max",xbStatUnit_BytePerSec,xbStatPropId_cUnitPeakMax,0},
	{"PeakMin","Peak min",xbStatUnit_BytePerSec,xbStatPropId_cUnitPeakMin,0},
	{"OpCount","Op count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_ByteTputSimpleAverage[]=
{
	{"Throughput","Throughput",xbStatUnit_BytePerSec,xbStatPropId_cUnit,0},
	{"OpCount","Op count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_ByteTputSimpleSum[]=
{
	{"ByteTransfered","Byte transfered",xbStatUnit_Byte,xbStatPropId_cUnit,0},
	{"OpCount","Op count",xbStatUnit_OpCount,xbStatPropId_cCall,0},
	{"StatTime","Stat time",xbStatUnit_MicroSec,xbStatPropId_cTiStat,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatType xbStatTypeList_ByteTputSimple[]=
{
	{xbStatPropType_InstantPerSec,"Instant/seconds",xbStatPropList_ByteTputSimpleInstant},
	{xbStatPropType_Average,"Average",xbStatPropList_ByteTputSimpleAverage},
	{xbStatPropType_Sum,"Sum",xbStatPropList_ByteTputSimpleSum},
	{xbStatPropType_eol,NULL,NULL}
};

const xbStatDesc xbStatDesc_ByteTputSimple=
{
	"Byte Throughput", xbStatTypeList_ByteTputSimple, 0
};

const xbStatProp xbStatPropList_CompressByteMuteInstant[]=
{
	{"Throughput","Throughput",xbStatUnit_BytePerSec,xbStatPropId_cUnit,0},
	{"CompressRatio","CompressRatio",xbStatUnit_Percent,xbStatPropId_Unit2PerUnitPercent,0},
	{"PeakMax","Peak max",xbStatUnit_Percent,xbStatPropId_Unit2PerUnitPercentPeakMax,0},
	{"PeakMin","Peak min",xbStatUnit_Percent,xbStatPropId_Unit2PerUnitPercentPeakMin,0},
	{"TimeRatio","TimeRatio",xbStatUnit_Percent,xbStatPropId_TimePercent,0},
	{"Throughput2","Throughput2",xbStatUnit_BytePerSec,xbStatPropId_cUnit2,0},
	{"OpCount","Op count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_CompressByteMuteAverage[]=
{
	{"Throughput","Throughput",xbStatUnit_BytePerSec,xbStatPropId_cUnit,0},
	{"CompressRatio","CompressRatio",xbStatUnit_Percent,xbStatPropId_Unit2PerUnitPercent,0},
	{"TimeRatio","TimeRatio",xbStatUnit_Percent,xbStatPropId_TimePercent,0},
	{"Throughput2","Throughput2",xbStatUnit_BytePerSec,xbStatPropId_cUnit2,0},
	{"OpCount","Op count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_CompressByteMuteSum[]=
{
	{"ByteCompressed","Byte compressed",xbStatUnit_Byte,xbStatPropId_cUnit,0},
	{"ByteTransfered","Byte transfered",xbStatUnit_Byte,xbStatPropId_cUnit2,0},
	{"TransferTime","Transfer time",xbStatUnit_MicroSec,xbStatPropId_cTi,0},
	{"OpCount","Op count",xbStatUnit_OpCount,xbStatPropId_cCall,0},
	{"StatTime","Stat time",xbStatUnit_MicroSec,xbStatPropId_cTiStat,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatType xbStatTypeList_CompressByteMute[]=
{
	{xbStatPropType_InstantPerSec,"Instant/seconds",xbStatPropList_CompressByteMuteInstant},
	{xbStatPropType_Average,"Average",xbStatPropList_CompressByteMuteAverage},
	{xbStatPropType_Sum,"Sum",xbStatPropList_CompressByteMuteSum},
	{xbStatPropType_eol,NULL,NULL}
};

const xbStatDesc xbStatDesc_CompressByteMute=
{
	"Compress", xbStatTypeList_CompressByteMute, 0
};

const xbStatDesc xbStatDesc_UncompressByteMute=
{
	"Uncompress", xbStatTypeList_CompressByteMute, 0
};

const xbStatProp xbStatPropList_OpInstant[]=
{
	{"OpCount","Op count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_OpAverage[]=
{
	{"OpCount","Op count",xbStatUnit_OpCountPerSec,xbStatPropId_cCall,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatProp xbStatPropList_OpSum[]=
{
	{"OpCount","Op count",xbStatUnit_OpCount,xbStatPropId_cCall,0},
	{"StatTime","Stat time",xbStatUnit_MicroSec,xbStatPropId_cTiStat,0},
	{NULL,NULL,NULL,0,0},
};

const xbStatType xbStatTypeList_Op[]=
{
	{xbStatPropType_InstantPerSec,"Instant/seconds",xbStatPropList_ByteTputSimpleInstant},
	{xbStatPropType_Average,"Average",xbStatPropList_ByteTputSimpleAverage},
	{xbStatPropType_Sum,"Sum",xbStatPropList_ByteTputSimpleSum},
	{xbStatPropType_eol,NULL,NULL}
};

const xbStatDesc xbStatDesc_Op=
{
	"Operations", xbStatTypeList_ByteTputSimple, 0
};
/*************************************************************************************
Stat engine implementation
**************************************************************************************/
xbStatEngine		g_xbStatEngine("Statistics Center");

xbStatEngine::xbStatEngine(const char *pLabel,const xbEngines &engUsed)
	:xbEngine(xbEngineStart_OnUse,pLabel,xbEngines(&g_xbThreadCenter,engUsed)),
	m_th(this)
{
	m_bRootAccessed=false;
}

void xbStatEngine::ConfigReload()
{
	g_xbPerfProcBug = 0;

	xbRefPtr<xbStatPointItemFolder> prRoot;
	RootGet(&prRoot,false);
	prRoot->ConfigReload();
	m_th.wakeUp();
}

bool xbStatEngine::PerfCounterBug() const
{
	return g_xbPerfProcBug==1;
}

void xbStatEngine::RootGet(xbRefPtr<xbStatPointItemFolder> *pprRoot,bool bStartWatchDog)
{
	xbLock lck(this);

	{
		xbSectLock lck(&m_sect);
		*pprRoot = m_prRoot;
	}
	if (bStartWatchDog)
		WatchDogStart();
}

bool xbStatEngine::IsWatchDogStarted() const
{
	return m_th.isStarted();
}

void xbStatEngine::WatchDogStart()
{
	m_bRootAccessed = true;
	if (m_th.isStarted())
		return;

	m_th.Create();
}

void xbStatEngine::WatchDogStop()
{
	m_th.AskDestroy("WatchDogStop called");
}

void xbStatEngine::onStart()
{
	xbRefPtr<xbStatPointItemFolder> prOldRoot;
	xbRefPtr<xbStatPointItemFolder> prRoot;
	RootCreateCb(&prRoot);
	{
		xbSectLock lck(&m_sect);
		prOldRoot = m_prRoot;
		m_prRoot = prRoot;
	}
	eProtectBegin();
	if (prOldRoot.IsValid())
		prOldRoot->Destroy();
	eProtectEnd();
}

bool xbStatEngine::onStop(bool)
{
	xbRefPtr<xbStatPointItemFolder> prOldRoot;
	{
		xbSectLock lck(&m_sect);
		prOldRoot = m_prRoot;
		m_prRoot.Empty();
	}
	bool bRet = m_th.AskDestroy("xbStatEngine stopping");
	eProtectBegin();
	if (prOldRoot.IsValid())
		prOldRoot->Destroy();
	eProtectEnd();
	return bRet;
}

DWORD	xbStatEngine::Thread::onThreadProc(sIxbThread *pTh)
{
	int cNothingToDo=0;
	ulong lastAccessTi = ::GetTickCount();
	while(m_eventPtr.ProcessToContinue())
	{
		m_pParent->m_cpu.update();

		if (!m_pParent->onPointWatch())
			cNothingToDo++;
		else
			cNothingToDo = 0;

		xbWait(cNothingToDo > 10?60000:1000,m_eventPtr);

		if (m_pParent->m_bRootAccessed)
		{
			m_pParent->m_bRootAccessed = false;

			lastAccessTi = ::GetTickCount();
		}
		else if (xbDiffTime(::GetTickCount(),lastAccessTi) > 60*60*1000)
			m_eventPtr.StopProcess();
	}
	return 0;
}

bool xbStatEngine::onPointWatch()
{
	xbRefPtr<xbStatPointItemFolder> prRoot;
	{
		xbSectLock lck(&m_sect);
		prRoot = m_prRoot;
	}
	if (prRoot.IsEmpty())
		return false;

	uint64 tiStart = ::xbGetTimeInMicroSec();
	int cDone = 0;
	prRoot->onPointWatch(cDone,tiStart);
	return cDone>0;
}

void xbStatEngine::onConfigLoad(xbStatPointItemBase *pItem,const xbString &path,xbCfgKey &cfgKey)
{
	xbString _path(xbCfgSlash(path));
	_path.Trim("/");
	if (_path.IsValid())
		_path = "Stat/"+_path;
	else
		_path = "Stat";

	cfgKey.Init(xbCfgCommon,_path);
	if (!(pItem->GetFlags() & xbSTATMODE_DONTCREATECFGKEY) && !cfgKey.Exists())
		cfgKey.CreateKey();
}

/*************************************************************************************
Stat point object base implementation
**************************************************************************************/
int		xbStatPointItemBase::findItemCb(const xbRefPtr<xbStatPointItemBase> &prPtr,const void *_id)
{
	return xbStricmp(prPtr->GetID(),(LPCSTR)_id);
}

void xbStatPointItemBase::FolderCreateCb(const xbString &parentPath,const xbString &id,xbRefPtr<xbStatPointItemBase> *pprFolder)
{
	xbStatPointItemFolder *ptr = xbDnew xbStatPointItemFolder();
	try
	{
		ptr->Create(m_pEngine,parentPath,id,NULL,NULL,NULL,0);
		*pprFolder = ptr;
	}
	catch(...)
	{
		ptr->Release();
		throw;
	}
	ptr->Release();
}

bool xbStatPointItemBase::HasPointEnabled() const
{
	xbSectLock lck(&m_sectData);
	for(int i = 0; i < m_arrPoint.Length; i++)
	{
		xbRefPtr<xbStatPointItemBase> prPtr(m_arrPoint.GetAt(i));
		lck.Unlock();
		if (prPtr->IsEnabled())
			return true;
		lck.Lock(&m_sectData);
	}
/*	const xbRefPtr<xbStatPointItemBase> *pprPtr = m_arrPoint.Begin();
	const xbRefPtr<xbStatPointItemBase> *pprPend = m_arrPoint.End();
	while(pprPtr < pprPend)
	{
		if ((*pprPtr)->IsEnabled())
			return true;
		pprPtr++;
	}*/
	return false;
}

bool xbStatPointItemBase::PointGet(int idx, xbRefPtr<xbStatPointItemBase> *pprChild)
{
	xbSectLock lck(&m_sectData);
	if (idx > m_arrPoint.Length)
		return false;

	if (pprChild)
		*pprChild = m_arrPoint.GetAt(idx);

	return true;
}

bool xbStatPointItemBase::PointGet(const xbString &path, xbRefPtr<xbStatPointItemBase> *pprChild,xbSTATCREATECB *pCreateCb)
{
	if (path.IsEmpty() || strchr(path,'/')!=NULL || strchr(path,'\\')!=NULL)
	{
		xbString subPath;
		int pos = 0;
		xbRefPtr<xbStatPointItemBase> prCur(this),prNext;
		while((pos = path.GetToken(pos,"/\\",&subPath)) > 0)
		{
			if (subPath.IsEmpty())
				continue;
			if (!prCur->PointGet(subPath,&prNext,pCreateCb?(pos>path.Length?pCreateCb:xbSTATCREATECB_FolderDflt):NULL))
				return false;
			prCur = prNext;
		}
		if (pprChild)
			*pprChild = prCur;

		return true;
	}
	xbSectLock lck(&m_sectData);

	int pos;
	xbRefPtr<xbStatPointItemBase> *pprPtr = m_arrPoint.SortedFind((LPCSTR)path,findItemCb,&pos);
	if (pprPtr)
	{
		if (pprChild)
			*pprChild = *pprPtr;

		return true;
	}
	if (!pCreateCb)
		return false;

	xbRefPtr<xbStatPointItemBase> prNew;
	if (pCreateCb == xbSTATCREATECB_FolderDflt)
		FolderCreateCb(GetPath(),path,&prNew);
	else
		pCreateCb->Call(GetPath(),path,&prNew);

	m_arrPoint.Insert(pos, prNew);
	if (pprChild)
		*pprChild = prNew;
	return true;
}

bool xbStatPointItemBase::PointDel(const xbString &path)
{
	if (strchr(path,'/')!=NULL || strchr(path,'\\')!=NULL)
	{
		xbString subPath;
		int pos = 0;
		xbRefPtr<xbStatPointItemBase> prCur(this),prNext;
		while((pos = path.GetToken(pos,"/\\",&subPath)) > 0)
		{
			if (subPath.IsEmpty())
				continue;
			if (pos>path.Length)
				return prCur->PointDel(subPath);

			if (!prCur->PointGet(subPath,&prNext))
				return false;
			prCur = prNext;
		}
		return false;
	}
	xbRefPtr<xbStatPointItemBase> prItem;
	xbSectLock lck(&m_sectData);

	int pos;
	xbRefPtr<xbStatPointItemBase> *pprPtr = m_arrPoint.SortedFind((LPCSTR)path,findItemCb,&pos);
	if (!pprPtr)
		return false;
	prItem = *pprPtr;
	m_arrPoint.Delete(pos);
	lck.Unlock();
	prItem->Destroy();

	return true;
}

void xbStatPointItemBase::SetEnabled(bool bVal,bool bRecursive,bool bCalledFromParent)
{
	xbSectLock lck(&m_sectData);
	uint64 flg;
	if (bCalledFromParent)
	{
		flg = (bVal?xbSTATFLAGS_PARENTENABLED:xbSTATFLAGS_PARENTDISABLED);
		if ((m_flags & (xbSTATFLAGS_PARENTENABLED|xbSTATFLAGS_PARENTDISABLED)) == flg)
			return;

		m_flags =(m_flags & ~ (xbSTATFLAGS_PARENTENABLED|xbSTATFLAGS_PARENTDISABLED|xbSTATFLAGS_CHKENABLED|xbSTATFLAGS_CHKDISABLED)) | flg;
	}
	else
	{
		flg = (bVal?xbSTATFLAGS_USERENABLED:xbSTATFLAGS_USERDISABLED);
		if ((m_flags & (xbSTATFLAGS_USERENABLED|xbSTATFLAGS_USERDISABLED|xbSTATFLAGS_PARENTENABLED|xbSTATFLAGS_PARENTDISABLED)) == flg)
			return;
		m_flags =(m_flags & ~ (xbSTATFLAGS_USERENABLED|xbSTATFLAGS_USERDISABLED|xbSTATFLAGS_PARENTENABLED|xbSTATFLAGS_PARENTDISABLED|xbSTATFLAGS_CHKENABLED|xbSTATFLAGS_CHKDISABLED)) | flg;
	}
	if (!bRecursive)
		return;
	for(int i = 0; i < m_arrPoint.Length; i++)
	{
		xbRefPtr<xbStatPointItemBase> prPtr(m_arrPoint.GetAt(i));
		lck.Unlock();
		prPtr->SetEnabled(bVal,true,true);
		lck.Lock(&m_sectData);
	}
}

void xbStatPointItemBase::SetSnapShot(bool bVal,bool bRecursive)
{
	xbSectLock lck(&m_sectData);
	if (bVal)
	{
		if (!(m_flags & xbSTATFLAGS_SNAPSHOT))
		{
			m_flags |= xbSTATFLAGS_SNAPSHOT;
			onStatSnapShotStart();
		}
	}
	else
	{
		if (m_flags & xbSTATFLAGS_SNAPSHOT)
		{
			m_flags &= ~xbSTATFLAGS_SNAPSHOT;
			onStatSnapShotStop();
		}
	}
	if (!bRecursive)
		return;
	for(int i = 0; i < m_arrPoint.Length; i++)
	{
		xbRefPtr<xbStatPointItemBase> prPtr(m_arrPoint.GetAt(i));
		lck.Unlock();
		prPtr->SetSnapShot(bVal,true);
		lck.Lock(&m_sectData);
	}
}

void xbStatPointItemBase::ConfigReload()
{
	xbSectLock lck(&m_sectData);
	m_flags &= ~(xbSTATFLAGS_CFGLOADED|xbSTATFLAGS_CHKDISABLED|xbSTATFLAGS_CHKENABLED);
	for(int i = 0; i < m_arrPoint.Length; i++)
	{
		xbRefPtr<xbStatPointItemBase> prPtr(m_arrPoint.GetAt(i));
		lck.Unlock();
		prPtr->ConfigReload();
		lck.Lock(&m_sectData);
	}
}

void xbStatPointItemBase::Reset(bool bRecursive)
{
	xbSectLock lck(&m_sectData);
	onStatReset();
	if (!bRecursive)
		return;
	for(int i = 0; i < m_arrPoint.Length; i++)
	{
		xbRefPtr<xbStatPointItemBase> prPtr(m_arrPoint.GetAt(i));
		lck.Unlock();
		prPtr->Reset(true);
		lck.Lock(&m_sectData);
	}
}

void xbStatPointItemBase::onEnabledCalc()
{
	xbSectLock lck(&m_sectData);
	if (m_flags & (xbSTATFLAGS_CHKDISABLED|xbSTATFLAGS_CHKENABLED))
		return;
	bool bVal = true;
	for(;;)
	{
		if (m_flags & (xbSTATFLAGS_PARENTDISABLED|xbSTATFLAGS_PARENTENABLED))
		{
			bVal = (m_flags & xbSTATFLAGS_PARENTENABLED)?true:false;
			break;
		}
		if (m_flags & (xbSTATFLAGS_USERDISABLED|xbSTATFLAGS_USERENABLED))
		{
			bVal = (m_flags & xbSTATFLAGS_USERENABLED)?true:false;
			break;
		}
		if (!(m_flags & xbSTATFLAGS_CFGLOADED))
		{
			onConfigLoad(lck);
			if (!lck.IsLocked())
				lck.Lock(&m_sectData);

			if (m_flags & (xbSTATFLAGS_CHKDISABLED|xbSTATFLAGS_CHKENABLED))
				return;
		}
		if (m_flags & (xbSTATFLAGS_CFGDISABLED|xbSTATFLAGS_CFGENABLED))
		{
			bVal = (m_flags & xbSTATFLAGS_CFGENABLED)?true:false;
			break;
		}
		if (m_flags & (xbSTATFLAGS_DISABLED|xbSTATFLAGS_ENABLED))
		{
			bVal = (m_flags & xbSTATFLAGS_ENABLED)?true:false;
			break;
		}
		break;
	}

	m_flags |= bVal?xbSTATFLAGS_CHKENABLED:xbSTATFLAGS_CHKDISABLED;
}

void xbStatPointItemBase::onConfigLoad(xbSectLock &sectData)
{
	xbStatEngine *pEngine = m_pEngine;
	m_flags &= ~xbSTATFLAGS_CFGDISABLED|xbSTATFLAGS_CFGENABLED;
	m_flags |= xbSTATFLAGS_CFGLOADED;
	if (!pEngine)
	{
		m_flags |= xbSTATFLAGS_CFGDISABLED;
		return;
	}
	sectData.Unlock();
	xbRefPtr<xbStatPointItemBase> prParent;
	if (m_parentPath.IsValid())
		pEngine->PointGet(m_parentPath,&prParent);
	if (prParent.IsValid())
		prParent->ConfigLoad();

	xbCfgKey cfgKey;
	pEngine->onConfigLoad(this, GetPath(), cfgKey);

	onConfigLoad(cfgKey,prParent);
}

void xbStatPointItemBase::onConfigLoad(xbCfgKey &cfgKey,xbStatPointItemBase *pParent)
{
	int enabled = 0;
	if (cfgKey.ValueExists("Enabled"))
	{
		if (cfgKey.Value["Enabled"].AsLong)
			enabled = 1;
		else
			enabled = -1;
	}
	else if(pParent)
	{
		uint64 flg = pParent->GetFlags()&(xbSTATFLAGS_CFGDISABLED|xbSTATFLAGS_CFGENABLED);
		if (flg)
			enabled = flg & xbSTATFLAGS_CFGDISABLED?-1:1;
	}

	if (enabled)
	{
		if (enabled>0)
		{
			xbSectLock lck(&m_sectData);
			m_flags &= ~xbSTATFLAGS_CFGDISABLED;
			m_flags |= xbSTATFLAGS_CFGENABLED;
		}
		else
		{
			xbSectLock lck(&m_sectData);
			m_flags &= ~xbSTATFLAGS_CFGENABLED;
			m_flags |= xbSTATFLAGS_CFGDISABLED;
		}
	}
}

void xbStatPointItemBase::onPointWatch(int &cDone,uint64 tiStart)
{
	xbSectLock lck(&m_sectData);

	for(int i = 0; i < m_arrPoint.Length; i++)
	{
		xbRefPtr<xbStatPointItemBase> prPtr(m_arrPoint.GetAt(i));
		lck.Unlock();
		prPtr->onPointWatch(cDone,tiStart);
		lck.Lock(&m_sectData);
	}
}

bool xbStatPointItemBase::PropertyTypeEnum(int &idx, xbStatPropType *pType, xbString *pLabel)
{
	if (!m_pDesc)
		return false;
	if (idx < 1)
		idx = 1;
	if (m_pDesc->m_pTypeList[idx-1].m_propType == xbStatPropType_eol)
		return false;
	if (pType)
		*pType = m_pDesc->m_pTypeList[idx-1].m_propType;
	if (pLabel)
	{
		if (m_pDesc->m_pTypeList[idx-1].m_pLabel)
			*pLabel = m_pDesc->m_pTypeList[idx-1].m_pLabel;
		else
			pLabel->Empty();
	}
	idx++;
	return true;
}

bool xbStatPointItemBase::HasPropertyType(xbStatPropType type)
{
	if (!m_pDesc)
		return false;
	const xbStatType *pType = m_pDesc->m_pTypeList;
	while(pType->m_propType != xbStatPropType_eol)
	{
		if (pType->m_propType == type)
			return true;
		pType++;
	}
	return false;
}
const xbStatProp *xbStatPointItemBase::propListFind(xbStatPropType type)
{
	if (!m_pDesc)
		return NULL;

	const xbStatType *pType = m_pDesc->m_pTypeList;
	while(pType->m_propType != xbStatPropType_eol)
	{
		if (pType->m_propType == type)
			return pType->m_pPropList;
		pType++;
	}

	return NULL;
}

bool xbStatPointItemBase::PropertyEnum(xbStatPropType type,int &idx, xbString *pLabel, const xbStatUnit	**pUnit, 
					int64 *pIntValue, double *pDblValue, xbString *pStringValue, 
					xbString *pId, int *pDispId)
{
	const xbStatProp *pProp = propListFind(type);
	if (!pProp)
		return false;
	if (idx < 1)
		idx = 1;
	const xbStatProp &prop = pProp[idx-1];
	if (!prop.m_pId)
		return false;
	if (pLabel)
	{
		if (prop.m_pLabel)
			*pLabel = prop.m_pLabel;
		else
			pLabel->Empty();
	}
	if (pUnit)
		*pUnit = prop.m_pUnit;
	if (pId)
	{
		if (prop.m_pId)
			*pId = prop.m_pId;
		else
			pId->Empty();
	}
	if (pDispId)
		*pDispId = prop.m_dispId;
	if (pIntValue || pDblValue || pStringValue)
	{
		int64 intVal;
		double dblVal;
		if (pStringValue)
		{
			pStringValue->Empty();
			if (!pIntValue)
				pIntValue = &intVal;
			if (!pDblValue)
				pDblValue = &dblVal;
		}
		if (pIntValue)
			*pIntValue = xbSTAT_NOTDEF;
		if (pDblValue)
			*pDblValue = 0.0;

		if (onPropertyGet(type,prop.m_dispId,pIntValue,pDblValue,pStringValue,0,NULL) && pStringValue && pStringValue->IsEmpty())
		{
			*pStringValue = xbStatUnit::ToString(prop.m_pUnit, *pIntValue, *pDblValue); 
		}
	}
	idx++;
	return true;
}

bool xbStatPointItemBase::PropertyGet(xbStatPropType type,LPCSTR pId, xbString *pLabel, const xbStatUnit	**pUnit, 
					int64 *pIntValue,double *pDblValue, xbString *pStringValue,
					int sampleCnt,int *pSampleCnt,int *pDispId)
{
	const xbStatProp *pProp = propListFind(type);
	if (!pProp)
		return false;
	if (pId)
	{
		for(;;)
		{
			if (!pProp->m_pId)
				return false;
			if (xbStricmp(pProp->m_pId,pId)==0)
				break;
			pProp++;
		}
	}
	if (pLabel)
	{
		if (pProp->m_pLabel)
			*pLabel = pProp->m_pLabel;
		else
			pLabel->Empty();
	}
	if (pUnit)
		*pUnit = pProp->m_pUnit;
	if (pDispId)
		*pDispId = pProp->m_dispId;
	if (pSampleCnt)
		*pSampleCnt = 0;
	if (pIntValue || pDblValue || pStringValue)
	{
		int64 intVal;
		double dblVal;
		if (pStringValue)
		{
			pStringValue->Empty();
			if (!pIntValue)
				pIntValue = &intVal;
			if (!pDblValue)
				pDblValue = &dblVal;
		}
		if (pIntValue)
			*pIntValue = xbSTAT_NOTDEF;
		if (pDblValue)
			*pDblValue = 0.0;

		if (!onPropertyGet(type,pProp->m_dispId,pIntValue,pDblValue,pStringValue,sampleCnt,pSampleCnt))
			return false;

		if (pStringValue && pStringValue->IsEmpty())
		{
			*pStringValue = xbStatUnit::ToString(pProp->m_pUnit, *pIntValue, *pDblValue); 
		}
	}
	else
	{
		if (pSampleCnt && !onPropertyGet(type,pProp->m_dispId,NULL,NULL,NULL,0,pSampleCnt))
			return false;
	}
	return true;
}

bool xbStatPointItemBase::PropertyGet(xbStatPropType type,int dispId, xbString *pLabel, const xbStatUnit	**pUnit, 
					int64 *pIntValue,double *pDblValue, xbString *pStringValue,
					int sampleCnt,int *pSampleCnt,xbString *pId)
{
	const xbStatProp *pProp = propListFind(type);
	if (!pProp)
		return false;
	if (dispId!=-1)
	{
		for(;;)
		{
			if (!pProp->m_pId)
				return false;
			if (pProp->m_dispId == dispId)
				break;
			pProp++;
		}
	}
	if (pLabel)
	{
		if (pProp->m_pLabel)
			*pLabel = pProp->m_pLabel;
		else
			pLabel->Empty();
	}
	if (pUnit)
		*pUnit = pProp->m_pUnit;
	if (pId)
	{
		if (pProp->m_pId)
			*pId = pProp->m_pId;
		else
			pId->Empty();
	}
	if (pSampleCnt)
		*pSampleCnt = 0;
	if (pIntValue || pDblValue || pStringValue)
	{
		int64 intVal;
		double dblVal;
		if (pStringValue)
		{
			pStringValue->Empty();
			if (!pIntValue)
				pIntValue = &intVal;
			if (!pDblValue)
				pDblValue = &dblVal;
		}
		if (pIntValue)
			*pIntValue = xbSTAT_NOTDEF;
		if (pDblValue)
			*pDblValue = 0.0;

		if (!onPropertyGet(type,pProp->m_dispId,pIntValue,pDblValue,pStringValue,sampleCnt,pSampleCnt))
			return false;

		if (pStringValue && pStringValue->IsEmpty())
		{
			*pStringValue = xbStatUnit::ToString(pProp->m_pUnit, *pIntValue, *pDblValue); 
		}
	}
	else
	{
		if (pSampleCnt && !onPropertyGet(type,pProp->m_dispId,NULL,NULL,NULL,0,pSampleCnt))
			return false;
	}
	return true;
}

bool xbStatPointItemBase::HasProperty(xbStatPropType type,int dispId)
{
	if (!m_pDesc)
		return false;
	const xbStatProp *pProp = propListFind(type);
	if (!pProp)
		return false;
	for(;;)
	{
		if (!pProp->m_pId)
			return false;
		if (pProp->m_dispId == dispId)
			break;
		pProp++;
	}
	return true;
}

/*************************************************************************************
Stat point object default implementation for counter, sum, throughput, mute
**************************************************************************************/
bool xbStatPointItemDfltImpl::onPropertyGet_Counter(xbStatPropType type,int dispId,int64 *pIntValue,double *pDblValue, xbString *pStringValue,int sampleCnt,int *pSampleCnt)
{
	switch(dispId)
	{
	case xbStatPropId_cCall:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cCall);
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_tiStart || !m_cur.m_tiStart || m_sum.m_tiStart >= m_cur.m_tiStart)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cCall)/(double(m_cur.m_tiStart) - double(m_sum.m_tiStart))*1000000.0;
			return true;
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			*pIntValue = m_sum.m_cCall;
			return true;
		}
		
		break;
	case xbStatPropId_cTiStat:
		switch(type)
		{
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			if (!m_sum.m_tiStart || !m_cur.m_tiStart || m_sum.m_tiStart >= m_cur.m_tiStart)
				*pIntValue = 0;
			else
				*pIntValue = m_cur.m_tiStart - m_sum.m_tiStart;
			return true;
		}
		break;
	}
	return false;
}

bool xbStatPointItemDfltImpl::onPropertyGet_Sum(xbStatPropType type,int dispId,int64 *pIntValue,double *pDblValue, xbString *pStringValue,int sampleCnt,int *pSampleCnt)
{
	if (onPropertyGet_Counter(type,dispId,pIntValue,pDblValue,pStringValue,sampleCnt,pSampleCnt))
		return true;

	switch(dispId)
	{
	case xbStatPropId_cUnit:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cUnit);
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_tiStart || !m_cur.m_tiStart || m_sum.m_tiStart >= m_cur.m_tiStart)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit)/(double(m_cur.m_tiStart) - double(m_sum.m_tiStart))*1000000.0;
			return true;
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			*pIntValue = m_sum.m_cUnit;
			return true;
		}
		
		break;
	case xbStatPropId_cUnitPerCall:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cUnitPerCall);
			return true;
		case xbStatPropType_Average:
//		case xbStatPropType_Sum:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_cCall)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit)/double(m_sum.m_cCall);
			return true;
		}
		
		break;
	case xbStatPropId_cUnitPeakMax:
	case xbStatPropId_cUnitPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId);
			return true;
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			*pIntValue = dispId == xbStatPropId_cUnitPeakMin?m_peakMin.m_cUnit:m_peakMax.m_cUnit;
			return true;
		}
		
		break;
	case xbStatPropId_UnitTimePercent:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cUnit)/1000000.0*100.0;
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_tiStart || !m_cur.m_tiStart || m_sum.m_tiStart >= m_cur.m_tiStart)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit)/(double(m_cur.m_tiStart) - double(m_sum.m_tiStart))*100.0;
			return true;
		}
		
		break;
	case xbStatPropId_UnitTimePercentPeakMax:
	case xbStatPropId_UnitTimePercentPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId==xbStatPropId_UnitTimePercentPeakMin?xbStatPropId_cUnitPeakMin:xbStatPropId_cUnitPeakMax)/1000000.0*100.0;
			return true;
		}
	}
	return false;
}

bool xbStatPointItemDfltImpl::onPropertyGet_Tput(xbStatPropType type,int dispId,int64 *pIntValue,double *pDblValue, xbString *pStringValue,int sampleCnt,int *pSampleCnt)
{
	if (onPropertyGet_Sum(type,dispId,pIntValue,pDblValue,pStringValue,sampleCnt,pSampleCnt))
		return true;
	switch(dispId)
	{
	case xbStatPropId_cTi:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cTi)/1000000.0*100.0; //Pourcentage temps/temps
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_tiStart || !m_cur.m_tiStart || m_sum.m_tiStart >= m_cur.m_tiStart)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cTi)/(double(m_cur.m_tiStart) - double(m_sum.m_tiStart))*100.0;
			return true;
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			*pIntValue = m_sum.m_cTi;
			return true;
		}
		
		break;
	case xbStatPropId_cTiPerCall:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cTiPerCall);
			return true;
		case xbStatPropType_Average:
//		case xbStatPropType_Sum:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_cCall)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cTi)/double(m_sum.m_cCall);
			return true;
		}
		
		break;
	case xbStatPropId_cTiPeakMax:
	case xbStatPropId_cTiPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId);
			return true;
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			*pIntValue = dispId == xbStatPropId_cTiPeakMin?m_peakMin.m_cTi:m_peakMax.m_cTi;
			return true;
		}
		
		break;
	case xbStatPropId_cUnitPerTi:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cUnitPerTi);
			return true;
		case xbStatPropType_Average:
//		case xbStatPropType_Sum:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_cTi)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit)/double(m_sum.m_cTi);
			return true;
		}
		
		break;
	case xbStatPropId_cUnitPerTiPeakMax:
	case xbStatPropId_cUnitPerTiPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId);
			return true;
		}
		
		break;
	case xbStatPropId_TimePercent:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cTi)/1000000.0*100.0;
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_tiStart || !m_cur.m_tiStart || m_sum.m_tiStart >= m_cur.m_tiStart)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cTi)/(double(m_cur.m_tiStart) - double(m_sum.m_tiStart))*100.0;
			return true;
		}
		
		break;
	case xbStatPropId_TimePercentPeakMax:
	case xbStatPropId_TimePercentPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId==xbStatPropId_TimePercentPeakMin?xbStatPropId_cTiPeakMin:xbStatPropId_cTiPeakMax)/1000000.0*100.0;
			return true;
		}
	}
	return false;
}

bool xbStatPointItemDfltImpl::onPropertyGet_Mute(xbStatPropType type,int dispId,int64 *pIntValue,double *pDblValue, xbString *pStringValue,int sampleCnt,int *pSampleCnt)
{
	if (onPropertyGet_Tput(type,dispId,pIntValue,pDblValue,pStringValue,sampleCnt,pSampleCnt))
		return true;
	switch(dispId)
	{
	case xbStatPropId_cUnit2:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cUnit2);
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_tiStart || !m_cur.m_tiStart || m_sum.m_tiStart >= m_cur.m_tiStart)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit2)/(double(m_cur.m_tiStart) - double(m_sum.m_tiStart))*1000000.0;
			return true;
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			*pIntValue = m_sum.m_cUnit2;
			return true;
		}
		
		break;
	case xbStatPropId_cUnit2PerCall:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cUnit2PerCall);
			return true;
		case xbStatPropType_Average:
//		case xbStatPropType_Sum:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_cCall)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit2)/double(m_sum.m_cCall);
			return true;
		}
		
		break;
	case xbStatPropId_cUnit2PeakMax:
	case xbStatPropId_cUnit2PeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId);
			return true;
		case xbStatPropType_Sum:
			if (!pIntValue)
				return false;
			*pIntValue = dispId == xbStatPropId_cUnitPeakMin?m_peakMin.m_cUnit2:m_peakMax.m_cUnit2;
			return true;
		}
		
		break;
	case xbStatPropId_cUnit2PerTi:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_cUnit2PerTi);
			return true;
		case xbStatPropType_Average:
//		case xbStatPropType_Sum:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_cTi)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit2)/double(m_sum.m_cTi);
			return true;
		}
		
		break;
	case xbStatPropId_cUnit2PerTiPeakMax:
	case xbStatPropId_cUnit2PerTiPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId);
			return true;
		}
		
		break;
	case xbStatPropId_UnitPerUnit2Percent:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_UnitPerUnit2Percent);
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_cUnit2)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit)/double(m_cur.m_cUnit2)*100.0;
			return true;
		}
		
		break;
	case xbStatPropId_UnitPerUnit2PercentPeakMax:
	case xbStatPropId_UnitPerUnit2PercentPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId);
			return true;
		}
	case xbStatPropId_Unit2PerUnitPercent:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(xbStatPropId_Unit2PerUnitPercent);
			return true;
		case xbStatPropType_Average:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			if (!m_sum.m_cUnit)
				*pDblValue = 0.0;
			else
				*pDblValue = double(m_sum.m_cUnit2)/double(m_cur.m_cUnit)*100.0;
			return true;
		}
		
		break;
	case xbStatPropId_Unit2PerUnitPercentPeakMax:
	case xbStatPropId_Unit2PerUnitPercentPeakMin:
		switch(type)
		{
		case xbStatPropType_InstantPerSec:
			if (pIntValue)
				*pIntValue = xbSTAT_ISDBL;
			if (!pDblValue)
				return false;
			*pDblValue = calcPerSec(dispId);
			return true;
		}
	}
	return false;
}


double xbStatPointItemDfltImpl::calcPerSec(int dispId)
{
	xbSectLock lck(&m_sectData);
	if (m_noSample <= 0)
		return 0.0;
	double	ret = 0.0;
	int64	retDiv = 1;
	int cSample = m_bSampleLoop?m_cSample:m_noSample;
	for(int i = 0; i < cSample; i++)
	{
		const StatRecStore &st = m_arrSample.GetAt(i);
		int64 tiDiff;
		if (i == m_noSample - 1)
		{
			if (!m_cur.m_tiStart)
				break;
			tiDiff = m_cur.m_tiStart - st.m_tiStart;
		}
		else
		{
			if (i>=m_cSample-1)
				tiDiff = m_arrSample.First().m_tiStart - st.m_tiStart;
			else
				tiDiff = m_arrSample.GetAt(i+1).m_tiStart - st.m_tiStart;
		}
		xbRdbgAssert(tiDiff>0);
		calcPerSec(dispId, st, double(tiDiff)/1000000, ret, retDiv, i);
	}

	return ret/retDiv;
}

void xbStatPointItemDfltImpl::calcPerSec(int dispId, const StatRecStore &st, double tiDiff, double &ret, int64 &retDiv, int no)
{
	double val;
	switch(dispId)
	{
	case xbStatPropId_cCall:
		ret += double(st.m_cCall)/tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cUnit:
		ret += double(st.m_cUnit)/tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cUnitPeakMin:
		val = double(st.m_cUnit)/tiDiff;
		if (no==1 || val < ret)
			ret = val;
		break;
	case xbStatPropId_cUnitPeakMax:
		val = double(st.m_cUnit)/tiDiff;
		if (no==1 || val > ret)
			ret = val;
		break;
	case xbStatPropId_cUnitPerCall:
		if (!st.m_cCall)
			break;
		ret += double(st.m_cUnit)/double(st.m_cCall);// /tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cTi:
		ret += double(st.m_cTi)/tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cTiPeakMin:
		val = double(st.m_cTi)/tiDiff;
		if (no==1 || val < ret)
			ret = val;
		break;
	case xbStatPropId_cTiPeakMax:
		val = double(st.m_cTi)/tiDiff;
		if (no==1 || val > ret)
			ret = val;
		break;
	case xbStatPropId_cTiPerCall:
		if (!st.m_cCall)
			break;
		ret += double(st.m_cTi)/double(st.m_cCall); // /tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cUnitPerTi:
		if (!st.m_cTi)
			break;
		ret += double(st.m_cUnit)/double(st.m_cTi); // /tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cUnitPerTiPeakMin:
		if (!st.m_cTi)
			break;
		val = double(st.m_cUnit)/double(st.m_cTi); // /tiDiff;
		if (no==1 || val < ret)
			ret = val;
		break;
	case xbStatPropId_cUnitPerTiPeakMax:
		if (!st.m_cTi)
			break;
		val = double(st.m_cUnit)/double(st.m_cTi); // /tiDiff;
		if (no==1 || val > ret)
			ret = val;
		break;
	case xbStatPropId_cUnit2:
		ret += double(st.m_cUnit2)/tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cUnit2PeakMin:
		val = double(st.m_cUnit2)/tiDiff;
		if (no==1 || val < ret)
			ret = val;
		break;
	case xbStatPropId_cUnit2PeakMax:
		val = double(st.m_cUnit2)/tiDiff;
		if (no==1 || val > ret)
			ret = val;
		break;
	case xbStatPropId_cUnit2PerCall:
		if (!st.m_cCall)
			break;
		ret += double(st.m_cUnit2)/double(st.m_cCall);// /tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cUnit2PerTi:
		if (!st.m_cTi)
			break;
		ret += double(st.m_cUnit2)/double(st.m_cTi); // /tiDiff;
		retDiv++;
		break;
	case xbStatPropId_cUnit2PerTiPeakMin:
		if (!st.m_cTi)
			break;
		val = double(st.m_cUnit2)/double(st.m_cTi); // /tiDiff;
		if (no==1 || val < ret)
			ret = val;
		break;
	case xbStatPropId_cUnit2PerTiPeakMax:
		if (!st.m_cTi)
			break;
		val = double(st.m_cUnit2)/double(st.m_cTi); // /tiDiff;
		if (no==1 || val > ret)
			ret = val;
		break;
	case xbStatPropId_Unit2PerUnitPercent:
		if (!st.m_cUnit)
			break;
		ret += double(st.m_cUnit2)/double(st.m_cUnit)*100.0; // /tiDiff;
		retDiv++;
		break;
	case xbStatPropId_Unit2PerUnitPercentPeakMax:
		if (!st.m_cUnit)
			break;
		val = double(st.m_cUnit2)/double(st.m_cUnit)*100.0; // /tiDiff;
		if (no==1 || val < ret)
			ret = val;
		break;
	case xbStatPropId_Unit2PerUnitPercentPeakMin:
		if (!st.m_cUnit)
			break;
		val = double(st.m_cUnit2)/double(st.m_cUnit)*100.0; // /tiDiff;
		if (no==1 || val > ret)
			ret = val;
		break;
	case xbStatPropId_UnitPerUnit2Percent:
		if (!st.m_cUnit2)
			break;
		ret += double(st.m_cUnit)/double(st.m_cUnit2)*100.0; // /tiDiff;
		retDiv++;
		break;
	case xbStatPropId_UnitPerUnit2PercentPeakMax:
		if (!st.m_cUnit2)
			break;
		val = double(st.m_cUnit)/double(st.m_cUnit2)*100.0; // /tiDiff;
		if (no==1 || val < ret)
			ret = val;
		break;
	case xbStatPropId_UnitPerUnit2PercentPeakMin:
		if (!st.m_cUnit2)
			break;
		val = double(st.m_cUnit)/double(st.m_cUnit2)*100.0; // /tiDiff;
		if (no==1 || val > ret)
			ret = val;
		break;
	default:
		xbRdbgAssert(!"calcPerSec not impl dispid");
	}
}

xbString xbStatUnit::ToString(const xbStatUnit *pUnit, int64 intValue, double dblValue, bool bShort)
{
	if (intValue == xbSTAT_ISDBL)
		return DblToString(pUnit,dblValue,bShort);
	return IntToString(pUnit,intValue,bShort);
}

xbString xbStatUnit::IntToString(const xbStatUnit *pUnit, int64 intValue, bool bShort)
{
	if (intValue >= xbSTAT_INTMAX || intValue <= xbSTAT_INTMIN)
		return "-";
	if (!pUnit)
		return xbString(intValue);

	uint64 intValueAbs = (uint64)((intValue>=0)?intValue:-intValue);
	const xbStatUnit *pUnitPtr = pUnit;
	while(pUnitPtr->m_unitDiv)
	{
		if (pUnitPtr->m_unitDiv<intValueAbs)
			break;
		pUnitPtr++;
	}
	xbString ret;
	for(;;)
	{
		if (!pUnitPtr->m_unitDiv)
		{
			if (ret.IsValid())
				ret += " ";

			ret += xbString(intValue);
			ret += xbString(bShort&&pUnitPtr->m_pUnitShortStr?pUnitPtr->m_pUnitShortStr:(intValueAbs>1?pUnitPtr->m_pUnitStr:pUnitPtr->m_pUnitPlurStr));
			if (pUnit->m_displayMode & xbSTATUNIT_PERSEC)
				ret += xbString(bShort?"/s":" per second");
			return ret;
		}
		int64 div = pUnitPtr->m_unitDiv/((pUnitPtr+1)->m_unitDiv?(pUnitPtr+1)->m_unitDiv:1);
		if (div == 1024 || div == 1000)
		{
			if (ret.IsValid())
				ret += " ";

			double dVal(double(intValue)/pUnitPtr->m_unitDiv);
			ret += xbFormat("%.3lf",dVal);
			ret += xbString(bShort&&pUnitPtr->m_pUnitShortStr?pUnitPtr->m_pUnitShortStr:((dVal>=2.0||dVal<=-2.0)?pUnitPtr->m_pUnitStr:pUnitPtr->m_pUnitPlurStr));
			if (pUnit->m_displayMode & xbSTATUNIT_PERSEC)
				ret += xbString(bShort?"/s":" per second");
			return ret;
		}

		int64 thisIntValue = intValue/pUnitPtr->m_unitDiv;
		intValue -= thisIntValue * pUnitPtr->m_unitDiv;
		if (thisIntValue || !intValue)
		{
			if (ret.IsValid())
				ret += " ";

			ret += xbString(thisIntValue);
			ret += xbString(bShort&&pUnitPtr->m_pUnitShortStr?pUnitPtr->m_pUnitShortStr:(thisIntValue>1?pUnitPtr->m_pUnitStr:pUnitPtr->m_pUnitPlurStr));
			if (!intValue)
			{
				if (pUnit->m_displayMode & xbSTATUNIT_PERSEC)
					ret += xbString(bShort?"/s":" per second");
				return ret;
			}
		}
		pUnitPtr++;
	}
}

xbString xbStatUnit::DblToString(const xbStatUnit *pUnit, double dblValue, bool bShort)
{
	if (!pUnit)
		return xbFormat("%.3lf",dblValue);
	xbString timeUnit;
	if (pUnit->m_displayMode & xbSTATUNIT_PERSEC)
	{
		int noTimeUnit=0;
		if (dblValue != 0.0 && !(pUnit->m_displayMode & xbSTATUNIT_PERSECNOAUTOADJ))
		{
			while(dblValue < 10.0 && noTimeUnit < 2)
			{
				dblValue *= 60.0;
				noTimeUnit++;
			}
		}
		switch(noTimeUnit)
		{
		case 0:
			timeUnit = xbString(bShort?"/s":" per second");
			break;
		case 1:
			timeUnit = xbString(bShort?"/min":" per minute");
			break;
		case 2:
			timeUnit = xbString(bShort?"/h":" per hour");
			break;
		}
	}

	double dblValueAbs = (dblValue>=0)?dblValue:-dblValue;
	const xbStatUnit *pUnitPtr = pUnit;
	while(pUnitPtr->m_unitDiv)
	{
		if (pUnitPtr->m_unitDiv<dblValueAbs)
			break;
		pUnitPtr++;
	}
	xbString ret;
	for(;;)
	{
		if (!pUnitPtr->m_unitDiv)
		{
			if (ret.IsValid())
				ret += " ";

			ret += xbFormat("%.3lf",dblValue);
			ret += xbString(bShort&&pUnitPtr->m_pUnitShortStr?pUnitPtr->m_pUnitShortStr:(dblValueAbs>=2.0?pUnitPtr->m_pUnitStr:pUnitPtr->m_pUnitPlurStr));
			if (pUnit->m_displayMode & xbSTATUNIT_PERSEC)
				ret += timeUnit;
			return ret;
		}
		int64 div = pUnitPtr->m_unitDiv/((pUnitPtr+1)->m_unitDiv?(pUnitPtr+1)->m_unitDiv:1);
		if (div == 1024 || div == 1000)
		{
			if (ret.IsValid())
				ret += " ";

			double dVal(dblValue/pUnitPtr->m_unitDiv);
			ret += xbFormat("%.3lf",dVal);
			ret += xbString(bShort&&pUnitPtr->m_pUnitShortStr?pUnitPtr->m_pUnitShortStr:((dVal>=2.0||dVal<=-2.0)?pUnitPtr->m_pUnitStr:pUnitPtr->m_pUnitPlurStr));
			if (pUnit->m_displayMode & xbSTATUNIT_PERSEC)
				ret += timeUnit;
			return ret;
		}

		int64 thisIntValue = int64(dblValue/pUnitPtr->m_unitDiv);
		dblValue -= thisIntValue * pUnitPtr->m_unitDiv;
		if (thisIntValue || dblValue == 0.0)
		{
			if (ret.IsValid())
				ret += " ";

			ret += xbString(thisIntValue);
			ret += xbString(bShort&&pUnitPtr->m_pUnitShortStr?pUnitPtr->m_pUnitShortStr:(thisIntValue>1?pUnitPtr->m_pUnitStr:pUnitPtr->m_pUnitPlurStr));
			if (dblValue == 0.0)
			{
				if (pUnit->m_displayMode & xbSTATUNIT_PERSEC)
					ret += timeUnit;
				return ret;
			}
		}
		pUnitPtr++;
	}
}

xbCpuUsage::xbCpuUsage(HANDLE hProcess,bool bSysGet)
{
	m_noSample = -2;
	m_bSampleFull = false;
	m_hProcess = hProcess;
    if (bSysGet)
		m_hKernel = LoadLibrary( "Kernel32.dll" );
	else
		m_hKernel = NULL;
    if( m_hKernel )
    {
        m_pfnGetSystemTimes = (pfnGetSystemTimes)GetProcAddress( m_hKernel, "GetSystemTimes" );
        if( m_pfnGetSystemTimes == NULL )
        {
            xbFreeLibrary( m_hKernel ); 
			m_hKernel = NULL;
        }
    }
}
xbCpuUsage::~xbCpuUsage()
{
    if( m_hKernel )
        xbFreeLibrary( m_hKernel ); 
}

long xbCpuUsage::processGet() const
{
	xbSectLock lck(&m_sect);
	long cpu = 0;
	long cSample = (m_bSampleFull?xbCpuUsage_SAMPLECOUNT:m_noSample+1);
	if (cSample>0&&m_hProcess)
	{
		for(long i =0;i <cSample;i++)
		{
			cpu += m_cpu[i];
		}

		cpu /= cSample;
		cpu /= g_xbEngineCenter.GetSysCoreCount();
	}
	return cpu;
}

long xbCpuUsage::systemGet() const
{
	xbSectLock lck(&m_sect);
	long cSample = (m_bSampleFull?xbCpuUsage_SAMPLECOUNT:m_noSample+1);
	long sysCpu = 0;
	if (cSample>0&&m_hKernel)
	{
		for(long i =0;i <cSample;i++)
		{
			sysCpu += m_sysCpu[i];
		}

		sysCpu /= cSample;
	}
	return sysCpu;
}

bool xbCpuUsage::get(long *pProcCpu,long *pSysCpu) const
{
	xbSectLock lck(&m_sect);
	long cSample = (m_bSampleFull?xbCpuUsage_SAMPLECOUNT:m_noSample+1);
	long cpu = 0,sysCpu = 0;
	if (cSample>0 && (!pProcCpu||m_hProcess) && (!pSysCpu||m_hKernel))
	{
		for(long i =0;i <cSample;i++)
		{
			cpu += m_cpu[i];
			sysCpu += m_sysCpu[i];
		}

		sysCpu /= cSample;
		cpu /= cSample;
		cpu /= g_xbEngineCenter.GetSysCoreCount();
	}
	else
		cSample = 0;
	if (pProcCpu)
		*pProcCpu = cpu;
	if (pSysCpu)
		*pSysCpu = sysCpu; 
	return cSample > 3;
}

void xbCpuUsage::update()
{
	xbSectLock lck(&m_sect);

	m_noSample++;
	if (m_noSample>=xbCpuUsage_SAMPLECOUNT)
	{
		m_noSample=0;
		m_bSampleFull = true;
	}

	uint64 ti,kernelTimeProcess=0,userTimeProcess=0;
	GetSystemTimeAsFileTime( (LPFILETIME)&ti );

	if (m_noSample>=0)
	{
		if (ti <= m_lastTi + 100000)
		{
			if (m_noSample==0 && m_bSampleFull)
				m_noSample = xbCpuUsage_SAMPLECOUNT-1;
			else
				m_noSample--;

			return;
		}
	}

	if (m_hProcess)
    {
        FILETIME createTime;
        FILETIME exitTime;
        GetProcessTimes( m_hProcess, &createTime, &exitTime, 
                         (LPFILETIME)&kernelTimeProcess, 
                         (LPFILETIME)&userTimeProcess );
    }

	uint64 idleTime=0,kernelTime=0,userTime=0;
	if (m_pfnGetSystemTimes)
		(*m_pfnGetSystemTimes)( (LPFILETIME)&idleTime, (LPFILETIME)&kernelTime, (LPFILETIME)&userTime );

	long cpu = 0,sysCpu = 0;

	if (m_noSample>=0)
	{
		cpu = long( ( ( ( userTimeProcess - m_lastUserTime ) + ( kernelTimeProcess - m_lastKernelTime ) ) *100 ) / (ti - m_lastTi) );

		uint64 usr = userTime   - m_lastSysUserTime;
		uint64 ker = kernelTime - m_lastSysKernelTime;
		uint64 idl = idleTime   - m_lastSysIdleTime;

		uint64 sys = (usr + ker);

		if( sys == 0 )
			sysCpu = 0;
		else
			sysCpu = long( (sys - idl) *100 / sys ); // System Idle take 100 % of cpu :-((

		m_cpu[m_noSample] = cpu;
		m_sysCpu[m_noSample] = sysCpu;
	}

	m_lastTi = ti;
	m_lastUserTime = userTimeProcess;
	m_lastKernelTime = kernelTimeProcess;

	m_lastSysUserTime = userTime;
	m_lastSysKernelTime = kernelTime;
	m_lastSysIdleTime = idleTime;
}

#pragma warning(pop)
