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

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

#ifndef _xbVERSION_
#	include "xbVersion.h"
#endif

#ifndef _xbEXCEPTION_
#	include "xbException.h"
#endif

#define xbHOTPLUGCB_TRYCNT		50
#define xbHOTPLUGCB_TRYSLEEP	100

#define xbHOTPLUGINUNLOAD_TRYCNT	50
#define xbHOTPLUGINUNLOAD_TRYSLEEP	100

xbEXPORT bool xbHotPlugPathOk(const xbString &path);
xbEXPORT bool xbHotPlugPathOk(xbString *pPath);
xbEXPORT xbString xbHotPlugPathCheck(const xbString &path);
xbEXPORT int xbHotPlugTraceLevel();

#define xbHotPlugTrace(level,msg) \
	if (level <= xbHotPlugTraceLevel()) \
	{ \
		xbRelOutput(xbString("HOTPLUG: ")+msg); \
	} 

enum xbPlugStatus
{
	xbPlugNoop = 0,
	xbPlugVerif = 10,
	xbPlugDownloading = 20,
	xbPlugDownloadDone = 30,
	xbPlugUninstalling = 40,
	xbPlugUninstallDone = 50,
	xbPlugInstalling = 60,
	xbPlugInstallDone = 70
};

extern xbEXPORT xbString xbLabel(xbPlugStatus status);
enum xbPlugUpdateCaps //max integer value is taken on multiple response  
{
	xbPlugUpdateNormal = 0,
	xbPlugUpdateRetry = 1,
	xbPlugUpdateNever = 2
};


//Exception xbElock: to use to signal multithread locks problems (timeouts, deadlocks, ...)
xbEdclStart(xbEhotPlug,xbEadv,{9512CCAD-D0F5-449b-9683-11867E2B6588},xbEXPORT)
	virtual xbMBiconType GetMBoxIcon() const {return xbMBicon_Exclamation;}
	virtual const char *GetMBoxTitle() const {return "HotPlug system error";} 
	virtual bool IsToWriteOnLogFile() const 
	{
		return g_xbExceptionEngine.IsToLog("xbEhotPlug") 
			|| xbHotPlugTraceLevel() > 0;
	}
xbEdclEnd(xbEhotPlug);

class xbCfgKey;
interface __declspec(uuid("{665207F3-B65A-45d4-B88E-CACF028D50F1}")) 
	xbEXPORT sIxbHotPlugType:public sIxbBase
{
	virtual xbString	GetPlugTypeName() const = 0;
	virtual xbString	GetPlugTypeVer() const = 0;
	virtual xbString	GetPlugTypeLabel() const = 0;
	virtual xbString	GetConfigPath() const = 0;
	virtual xbCfgKey	GetConfigKey() const = 0;
};

interface __declspec(uuid("{29E92B51-AE22-41a0-A7F9-2094093F13DD}")) 
	xbEXPORT IxbHotPlugin:public IxbObject
{
	virtual xbString	GetPluginId() const = 0;
	virtual xbString	GetPluginLabel() const = 0;

	virtual xbString	GetPlugTypeName() const = 0;
	virtual xbString	GetPlugTypeVer() const = 0;
	virtual xbString	GetPlugTypeLabel() const = 0;
	virtual xbString	GetConfigPath() const = 0;
	virtual xbCfgKey	GetConfigKey() const = 0;

	virtual void		_forceUnload() = 0;
};

interface __declspec(uuid("{55BCA102-B371-4d0e-9AF8-9609F9DED1A3}")) 
	xbEXPORT IxbHotPluggedObject:public IxbObject
{
	virtual void	onDestroy(IxbHotPlugin *pPlugin) = 0;
};

interface xbEXPORT __declspec(uuid("{8E0D3622-4326-4eae-A3B1-0303262E36B2}")) 
	sIxbPlugPtr:public sIxbBase
{
	virtual void		onCreatePluggedObject(IxbHotPlugin *pPlugin, IxbObject *pObj) = 0;
	virtual void		onUnloadPluggedObject(IxbHotPlugin *pPlugin, xbRefPtr<IxbHotPluggedObject> *ppObj) = 0;
};


interface __declspec(uuid("{7C73CE65-A624-48a9-BE9C-BB47C51DAFA3}")) 
	xbEXPORT IxbHotPlugTypeCb:public IxbObject
{
	virtual xbPlugUpdateCaps	OnGetPluginCaps(IxbHotPlugin*) = 0; //called before internal update to see if update is possible and when.

	virtual void				OnUnloadPluginAsked(IxbHotPlugin*,bool bForce) = 0; //called by module message or OnStartBinding
	virtual void				OnLoadPluginAsked(IxbHotPlugin*) = 0; //called by module message or OnStopBinding / OnObjectAvailable
	virtual void				OnProgress(IxbHotPlugin*,double progress,
									xbPlugStatus status, const xbString &statusText) = 0;
	virtual xbPlugUpdateCaps	OnError(IxbHotPlugin*,bool bCanRetry, HRESULT hr, const xbString &errorText) = 0;

	virtual void				OnReloadConfig(sIxbHotPlugType *pPlugType) = 0;
};

class xbHotPlugCb;
interface __declspec(uuid("{A5A024C0-C8AA-484e-AC6F-3F2AF4682D7F}")) 
	xbEXPORT sIxbHotPlugTypeCb:public sIxbBase
{
	virtual xbPlugUpdateCaps	onGetPluginCaps(xbHotPlugCb *pCb,IxbHotPlugin*,xbSectLock &lck) = 0; //called before internal update to see if update is possible and when.

	virtual void				onUnloadPluginAsked(xbHotPlugCb *pCb,IxbHotPlugin*,bool bForce,xbSectLock &lck) = 0; //called by module message or OnStartBinding
	virtual void				onLoadPluginAsked(xbHotPlugCb *pCb,IxbHotPlugin*,xbSectLock &lck) = 0; //called by module message or OnStopBinding / OnObjectAvailable
	virtual xbPlugUpdateCaps	onError(xbHotPlugCb *pCb,IxbHotPlugin*,bool bCanRetry, HRESULT hr, const xbString &errorText,xbSectLock &lck) = 0;

	virtual void				onReloadConfig(xbHotPlugCb *pCb,sIxbHotPlugType *pPlugType,xbSectLock &lck) = 0;

	virtual void				onProgress(xbHotPlugCb *pCb,IxbHotPlugin *pPlugType, double progress,
									xbPlugStatus status, const xbString &statusText,xbSectLock &lck) = 0;
};

class xbEXPORT xbHotPlugCb:public xbBase,public sIxbHotPlugTypeCb
{
public:
	xbHotPlugCb(sIxbHotPlugTypeCb *pPlugTypeCb,bool bStartNotif = false,xbPlugUpdateCaps caps = xbPlugUpdateNormal)
	{
		m_pPlugTypeCb = pPlugTypeCb;
		m_bStartNotif = bStartNotif;
		m_caps = caps;
	}
	~xbHotPlugCb()
	{
		eProtectTrace(Destroy());
	}

	virtual void Create();
	virtual void Destroy();
	operator IxbHotPlugTypeCb*();
	virtual bool HasPlugTypeCb() {return m_pPlugTypeCb != NULL;}

//sIxbBase methods
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(sIxbHotPlugTypeCb);
		return xbBase::GetInterface(riid,ppInterf);
	}


	//sIxbHotPlugTypeCb methods
	virtual xbPlugUpdateCaps	onGetPluginCaps(xbHotPlugCb *pCb,IxbHotPlugin *pPlugin,xbSectLock &lck)
	{
		if (!m_pPlugTypeCb) return xbPlugUpdateNormal;
		return m_pPlugTypeCb->onGetPluginCaps(pCb,pPlugin,lck);
	}

	virtual void				onUnloadPluginAsked(xbHotPlugCb *pCb,IxbHotPlugin *pPlugin,bool bForce,xbSectLock &lck)
	{
		if (!m_pPlugTypeCb) return;
		m_pPlugTypeCb->onUnloadPluginAsked(pCb,pPlugin,bForce,lck);
	}

	virtual void				onLoadPluginAsked(xbHotPlugCb *pCb,IxbHotPlugin *pPlugin,xbSectLock &lck)
	{
		if (!m_pPlugTypeCb) return;
		m_pPlugTypeCb->onLoadPluginAsked(pCb,pPlugin,lck);
	}

	virtual xbPlugUpdateCaps	onError(xbHotPlugCb *pCb,IxbHotPlugin *pPlugin,bool bCanRetry, HRESULT hr, const xbString &errorText,xbSectLock &lck)
	{
		if (!m_pPlugTypeCb) return xbPlugUpdateNormal;
		return m_pPlugTypeCb->onError(pCb,pPlugin,bCanRetry,hr,errorText,lck);
	}

	virtual void				onReloadConfig(xbHotPlugCb *pCb,sIxbHotPlugType *pPlugType,xbSectLock &lck)
	{
		if (!m_pPlugTypeCb) return;
		m_pPlugTypeCb->onReloadConfig(pCb,pPlugType,lck);
	}
	virtual void onProgress(xbHotPlugCb *pCb,IxbHotPlugin *pPlugin,double progress,
		xbPlugStatus status, const xbString &statusText,xbSectLock &lck)
	{
		if (!m_pPlugTypeCb) return;
		m_pPlugTypeCb->onProgress(pCb,pPlugin,progress,status,statusText,lck);
	}

	class __declspec(uuid("{E96827BB-3B13-41b4-9796-A4E0065CD936}")) 
		CBobj:public xbObject,public IxbHotPlugTypeCb
	{
	public:
		CBobj(xbHotPlugCb *pParent,bool bStartNotif,xbPlugUpdateCaps caps)
		{
			m_bStartNotif = bStartNotif;
			m_caps = caps;
			m_pParent = pParent;
		}
		virtual ~CBobj()
		{
			xbAssert(m_pParent == NULL);
			eProtectTrace(Destroy());
		}

		xbImplObject();

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

		// IxbHotPlugTypeCb methods
		virtual xbPlugUpdateCaps	OnGetPluginCaps(IxbHotPlugin *)
		{
			xbSectLock lck(&m_sectPtr);
			return m_caps;
		}
		virtual void		OnUnloadPluginAsked(IxbHotPlugin *pPlugType,bool bForce) 
		{
			xbSectLock lck(&m_sectPtr);
			m_bStartNotif = true;
			if (m_bDestruction) return;
			m_pParent->onUnloadPluginAsked(m_pParent,pPlugType,bForce,lck);
		}
		virtual void		OnLoadPluginAsked(IxbHotPlugin *pPlugType)
		{
			xbSectLock lck(&m_sectPtr);
			if (!m_bStartNotif)
			{
				m_bStartNotif = true;
				return;
			}
			if (m_bDestruction) return;
			m_pParent->onLoadPluginAsked(m_pParent,pPlugType,lck);
		}
		virtual void		OnProgress(IxbHotPlugin *pPlugType, double progress,
							xbPlugStatus status, const xbString &statusText)
		{
			xbSectLock lck(&m_sectPtr);
			m_bStartNotif = true;
			if (m_bDestruction) return;
			m_pParent->onProgress(m_pParent,pPlugType,progress,status,statusText,lck);
		}
		virtual xbPlugUpdateCaps		OnError(IxbHotPlugin *pPlugType, bool bCanRetry, HRESULT hr, const xbString &errorText)
		{
			xbSectLock lck(&m_sectPtr);
			m_bStartNotif = true;
			if (m_bDestruction) return xbPlugUpdateNormal;
			return m_pParent->onError(m_pParent,pPlugType,bCanRetry,hr,errorText,lck);
		}
		virtual void				OnReloadConfig(sIxbHotPlugType *pPlugType)
		{
			xbSectLock lck(&m_sectPtr);
			if (!m_bStartNotif)
			{
				m_bStartNotif = true;
				return;
			}
			if (m_bDestruction) return;
			m_pParent->onReloadConfig(m_pParent,pPlugType,lck);
		}
	protected:
		virtual void onBeforeDestruction(bool /*bInDestroy*/) 
		{
			xbSectLock lck(&m_sectPtr);
			m_pParent = NULL;
			m_bDestruction = true;
		}
		xbHotPlugCb			*m_pParent;
		xbPlugUpdateCaps	m_caps;
		bool				m_bStartNotif;

		friend xbHotPlugCb;
	};

protected:
	sIxbHotPlugTypeCb	*m_pPlugTypeCb;
	xbRefPtr<CBobj>		m_prCBobj;
	xbPlugUpdateCaps	m_caps;
	bool				m_bStartNotif;
};
#endif
