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

#include "xbHotPlug.h"

#pragma warning(push)
#pragma warning(disable: 4355) //'this' : used in base member initializer list
static xbHotPlugFactory *g_embeddedFactory = NULL;

//////////////////////////////////////////////////////////////////////////////////////////
// xbHotPlugType
//////////////////////////////////////////////////////////////////////////////////////////
xbHotPlugType::xbHotPlugType(const char *pPlugTypeName,const char *pPlugTypeVer,const char *pLabel,const xbEngines &engUsed)
	:xbEngine(xbEngineStart_OnUse,pLabel,xbEngines(&g_xbCfgEngine,engUsed)),
	m_cfgChangedCb(
	xbCbCast(xbCFGCHANGED,xbEngine,(xbCfgChangedCb &,xbCfgRoot,xbCfgKeyChangesInfo *,const xbString &),this,xbHotPlugType::receiveConfigChanged)
	)
{
	m_cPlugin = 0;
	m_nLockCbCall = m_nLockCbAdd = 0;
	m_pName = pPlugTypeName;
	m_pVer = pPlugTypeVer;
	m_bCfgLoaded = false;
}

void xbHotPlugType::AskConfigUpdate() const
{
	CheckReady();
	m_cfgChangedCb.AskConfigUpdate();
//	m_MsgCb.Post(xbQTARGET_ALLANDTHIS,0,0);
}

void xbHotPlugType::onStart()
{
	m_bCfgLoaded = false;
	m_cfgChangedCb.Init(xbCfgCommon,GetConfigPath());
	g_xbCfgEngine.AddConfigChangedCb(m_cfgChangedCb);
}

bool xbHotPlugType::onStop(bool bForce)
{
	m_cfgChangedCb.Destroy();

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

	{
		Object *ptr = m_arrObj.Begin();
		Object *pEnd = m_arrObj.End();
		while(ptr < pEnd)
		{
			eProtectTrace( onUnloadConfigForPluggedObject(ptr->m_pPlg->GetPluginId(),ptr->m_name,ptr->m_pData) );
			ptr++;
		}
	}
	m_arrObj.Clear();

	{
		xbRefPtr<Plugin> *ptr = m_arrPlg.Begin();
		xbRefPtr<Plugin> *pEnd = m_arrPlg.End();
		while(ptr < pEnd)
		{
			eProtectTrace( (*ptr)->Destroy() );
			ptr++;
		}
	}
	m_arrPlg.Clear();	
	m_arrCb.Clear();

	return bForce || m_cPlugin <= 0;
}

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

	CbAddLock	lckCbAdd(this);

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

int		xbFindPlugObj(const xbPlugObjInfo &Obj,const void *pStr)
{
	const xbString &str = *(const xbString*)pStr;
	return Obj.m_name.CompareNoCase(str);
}

int		xbHotPlugType::findPlugin(const xbRefPtr<Plugin> &PlgId1,const void *pId)
{
	const xbString &id = *(const xbString*)pId;
	return PlgId1->GetPluginId().CompareNoCase(id);
}

int		xbHotPlugType::findPlugObj(const Object &Obj,const void *pStr)
{
	const xbString &str = *(const xbString*)pStr;
	return Obj.m_name.CompareNoCase(str);
}

bool	xbHotPlugType::CreatePluggedObject(sIxbPlugPtr *pPtr,const xbString &name,void *pCtorData)
{
	xbLock lckStart(this);
	loadConfig();

	xbRefPtr<Plugin>	pPlg;
	{
		xbSectLock lck(&m_sect);
		Object *pObj = m_arrObj.SortedFind(&name,findPlugObj);
		if (!pObj) 
			return false;

		pPlg = pObj->m_pPlg;
	}

	pPlg->createPluggedObject(pPtr,name,pCtorData);

	return true;
}

bool	xbHotPlugType::CreatePluggedObject(const xbString &name,xbRefPtr<IxbObject> *pprObj,xbRefPtr<IxbHotPlugin> *pprPlugin,void *pCtorData)
{
	xbLock lckStart(this);
	loadConfig();

	xbRefPtr<Plugin>	pPlg;
	{
		xbSectLock lck(&m_sect);
		Object *pObj = m_arrObj.SortedFind(&name,findPlugObj);
		if (!pObj) 
			return false;

		pPlg = pObj->m_pPlg;
	}

	pPlg->createPluggedObject(name,pprObj,pprPlugin,pCtorData);

	return true;
}

bool	xbHotPlugType::CanWorkWith(IxbHotPlugin *pObj,const xbString &name)
{
	xbLock lckStart(this);
	loadConfig();

	xbRefPtr<Plugin>	pPlg;
	{
		xbSectLock lck(&m_sect);
		Object *pObj = m_arrObj.SortedFind(&name,findPlugObj);
		if (!pObj) return false;

		pPlg = pObj->m_pPlg;
	}

	return pPlg->canWorkWith(pObj,name);
}

xbString xbHotPlugType::GetConfigPath() const
{
/*	return xbString(m_pName) + 
#ifdef _DEBUG
		"-d"
#elif _RELDBG
		"-t"
#elif _RELALT
		"-x"
#endif
		" plugins." + m_pVer;*/
	return "hotplug/" + xbString(m_pName)+ "." + xbString(m_pVer);
}

xbCfgKey xbHotPlugType::GetConfigKey() const
{
	return xbCfgKey(xbCfgCommon,GetConfigPath());
}

void xbHotPlugType::onReloadConfig()
{
//	xbHotPlugCenter::InUpdateLock inUpdateLck(&g_xbHotPlugCenter);

	xbArray<xbPlugObjInfo> arrPlug;

#ifdef XDCBASE_EMBED
	xbHotPlugTrace(0,"xbHotPlugType::onReloadConfig(EMBEDDED mode)");
#else
	xbHotPlugTrace(0,"xbHotPlugType::onReloadConfig("+GetConfigPath()+")");

	xbCfgKey keypluglst(GetConfigKey());

	if (keypluglst.Exists())
	{
		xbCfgEnumKey	enumplug(keypluglst);
		xbCfgKey		keyplug;

		xbPlugObjInfo	plugInfo;
		plugInfo.m_pEmbeddedFact = NULL;

		while(enumplug.Next(&keyplug))
		{
			eProtectBegin();
			if (keyplug.ValueExists("enabled")
				&& !keyplug.Value["enabled"].AsBool)
			{
				xbHotPlugTrace(1,"xbHotPlugType::ReloadConfig("+GetConfigPath()+") : '"+keyplug.Name+"' ignored, enabled=false");
				continue;
			}

			xbString plugId(keyplug.Name),plugPath;
			if (keyplug.ValueExists("path"))
				plugPath = keyplug.Value["path"].AsString;
			else
				plugPath = plugId;

			if (!xbHotPlugPathOk(&plugPath)) 
			{
				xbHotPlugTrace(1,"xbHotPlugType::ReloadConfig("+GetConfigPath()+") : '"+plugId+"' key ignored, it's not a plugin key or module("+plugPath+") not found");
				continue;
			}
			plugInfo.m_plugId = plugId;
			plugInfo.m_plugPath = plugPath;

			xbCfgEnumKey	enumobj(keyplug);
			xbCfgKey		keyobj;
			while(enumobj.Next(&keyobj))
			{
				eProtectBegin();
				if (keyobj.ValueExists("enabled")
					&& !keyobj.Value["enabled"].AsBool)
				{
					xbHotPlugTrace(1,"xbHotPlugType::ReloadConfig("+GetConfigPath()+") : plugin '"+keyplug.Name+"', '"+keyobj.Name+"' ignored, enabled = false");
					continue;
				}

				xbVariant	var(keyobj.Value["priority"]);
				if (var.IsNull()) 
				{
					xbHotPlugTrace(1,"xbHotPlugType::ReloadConfig("+GetConfigPath()+") : plugin '"+keyplug.Name+"', '"+keyobj.Name+"' key ignored, it's not a plugin object key, 'priority' value missing");
					continue;
				}
				plugInfo.m_priority = var.AsLong;
				plugInfo.m_name = keyobj.Name;
				plugInfo.m_pData = NULL;				

				int plugFndPos;
				xbPlugObjInfo *pPlugFnd = arrPlug.SortedFind(&plugInfo.m_name,xbFindPlugObj,&plugFndPos);
				if (pPlugFnd)
				{
					if (pPlugFnd->m_priority > plugInfo.m_priority) 
						continue;
					
					if (pPlugFnd->m_priority == plugInfo.m_priority)
						xbThrow xbEsys("xbHotPlugType::ReloadConfig("+GetConfigPath()+") : plugin '"+keyplug.Name+"', '"+plugInfo.m_name+"' object as same priority than plugin '"+xbString(pPlugFnd->m_plugId)+"', '"+pPlugFnd->m_name+" (priority="+xbString(pPlugFnd->m_priority)+")");

					if (!onLoadConfigForPluggedObject(plugInfo.m_plugId,plugInfo.m_name,plugInfo.m_priority,keyobj,&plugInfo.m_pData))
						continue;

					try
					{
						onUnloadConfigForPluggedObject(pPlugFnd->m_plugId,pPlugFnd->m_name,pPlugFnd->m_pData);
					}
					catch(...)
					{
						onUnloadConfigForPluggedObject(plugInfo.m_plugId,plugInfo.m_name,plugInfo.m_pData);
						throw;
					}
					*pPlugFnd = plugInfo;
					continue;
				}
				
				arrPlug.Insert(plugFndPos,plugInfo);

				eProtectEndTrace();
			}
			eProtectEndTrace();
		}
	}
#endif

	if (g_embeddedFactory)
	{
		eProtectBegin();
		xbSectLock lck(&g_xbEngineCenter.m_sectData);
		xbHotPlugFactory *pFact = g_embeddedFactory;
		while(pFact)
		{
			if (pFact->GetHotPlugType()==this)
			{
				const xbHotPlugFactoryList *pFactLst = pFact->GetHotPlugFactoryList();
				xbPlugObjInfo	plugInfo;
				plugInfo.m_pEmbeddedFact = pFact;
				plugInfo.m_plugId = pFact->GetID();

				while(pFactLst->m_pName)
				{
					eProtectBegin();
					plugInfo.m_priority = pFactLst->m_priority;
					plugInfo.m_name = pFactLst->m_pName;
					plugInfo.m_pData = NULL;				

					xbCfgKey		keyobj(keypluglst,"embedded/"+plugInfo.m_plugId+"/"+xbString(plugInfo.m_name));
					if (keyobj.ValueExists("enabled")
						&& !keyobj.Value["enabled"].AsBool)
					{
						xbHotPlugTrace(1,"xbHotPlugType::ReloadConfig("+GetConfigPath()+"/embedded) : plugin '"+plugInfo.m_plugId+"', '"+plugInfo.m_name+"' ignored, enabled = false");
						continue;
					}
					if (keyobj.ValueExists("priority"))
						plugInfo.m_priority = keyobj.Value["priority"].AsLong;

					int plugFndPos;
					xbPlugObjInfo *pPlugFnd = arrPlug.SortedFind(&plugInfo.m_name,xbFindPlugObj,&plugFndPos);
					if (pPlugFnd)
					{
						if (pPlugFnd->m_priority > plugInfo.m_priority) 
						{
							pFactLst++;
							continue;
						}
						if (pPlugFnd->m_priority == plugInfo.m_priority)
							xbThrow xbEsys("xbHotPlugType::ReloadConfig("+GetConfigPath()+") : embedded plugin '"+plugInfo.m_plugId+"', '"+plugInfo.m_name+"' object as same priority than plugin '"+xbString(pPlugFnd->m_plugId)+"', '"+pPlugFnd->m_name+" (priority="+xbString(pPlugFnd->m_priority)+")");

						if (!onLoadConfigForPluggedObject(plugInfo.m_plugId,plugInfo.m_name,plugInfo.m_priority,keyobj,&plugInfo.m_pData))
							continue;

						try
						{
							onUnloadConfigForPluggedObject(pPlugFnd->m_plugId,pPlugFnd->m_name,pPlugFnd->m_pData);
						}
						catch(...)
						{
							onUnloadConfigForPluggedObject(plugInfo.m_plugId,plugInfo.m_name,plugInfo.m_pData);
							throw;
						}
						
						*pPlugFnd = plugInfo;
						continue;
					}
					
					arrPlug.Insert(plugFndPos,plugInfo);

					eProtectEndTrace();
					pFactLst++;
				}
			}
			pFact = pFact->GetEmbeddedNext();
		}
		eProtectEnd();
	}

	xbArray< xbRefPtr<Plugin> >	arrPlgToDestroy;
	xbArray< xbRefPtr<Plugin> >	arrPlgToCheck;
	{
		xbSectLock lck(&m_sect);
		{
			xbRefPtr<Plugin> *ptr = m_arrPlg.Begin();
			xbRefPtr<Plugin> *pEnd = m_arrPlg.End();
			while(ptr < pEnd)
			{
				(*ptr)->resetUsed();
				ptr++;
			}
		}
		{
			Object *ptr = m_arrObj.Begin();
			Object *pEnd = m_arrObj.End();
			while(ptr < pEnd)
			{
				eProtectTrace( onUnloadConfigForPluggedObject(ptr->m_pPlg->GetPluginId(),ptr->m_name,ptr->m_pData) );
				ptr++;
			}
			m_arrObj.Clear();
		}
		{
			xbPlugObjInfo *ptr = arrPlug.Begin();
			xbPlugObjInfo *pEnd = arrPlug.End();
			while(ptr < pEnd)
			{
				eProtectBegin();
				int plugFndPos;
				xbRefPtr<Plugin> pPlug;
				xbRefPtr<Plugin> *ppPlugFnd = m_arrPlg.SortedFind(&ptr->m_plugId,findPlugin,&plugFndPos);
				if (!ppPlugFnd || !(*ppPlugFnd)->setUsed(ptr))
				{
					if (ppPlugFnd)
					{
						arrPlgToDestroy.Add(*ppPlugFnd);
						m_arrPlg.Delete(plugFndPos);
						ppPlugFnd = NULL;
					}
					xbString		label;
#ifdef XDCBASE_EMBED
					label = ptr->m_pEmbeddedFact->GetLabel();
#else
					if (ptr->m_pEmbeddedFact)
					{
						label = ptr->m_pEmbeddedFact->GetLabel();
					}
					else
					{
						xbCfgKey		keyplug(keypluglst,xbString(ptr->m_plugId));
						if (keyplug.DefaultValueExists()) 
							label = keyplug.DefaultValue.AsString;
					}
#endif

					Plugin *plg = xbDnew Plugin(this,ptr->m_plugId,label,ptr->m_plugPath);
					try
					{
						plg->Create();


						m_arrPlg.Insert(plugFndPos,plg);

						pPlug = plg;
						ppPlugFnd = &pPlug;

						plg->Release();
					}
					catch(...)
					{
						plg->Release();
						throw;
					}
					if (ptr->m_pEmbeddedFact)
					{
						lck.Unlock();
						plg->setEmbeddedFact(ptr->m_pEmbeddedFact);
						lck.Lock(&m_sect);
					}
				}

				m_arrObj.Add(Object(ptr->m_name,ptr->m_priority,*ppPlugFnd,ptr->m_pData));
				
				eProtectEndTrace();
				ptr++;
			}
		}
		{
			for (int i = 0; i < m_arrPlg.Length; i++)
			{
				if (m_arrPlg.unsafeGetAt(i)->isUsed()) 
				{
					if (m_arrPlg.unsafeGetAt(i)->isToCheck())
						arrPlgToCheck.Add(m_arrPlg.unsafeGetAt(i));
					continue;
				}
				arrPlgToDestroy.Add(m_arrPlg.unsafeGetAt(i));
				m_arrPlg.Delete(i);
			}
		}
	}
	{
		xbRefPtr<Plugin> *ptr = arrPlgToCheck.Begin();
		xbRefPtr<Plugin> *pEnd = arrPlgToCheck.End();
		while(ptr < pEnd)
		{
			eProtectTrace( (*ptr)->check() );
			ptr++;
		}
	}
	{
		xbRefPtr<Plugin> *ptr = arrPlgToDestroy.Begin();
		xbRefPtr<Plugin> *pEnd = arrPlgToDestroy.End();
		while(ptr < pEnd)
		{
			eProtectTrace( (*ptr)->Destroy() );
			ptr++;
		}
	}

	{
		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(this));
		}

		g_xbCfgEngine.onPlugTypeReloadConfig(this);
	}

//	g_xbHotPlugCenter.fireOnLoadPluginAsked(NULL,false);
}

void xbHotPlugType::receiveConfigChanged(xbCfgChangedCb &cbObj,xbCfgRoot root,xbCfgKeyChangesInfo *pInfo,const xbString &cfgKeyPath)
{
	xbLock lckStart;
	if (!IsNowStarted(&lckStart)) return;
	
	{
		xbSectLock lck(&m_sect);
		m_bCfgLoaded = true;
	}
	
	onReloadConfig();
}

void	xbHotPlugType::loadConfig()
{
	{
		xbSectLock lck(&m_sect);
		if (m_bCfgLoaded) return;
		m_bCfgLoaded = true;
	}
	
	onReloadConfig();
}

void	xbHotPlugType::ReloadConfig(bool bNow)
{
	xbLock lckStart;
	if (!IsNowStarted(&lckStart)) 
		return;

	{
		xbSectLock lck(&m_sect);
		if (!bNow)
		{
			m_bCfgLoaded = false;
			return;
		}
		m_bCfgLoaded = true;
	}
	
	onReloadConfig();
}

void xbHotPlugType::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 xbHotPlugType::lockCbCall() const
{
//	if (!isNowStarted()) 
//		xbThrow xbEsys("xbHotPlugType::lockCbCall() : destroyed plug");
	xbSectLock lck(&m_sect);
	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_sect);
			if (m_nLockCbAdd <= 0) 
				break;
			lck.Unlock();
		}
		SleepEx(xbHOTPLUGCB_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
//		if (!isNowStarted()) 
//			xbThrow xbEsys("xbHotPlugType::lockCbCall() : destroyed plug");
		lck.Lock(&m_sect);
	}
	m_nLockCbCall++;
}

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

void xbHotPlugType::lockCbAdd() const
{
//	if (!IsNowStarted()) xbThrow xbEsys("xbHotPlugType::lockCbAdd() : destroyed plug");
	xbSectLock lck(&m_sect);
	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_sect);
			if (m_nLockCbCall <= 0) 
				break;
			lck.Unlock();
		}
		SleepEx(xbHOTPLUGCB_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
//		if (!IsNowStarted()) xbThrow xbEsys("xbHotPlugType::lockCbAdd() : destroyed plug");
		lck.Lock(&m_sect);
	}
	m_nLockCbAdd++;
}

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

//////////////////////////////////////////////////////////////////////////////////////////
// xbHotPlugType::Plugin
//////////////////////////////////////////////////////////////////////////////////////////
xbHotPlugType::Plugin::Plugin(xbHotPlugType *pPlugType,const xbString &id,const xbString &label,const xbString &plugPath)
{
	{
		xbSectLock lck(&pPlugType->m_sect);
		pPlugType->m_cPlugin++;
	}
	m_pEmbeddedFact = NULL;
	m_bIsEmbeddedFact = false;
	m_pPlugType = pPlugType;
	m_plugId = id;
	m_plugPath = plugPath;
	m_label = label;
	m_bUsed = true;
	m_hPlugin = NULL;
	m_bInPluginLoad = m_bInPluginUnload = m_bPluginUnloadAsked = false;
	m_cInPluginCreate = 0;
	m_pPluginCreateObject = NULL;
	m_pPluginUnload = NULL;
	m_hrPlugin = S_OK;
	m_nLockUpdateCall = 0;
	m_hLockUpdateThread = 0;
}

void xbHotPlugType::Plugin::setEmbeddedFact(xbHotPlugFactory *pEmbeddedFact)
{
	m_bIsEmbeddedFact = true;
	pEmbeddedFact->Load(this);
	m_pEmbeddedFact = pEmbeddedFact;
}

xbHotPlugType::Plugin::~Plugin()
{
	eProtect(unloadPlugin(true));
	eProtectBegin();
	if (m_pPlugType->IsReady())
	{
		xbSectLock lck(&m_pPlugType->m_sect);
		m_pPlugType->m_cPlugin--;
		xbAssert(m_pPlugType->m_cPlugin >= 0);
	}
	eProtectEndTrace();
	xbAssert(m_nLockUpdateCall == 0 && m_hLockUpdateThread == NULL);
}

void xbHotPlugType::Plugin::lockUpdateCall() const
{
//	if (m_bDestruction) xbThrow xbEsys("xbHotPlugType::Plugin::lockUpdateCall() : destroyed plug");
	xbSectLock lck(m_pSectPtr);
	xbAssert(m_nLockUpdateCall > 0 || m_hLockUpdateThread == NULL);
	int cnt = 0;
	HANDLE hThread = ::GetCurrentThread();
	if (hThread != m_hLockUpdateThread)
	{
		while(m_hLockUpdateThread != NULL)
		{
			if (++cnt > xbHOTPLUGCB_TRYCNT) 
				xbThrow xbEsys(xbHR_TIMEOUT,"xbHotPlugType::Plugin::lockUpdateCall() : timeout, can't update or load plugin !");
			lck.Unlock();
			if (xbDoEvents(NULL)) 
			{
//				if (m_bDestruction) xbThrow xbEsys("xbHotPlugType::Plugin::lockUpdateCall() : destroyed plug");
				lck.Lock(m_pSectPtr);
				if (m_hLockUpdateThread == NULL) break;
				lck.Unlock();
			}
			SleepEx(xbHOTPLUGCB_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
//			if (m_bDestruction) xbThrow xbEsys("xbHotPlugType::Plugin::lockUpdateCall() : destroyed plug");
			lck.Lock(m_pSectPtr);
		}
	}
	m_nLockUpdateCall++;
	m_hLockUpdateThread = hThread;
}

void xbHotPlugType::Plugin::unlockUpdateCall() const
{
	xbSectLock lck(m_pSectPtr);
	m_nLockUpdateCall--;
	xbAssert(m_nLockUpdateCall >= 0);
	if (m_nLockUpdateCall <= 0) 
		m_hLockUpdateThread = NULL;
}

bool xbHotPlugType::Plugin::isToCheck()
{
	xbSectLock lck(m_pSectPtr);
	if (m_bIsEmbeddedFact)
		return false;
	return /*!m_pluginPath.IsEmpty() ||*/ FAILED(m_hrPlugin);
}

void xbHotPlugType::Plugin::check()
{
	if (m_bDestruction || m_bIsEmbeddedFact) 
		return;
	{
		xbSectLock lck(m_pSectPtr);
		if (FAILED(m_hrPlugin)) 
			m_hrPlugin = S_OK;
	}
	loadPlugin();
//	g_xbHotPlugCenter.CheckPluginPath(m_id);
}

void xbHotPlugType::Plugin::createPluggedObject(sIxbPlugPtr *pPtr,const xbString &name,void *pCtorData)
{
	if (m_bDestruction) 
		xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::createPluggedObject()");
	xbRefPtr<IxbObject>	prObj;
	loadPlugin();
	createObject(name,&prObj,pCtorData);
	pPtr->onCreatePluggedObject(this,prObj);
}

void xbHotPlugType::Plugin::createPluggedObject(const xbString &name,xbRefPtr<IxbObject> *pprObj,xbRefPtr<IxbHotPlugin> *pprPlugin,void *pCtorData)
{
	if (m_bDestruction) 
		xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::createPluggedObject()");
	loadPlugin();
	if (pprObj)
		createObject(name,pprObj,pCtorData);
	if (pprPlugin)
		*pprPlugin = this;
}

bool xbHotPlugType::Plugin::canWorkWith(IxbHotPlugin *pObj,const xbString &name)
{
	if (m_bDestruction) 
		xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction... ","xbHotPlugType::Plugin::canWorkWith()");
	loadPlugin();
	{
		xbSectLock lck(m_pSectPtr);
		if (m_bDestruction) 
			xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::canWorkWith(2)");
		if (FAILED(m_hrPlugin)) 
			xbThrow xbEsys(m_hrPlugin,"Can't load plugin '"+m_label+"', type '"+m_pPlugType->Label+"' !");
		if (m_bInPluginLoad) 
		{
			if (!pObj) return false;
			xbThrow xbEhotPlug("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' is loading...","xbHotPlugType::Plugin::canWorkWith()");
		}
		if (m_bPluginUnloadAsked) 
		{
			if (!pObj) return false;
			xbThrow xbEhotPlug("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' is unloading...","xbHotPlugType::Plugin::canWorkWith()");
		}
//		if (m_hPlugin == NULL || m_pPluginCreateObject == NULL) xbThrow xbEsys("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' isn't loaded (Abnormal error) !");
	}
	if (!pObj) return true;

	return (pObj->GetPluginId() == m_plugId)?true:false;
}

bool xbHotPlugType::Plugin::setUsed(xbPlugObjInfo *pInfo) 
{
	m_bUsed = true;
	xbSectLock lck(m_pSectPtr);
	return m_pEmbeddedFact == pInfo->m_pEmbeddedFact 
		&& (!m_bIsEmbeddedFact || pInfo->m_pEmbeddedFact)
		&& m_plugPath.CompareNoCase(pInfo->m_plugPath) == 0;
}

void xbHotPlugType::Plugin::loadPlugin()
{
	if (m_pEmbeddedFact)
		return;
	UpdateCallLock lckUpdateCall(this);
	if (m_bIsEmbeddedFact)
		xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::loadPlugin(Embedded)");

	{
		xbSectLock lck(m_pSectPtr);
		if (m_bDestruction) 
			xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::loadPlugin()");
		if (m_hPlugin != NULL) 
			return;
		if (FAILED(m_hrPlugin)) 
			xbThrow xbEsys(m_hrPlugin,"Can't load plugin '"+m_label+"', type '"+m_pPlugType->Label+"' !");
		if (m_bInPluginLoad) 
			xbThrow xbEhotPlug("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' is loading...","xbHotPlugType::Plugin::loadPlugin()");
		if (m_bPluginUnloadAsked) 
			xbThrow xbEhotPlug("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' is unloading...","xbHotPlugType::Plugin::loadPlugin()");
		m_bInPluginLoad = true;
		xbAssert(m_cInPluginCreate == 0);
		m_cInPluginCreate = 0;
	}

	//xbHotPlugCenter::InUpdateLock inUpdateLck(&g_xbHotPlugCenter);

	xbPlugRet	ret = xbPlugOk;
	try
	{
		ret = onLoadPluginAsked();
	}
	catch(xbEbase *pE)
	{
		{
			xbSectLock lck(m_pSectPtr);
			m_hrPlugin = xbGetHR(pE);
			m_bInPluginLoad = false;
		}
		onError(pE);
		xbTraceException(pE);
	}
	catch(...)
	{
		{
			xbSectLock lck(m_pSectPtr);
			m_hrPlugin = E_POINTER;
			m_bInPluginLoad = false;
		}
		onError(NULL);
		xbTraceException(NULL);
	}
	if (ret == xbPlugUpdating)
	{
/*		xbSectLock lck(m_pSectPtr);
		m_hrPlugin = S_OK;
		m_bInPluginLoad = false;*/
		xbThrow xbEhotPlug("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' is loading...","xbHotPlugType::Plugin::loadPlugin() : g_xbHotPlugCenter.LoadPlugin() returning xbPlugUpdating");
	}
}

void xbHotPlugType::Plugin::unloadPlugin(bool bForce)
{
//	xbHotPlugCenter::InUpdateLock inUpdateLck(&g_xbHotPlugCenter);
	if (m_pPlugType->isNowStarted())
	{
		CbCallLock	lckCbCall(m_pPlugType);
		for (int no = 0; no < m_pPlugType->m_arrCb.Length; no++)
		{
			xbRefPtr<IxbHotPlugTypeCb> ptr;
			if (!m_pPlugType->m_arrCb.unsafeGetAt(no)->GetPtr(&ptr)) 
				continue;

			eProtectTrace(ptr->OnUnloadPluginAsked(this,bForce));
		}
		g_xbCfgEngine.onPlugTypeUnloadPluginAsked(this,bForce);
	}

	UpdateCallLock lckUpdateCall(this);

	xbSectLock lck(m_pSectPtr);
	if (m_bIsEmbeddedFact)
	{
		if (!m_pEmbeddedFact) 
			return;

		m_bInPluginUnload = true;
		m_bPluginUnloadAsked = true;

		try
		{
			int			cnt = 0;
			for(;;)
			{
				if (!m_bInPluginLoad && m_cInPluginCreate <= 0)
					break;

				lck.Unlock();
				if (++cnt > xbHOTPLUGINUNLOAD_TRYCNT) 
					xbThrow xbEsys("xbHotPlugType("+xbString(m_pPlugType->Label)+")::Plugin("+m_label+")::unloadPlugin() : timeout");

				unloadPluggedObj();
				
				bool bDone = false;
				if (xbDoEvents(NULL)) 
				{
					lck.Lock(m_pSectPtr);
					if (!m_pEmbeddedFact) 
						return;
					if (!m_bInPluginLoad && m_cInPluginCreate <= 0)
					{
						bDone = true;
						break;
					}
					lck.Unlock();
				}
				
				if (bDone) 
					break;
				::SleepEx(xbHOTPLUGINUNLOAD_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
				lck.Lock(m_pSectPtr);
			}
			lck.Unlock();

			cnt = 0;
			for(;;)
			{
				if (++cnt > xbHOTPLUGINUNLOAD_TRYCNT) 
					xbThrow xbEsys("xbHotPlugType("+xbString(m_pPlugType->Label)+")::Plugin("+m_label+")::unloadPlugin() : timeout (2)");

				unloadPluggedObj();

				bool bDone = m_pEmbeddedFact->Unload(this,bForce);
				if (bDone) 
					break;
				
				if (xbDoEvents(NULL)) 
				{
					bDone = m_pEmbeddedFact->Unload(this,bForce);
					if (bDone) 
						break;
				}
				
				if (bDone) 
					break;
				::SleepEx(xbHOTPLUGINUNLOAD_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
			}
		}
		catch(...)
		{
			lck.Lock(m_pSectPtr);
			m_bInPluginUnload = false;
			if (bForce)
			{
				m_bPluginUnloadAsked = false;
				m_hrPlugin = E_FAIL;
				m_pEmbeddedFact = NULL;
			}
			throw;
		}

		lck.Lock(m_pSectPtr);
		m_pEmbeddedFact = NULL;
		m_hrPlugin = S_OK;
		m_bInPluginUnload = false;
		m_bPluginUnloadAsked = false;
		lck.Unlock();
		xbHotPlugTrace(1,"xbHotPlugType("+m_pPlugType->Label+")::Plugin("+m_label+")::unloadPlugin() : plugin unloaded");
		return;
	}
	if (!m_hPlugin || m_bInPluginUnload) 
		return;
	m_bInPluginUnload = true;
	m_bPluginUnloadAsked = true;
#ifdef XDCBASE_EMBED
	xbThrow xbEsys("Plugin "+m_label+" can't be linked dynamically, unloadPlugin forbidden");
#else
	try
	{
		int			cnt = 0;
		for(;;)
		{
			if (!m_bInPluginLoad && m_cInPluginCreate <= 0)
				break;

			lck.Unlock();
			if (++cnt > xbHOTPLUGINUNLOAD_TRYCNT) 
				xbThrow xbEsys("xbHotPlugType("+xbString(m_pPlugType->Label)+")::Plugin("+m_label+")::unloadPlugin() : timeout");

			unloadPluggedObj();
			
			bool bDone = false;
			if (xbDoEvents(NULL)) 
			{
				lck.Lock(m_pSectPtr);
				if (!m_hPlugin) 
					return;
				if (!m_bInPluginLoad && m_cInPluginCreate <= 0)
				{
					bDone = true;
					break;
				}
				lck.Unlock();
			}
			
			if (bDone) 
				break;
			::SleepEx(xbHOTPLUGINUNLOAD_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
			lck.Lock(m_pSectPtr);
		}
		lck.Unlock();

		cnt = 0;
		for(;;)
		{
			if (++cnt > xbHOTPLUGINUNLOAD_TRYCNT) 
				xbThrow xbEsys("xbHotPlugType("+xbString(m_pPlugType->Label)+")::Plugin("+m_label+")::unloadPlugin() : timeout (2)");

			unloadPluggedObj();

			bool bDone = (*m_pPluginUnload)(this,bForce);
			if (bDone) 
				break;
			
			if (xbDoEvents(NULL)) 
			{
				bDone = (*m_pPluginUnload)(this,bForce);
				if (bDone) 
					break;
			}
			
			if (bDone) 
				break;
			::SleepEx(xbHOTPLUGINUNLOAD_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
		}
	}
	catch(...)
	{
		lck.Lock(m_pSectPtr);
		m_bInPluginUnload = false;
		if (bForce)
		{
			m_bPluginUnloadAsked = false;
//			HINSTANCE	hInst = m_hPlugin;
			m_hPlugin = NULL;
			m_pPluginUnload = NULL;
			m_pPluginCreateObject = NULL;
			m_hrPlugin = E_FAIL;
//			lck.Unlock();
//			xbDbgOutput("xbHotPlugType("+m_pPlugType->Label+")::Plugin("+m_label+")::unloadPlugin() : on error : force plugin unload");
//			::AfxFreeLibrary(hInst);
		}
		throw;
	}

	lck.Lock(m_pSectPtr);
	HINSTANCE	hInst = m_hPlugin;
	m_hPlugin = NULL;
	m_pPluginUnload = NULL;
	m_pPluginCreateObject = NULL;
	m_hrPlugin = S_OK;
	m_bInPluginUnload = false;
	m_bPluginUnloadAsked = false;
	lck.Unlock();
	xbHotPlugTrace(1,"xbHotPlugType("+m_pPlugType->Label+")::Plugin("+m_label+")::unloadPlugin() : plugin unloaded");
	xbFreeLibrary(hInst);
#endif
}

void xbHotPlugType::Plugin::createObject(const xbString &name,xbRefPtr<IxbObject> *ppObj,void *pCtorData)
{
	int cLoadTry = 0;
	for(;;)
	{
		xbSectLock lck(m_pSectPtr);
		if (m_bDestruction) 
			xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::createObject()");
		if (FAILED(m_hrPlugin)) 
			xbThrow xbEsys(m_hrPlugin,"Can't load plugin '"+m_label+"', type '"+m_pPlugType->Label+"' !");
		if (m_bInPluginLoad) 
			xbThrow xbEhotPlug("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' is loading...","xbHotPlugType::Plugin::createObject()");
		if (m_bPluginUnloadAsked) 
			xbThrow xbEhotPlug("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' is unloading...","xbHotPlugType::Plugin::createObject()");
		if (!m_pEmbeddedFact && (m_hPlugin == NULL || m_pPluginCreateObject == NULL))
		{
			if (m_cInPluginCreate <= 0 && cLoadTry++ < 4)
			{
				lck.Unlock();
				loadPlugin();
				continue;
			}
			xbThrow xbEsys("Plugin '"+m_label+"', type '"+m_pPlugType->Label+"' isn't loaded (Abnormal error, xbHotPlugType::Plugin::createObject) !");
		}
		m_cInPluginCreate++;
		break;
	}

	try
	{
		if (m_pEmbeddedFact)
			m_pEmbeddedFact->CreateObject(this,name,ppObj,pCtorData);
		else
			(*m_pPluginCreateObject)(this,name,ppObj,pCtorData);
	}
	catch(...)
	{
		xbSectLock lck(m_pSectPtr);
		m_cInPluginCreate--;
		throw;
	}
	xbSectLock lck(m_pSectPtr);
	m_cInPluginCreate--;
	xbAssert(m_cInPluginCreate >= 0);
}

xbPlugRet xbHotPlugType::Plugin::onLoadPluginAsked()
{
	if (m_pEmbeddedFact)
		return xbPlugOk;
#ifdef XDCBASE_EMBED
	xbThrow xbEsys("Plugin "+m_label+" can't be linked dynamically, OnLoadPluginAsked forbidden");
#else
//	xbHotPlugCenter::InUpdateLock inUpdateLck(&g_xbHotPlugCenter);

	xbString modulepath;
	{
		xbSectLock lck(m_pSectPtr);
		if (m_hPlugin != NULL) 
		{
			xbRdbgAssert(!m_bInPluginLoad);
			xbHotPlugTrace(1,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : not in load(inload"+xbString(m_bInPluginLoad)+") -> exit");
			m_bInPluginLoad = false;
			return xbPlugUpdating;
		}
		if (m_bPluginUnloadAsked || m_bDestruction)
		{
			xbHotPlugTrace(1,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : unload asked, or in destruction -> cancel");
			m_bInPluginLoad = false;
			return xbPlugUpdating;
		}
/*		if (FAILED(m_hrPlugin))
		{
			xbHotPlugTrace(1,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : errors ("+xbString(m_hrPlugin)+":"+xbStrFromHR(m_hrPlugin)+") -> cancel");
			m_bInPluginLoad = false;
			return;
		}*/
		m_bInPluginLoad = true;
		xbAssert(m_cInPluginCreate == 0);
		m_cInPluginCreate = 0;
		modulepath = m_plugPath;
		modulepath.OwnData();
	}

	try
	{
/*		if (modulepath.IsEmpty())
		{
			xbHotPlugTrace(0,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : modulepath is empty");
			xbSectLock lck(m_pSectPtr);
			m_bInPluginLoad = false;
			return;
		}
*/
		xbHotPlugTrace(1,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : loading plugin file '"+modulepath+"'...");

		HINSTANCE hInst = ::xbLoadLibraryAsPlug(modulepath);
		if (!hInst) 
			xbThrow xbEsys(xbHR_LAST,"Can't load plugin file '"+modulepath+"' (id="+xbString(m_plugId)+") !");

		try
		{
			xbPluginLoad			pLoadFct;
			xbPluginCreateObject	pCreateFct;
			xbPluginUnload			pUnloadFct;

			pCreateFct = (xbPluginCreateObject)::GetProcAddress(hInst, (LPCSTR)
				xbString("xdc")+xbString(m_pPlugType->m_pName)+"PluginCreateObject");
			if (!pCreateFct)
				 xbThrow xbEsys(xbHR_LAST,"File '"+modulepath+"' is not a valid plugin for "+m_pPlugType->Label+" !");
			pUnloadFct = (xbPluginUnload)::GetProcAddress(hInst,(LPCSTR)
				xbString("xdc")+xbString(m_pPlugType->m_pName)+"PluginUnload");
			if (!pUnloadFct)
				 xbThrow xbEsys(xbHR_LAST,"File '"+modulepath+"' is not a valid plugin for "+m_pPlugType->Label+" ! (2)");
			pLoadFct = (xbPluginLoad)::GetProcAddress(hInst,(LPCSTR)
				xbString("xdc")+xbString(m_pPlugType->m_pName)+"PluginLoad");
			if (!pLoadFct)
				 xbThrow xbEsys(xbHR_LAST,"File '"+modulepath+"' is not a valid plugin for "+m_pPlugType->Label+" ! (3)");

			xbHotPlugTrace(1,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : initializing plugin file '"+modulepath+"'");

			(*pLoadFct)(this);
			
			xbHotPlugTrace(1,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : plugin file '"+modulepath+"' loaded");
			
			xbSectLock lck(m_pSectPtr);
			m_hPlugin = hInst;
			m_bInPluginLoad = false;
			m_pPluginCreateObject = pCreateFct;
			m_pPluginUnload = pUnloadFct;
			m_hrPlugin = S_OK;
		}
		catch(...)
		{
			xbFreeLibrary(hInst);
			throw;
		}
	}
	catch(xbEbase *pE)
	{
		{
			xbSectLock lck(m_pSectPtr);
			m_hrPlugin = xbGetHR(pE);
			m_bInPluginLoad = false;
		}
		throw;
	}
	catch(...)
	{
		{
			xbSectLock lck(m_pSectPtr);
			m_hrPlugin = E_POINTER;
			m_bInPluginLoad = false;
		}
		throw;
	}
	
	xbHotPlugTrace(1,"xbHotPlugType::Plugin("+xbString(m_plugId)+")::OnLoadPluginAsked() : loaded ok ("
		+xbString(m_hrPlugin)+":"+xbStrFromHR(m_hrPlugin)+")");

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

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

		eProtectTrace( g_xbCfgEngine.onPlugTypeLoadPluginAsked(this) );
	}
#endif
	return xbPlugOk;
}

void xbHotPlugType::Plugin::onProgress( double progress, xbPlugStatus status, const xbString &statusText)
{
	if (m_bDestruction) 
		return;

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

		eProtectTrace( ptr->OnProgress(this,progress,status,statusText) );
	}
	eProtectTrace( g_xbCfgEngine.onPlugTypeProgress(this,progress,status,statusText) );
}

xbPlugUpdateCaps xbHotPlugType::Plugin::onError(xbEbase *pE)
{
	if (pE == NULL)
		return onError(false,E_POINTER,"Unknown type exception !");
	return onError(false,pE->GetHR(),xbGetMsg("text",pE));
}

xbPlugUpdateCaps xbHotPlugType::Plugin::onError(bool bCanRetry, HRESULT hr, const xbString &errorText)
{
	if (m_bDestruction) 
		return xbPlugUpdateNormal;

	CbCallLock	lckCbCall(m_pPlugType);
	int ret = int(m_pPlugType->onErrorGetPluginCaps());

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

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

		if (subret > ret) 
			ret = subret;
	}

	int subret = -1;
	eProtectTrace( subret = g_xbCfgEngine.onPlugTypeError(this,bCanRetry,hr,errorText) );

	if (subret > ret) 
		ret = subret;

	return xbPlugUpdateCaps(ret);
}

xbPlugUpdateCaps	xbHotPlugType::Plugin::getPluginCaps()
{
	if (m_bDestruction) return xbPlugUpdateNormal;

	CbCallLock	lckCbCall(m_pPlugType);
	int ret = int(m_pPlugType->onGetPluginCaps());

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

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

		if (subret > ret) 
			ret = subret;
	}

	int subret = -1;
	eProtectTrace( subret = g_xbCfgEngine.onPlugTypeGetPluginCaps(this) );

	if (subret > ret) 
		ret = subret;
	return xbPlugUpdateCaps(ret);
}

void xbHotPlugType::Plugin::onBeforeDestruction(bool /*bInDestroy*/) 
{
	eProtect(unloadPlugin(false));
}

void xbHotPlugType::Plugin::onDestruction(bool /*bInDestroy*/) 
{
	eProtect(unloadPlugin(true));
}

void xbHotPlugType::Plugin::unloadPluggedObj()
{
	xbArray< xbRefPtr<IxbHotPluggedObject> > arrObj;
	{
		xbSectLock	lck(m_pSectPtr);
		sIxbPtr		*ptr = m_pFirstPtr;
		while(ptr)
		{
			sIxbPtr		*pcur = ptr;
			ptr = ptr->_getNext();

			sIxbPlugPtr *pPlugPtr;
			if (!xbCastIn(pcur,&pPlugPtr)) continue;

			eProtectBegin();

			xbRefPtr<IxbHotPluggedObject> pPlugObj;
			pPlugPtr->onUnloadPluggedObject(this,&pPlugObj);

			if (pPlugObj.IsValid()) arrObj.Add(pPlugObj);

			eProtectEndTrace();
		}
	}
	xbRefPtr<IxbHotPluggedObject> *ptr = arrObj.Begin();
	xbRefPtr<IxbHotPluggedObject> *pEnd = arrObj.End();
	while(ptr < pEnd)
	{
		eProtectTrace( (*ptr)->onDestroy(this) );
		ptr++;
	}
}

xbString	xbHotPlugType::Plugin::GetConfigPath() const
{
	if (m_bDestruction) 
		xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::GetConfigPath()");
	return m_pPlugType->GetConfigPath();
}

xbCfgKey	xbHotPlugType::Plugin::GetConfigKey() const
{
	if (m_bDestruction) 
		xbThrow xbEhotPlug(E_ABORT,"Plugin '"+m_label+"' is in destruction...","xbHotPlugType::Plugin::GetConfigKey()");
	return m_pPlugType->GetConfigKey();
}


//////////////////////////////////////////////////////////////////////////////////////////
// xbHotPlugFactory
//////////////////////////////////////////////////////////////////////////////////////////
xbHotPlugFactory::xbHotPlugFactory(
#ifndef XDCBASE_EMBED
		xbXdcLib *pLib,
#endif
	const xbHotPlugType *pPlugType,const char *pId,const char *pLabel,
	const xbHotPlugFactoryList *pFactList,ulong unloadDelayInMs,xbHotPlugLinkType linkType)
	:m_poPluginContainer(&m_sect)
{
	m_pPlugType = pPlugType;
	m_pId = pId;
	m_pLabel = pLabel;
	m_pFactList = pFactList;
	m_bUnloadAsked = false;
	m_ObjCnt = 0;
	m_lastTimeFreeCnt = ::GetTickCount();
	m_pDbgPluginContainer = NULL;
	m_unloadDelayInMs = unloadDelayInMs?unloadDelayInMs:300;
	m_linkType = linkType;
#ifdef XDCBASE_EMBED
	if (m_linkType != xbHotPlugLinkEmbedded)
		xbRelOutput("Plugin "+xbString(pLabel)+" can't be linked dynamically in embedded mode !");
#else
	m_pLib = pLib;
#endif
	m_pEmbeddedFactoryNext = NULL;
	if (m_linkType == xbHotPlugLinkEmbedded)
		eProtect(_embeddedRegister());
}

xbHotPlugFactory::~xbHotPlugFactory()
{
	if (m_linkType == xbHotPlugLinkEmbedded)
		eProtect(_embeddedUnregister());
	xbAssert(m_ObjCnt == 0 && m_pDbgPluginContainer == NULL);
}

void xbHotPlugFactory::_embeddedRegister()
{
	if (m_linkType != xbHotPlugLinkEmbedded)
		xbThrow xbEsys("Plugin "+xbString(m_pLabel)+" can't be linked statically, _embeddedRegister forbidden");

/*	{
		xbLock lckStart;
		if (g_xbHotPlugCenter.IsNowStarted(&lckStart))
			xbThrow xbEsys("Trying to unregister embedded plugin "+xbString(m_pLabel)+" when hotplug center is started");
	}*/
	
	xbSectLock lck(&g_xbEngineCenter.m_sectData);

	xbHotPlugFactory *pFact = g_embeddedFactory;
	while(pFact)
	{
		if (pFact == this)
			xbThrow xbEsys("Plugin "+xbString(m_pLabel)+" already registered as embedded");
		pFact = pFact->m_pEmbeddedFactoryNext;
	}

	m_pEmbeddedFactoryNext = g_embeddedFactory;
	g_embeddedFactory = this;
	lck.Unlock();
	((xbHotPlugType*)m_pPlugType)->ReloadConfig(false);
}
void xbHotPlugFactory::_embeddedUnregister()
{
	if (m_linkType != xbHotPlugLinkEmbedded)
		xbThrow xbEsys("Plugin "+xbString(m_pLabel)+" can't be linked statically, _embeddedUnregister forbidden");

/*	{
		xbLock lckStart;
		if (g_xbHotPlugCenter.IsNowStarted(&lckStart))
			xbThrow xbEsys("Trying to unregister embedded plugin "+xbString(m_pLabel)+" when hotplug center is started");
	}
	*/

	xbSectLock lck(&g_xbEngineCenter.m_sectData);
	xbHotPlugFactory *pFact = g_embeddedFactory, *pFactPrev = NULL;
	while(pFact)
	{
		if (pFact == this)
		{
			if (!pFactPrev)
			{
				g_embeddedFactory = m_pEmbeddedFactoryNext;
				m_pEmbeddedFactoryNext = NULL;
				lck.Unlock();
				((xbHotPlugType*)m_pPlugType)->ReloadConfig(true);
				return;
			}
			pFactPrev->m_pEmbeddedFactoryNext = m_pEmbeddedFactoryNext;
			m_pEmbeddedFactoryNext = NULL;
			lck.Unlock();
			((xbHotPlugType*)m_pPlugType)->ReloadConfig(true);
			return;
		}
		pFactPrev = pFact;
		pFact = pFact->m_pEmbeddedFactoryNext;
	}
	xbThrow xbEsys("Plugin "+xbString(m_pLabel)+" not registered as embedded, _embeddedUnregister failed");
}

void xbHotPlugFactory::Register()
{
#ifdef XDCBASE_EMBED
	xbThrow xbEsys("Plugin "+xbString(m_pLabel)+" can't be linked dynamically, Register forbidden");
#else
	xbUseEngineCenterExe	use(&g_xbEngineCenter,true);

	if (m_linkType != xbHotPlugLinkDynamic)
		xbThrow xbEsys("Plugin "+xbString(m_pLabel)+" can't be linked dynamically, Register forbidden");

	{
		xbCfgKey cfgkey(m_pPlugType->GetConfigKey());
		
		xbCfgKey plugkey(cfgkey,xbString(m_pId));

		if (m_pLabel) 
			plugkey.DefaultValue = m_pLabel;

		plugkey.Value["enabled"] = true;
		plugkey.Value["path"] = xbHotPlugPathCheck(m_pLib->GetFilePath());
		
		const xbHotPlugFactoryList *pFact = m_pFactList;

		while(pFact->m_pName)
		{
			xbCfgKey objkey(plugkey,pFact->m_pName);
			
			objkey.Value["priority"] = pFact->m_priority;
			objkey.Value["enabled"] = true;

			pFact++;
		}
	}

	m_pPlugType->AskConfigUpdate();
#endif
}

void xbHotPlugFactory::Unregister()
{
	xbUseEngineCenterExe	use(&g_xbEngineCenter,false);

	{
		xbCfgKey cfgkey(m_pPlugType->GetConfigKey());
		
		xbCfgKey plugkey(cfgkey,xbString(m_pId));

		plugkey.DeleteKey(true);
	}

	m_pPlugType->AskConfigUpdate();
}

void xbHotPlugFactory::CreateObject(IxbHotPlugin *pPluginContainer,const xbString &name,xbRefPtr<IxbObject> *ppObj,void *pCtorData)
{
	{
		xbSectLock lck(&m_sect);
		if (m_bUnloadAsked) 
			xbThrow xbEhotPlug("xbHotPlugFactory / Plugin '"+xbString(m_pLabel)+"' is unloading : can't create object '"+name+"' !");
	}

	const xbHotPlugFactoryList *pFact = m_pFactList;

	while(pFact->m_pName)
	{
		if (name.CompareNoCase(pFact->m_pName) == 0)
		{
			(*pFact->m_pCreateFct)(this,pPluginContainer,pFact->m_pName,pCtorData?pCtorData:pFact->m_pCtorData,ppObj);
			return;
		}
		pFact++;
	}
	xbThrow xbEsys("xbHotPlugFactory / Plugin '"+xbString(m_pLabel)+"' : object '"+name+"' doesn't exist !");
}

bool xbHotPlugFactory::Unload(IxbHotPlugin *pPluginContainer,bool bForce)
{
	{
		xbSectLock lck(&m_sect);
		m_bUnloadAsked = true;
		xbAssert(m_pDbgPluginContainer == pPluginContainer);

		if (m_ObjCnt > 0 && !bForce) return false;

		if (xbDiffTime(::GetTickCount(),m_lastTimeFreeCnt) < m_unloadDelayInMs) 
			return false;

		m_pDbgPluginContainer = NULL;

		lck.Unlock();
		m_poPluginContainer.Empty();
	}

	onUnload(pPluginContainer);

	return true;
}

void xbHotPlugFactory::Load(IxbHotPlugin *pPluginContainer)
{
	{
//		xbPtrLock ptrLck(pPluginContainer);
		xbSectLock lck(&m_sect);
		if (m_ObjCnt != 0
			|| (m_pDbgPluginContainer != pPluginContainer && m_pDbgPluginContainer != NULL)) 
			xbRelOutput("xbHotPlugFactory::Load() : internal pb in Plugin '"+xbString(m_pLabel)+"' (objcnt="+xbString(m_ObjCnt)+")");
		xbAssert(m_ObjCnt == 0);
		xbAssert(m_pDbgPluginContainer == pPluginContainer || m_pDbgPluginContainer == NULL);
		m_bUnloadAsked = false;
		m_pDbgPluginContainer = pPluginContainer;
		lck.Unlock();
		m_poPluginContainer = pPluginContainer;
	}

	try
	{
		onLoad(pPluginContainer);

		g_xbEngineCenter.Start();
	}
	catch(...)
	{
		{
			xbSectLock lck(&m_sect);
			m_bUnloadAsked = true;
		}
		throw;
	}
}

void xbHotPlugFactory::DecObjCnt()
{
	xbSectLock lck(&m_sect);
	m_ObjCnt--;
	xbAssert(m_ObjCnt >= 0);
	if (m_ObjCnt > 0) return;
	m_lastTimeFreeCnt = ::GetTickCount();
}

void xbHotPlugFactory::IncObjCnt()
{
	xbSectLock lck(&m_sect);
	m_ObjCnt++;
}


bool xbHotPlugFactory::ForceUnload()
{
	xbRefPtr<IxbHotPlugin>	prPlugCont;
	if (!GetPluginContainer(&prPlugCont)) 
		return false;
	xbDbgOutput("xbHotPlugFactory("+xbString(m_pLabel)+")::ForceUnload() called");
	eProtectTrace(prPlugCont->_forceUnload());
	eProtectTrace(m_poPluginContainer.Empty());
	return true;
}
#pragma warning(pop)
