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

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _xbHOTPLUG_
#	include "xbHotPlug.h"
#endif

#ifndef _xbDNITF_
#	include "xbDnItf.h"
#endif

extern xbHotPlugType xbEXPORT g_xbDnPlugType;

interface __declspec(uuid("{6595571A-398C-4ff5-BEF5-975584C886B7}")) 
	xbEXPORT IxbDnPlugProto:public IxbDnProto
{
	virtual bool CanWorkWithProto(xbDnData *pDnData,IxbDnObject *pObj,const xbString &szProto) const = 0;

	virtual void askClose(LPCSTR pReason) = 0;
	virtual xbString getCloseReason() const = 0;

	virtual bool	ReadUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbInputStream> *ppStm) = 0; //return true, if op completed -> no need to read bytes (*ppStm in this case is empty)
	virtual bool	WriteUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbOutputStream> *ppStm) = 0; //return true, if op completed -> no need to read bytes (*ppStm in this case is empty)

	virtual xbString	closeReasonLabel(bool bLfAfter = true) const
	{
		xbString ret(getCloseReason());
		if (ret.IsEmpty())
			return ret;
		return "(reason: "+ret+(bLfAfter?")\n":")");
	}
};

interface __declspec(uuid("{4ED6E887-B3CA-4098-A7E7-A88A98145EE4}")) 
	xbEXPORT IxbDnPlugFactory:public IxbObject
{
	virtual bool CanWorkWithProto(xbDnData *pDnData,IxbDnObject *pObj,const xbString &szProto) const = 0;

	virtual void ProtoCreate(IxbDnObject *pObj,const xbString &szProto,xbDnOp op,xbRefPtr<IxbDnPlugProto> *ppProto) = 0;
};

interface __declspec(uuid("{5E14BC1C-8447-442d-AA1D-D1FDEAD29F61}")) 
	xbEXPORT IxbDnObjectProto:public IxbObject
{
	virtual bool ProtoCreate(xbDnOp op,const xbString &szUrl,xbRefPtr<IxbDnPlugProto> *ppProto) = 0;
};

class xbEXPORT xbDnSession:public xbBase, public sIxbDnSession
{
public:
	xbDnSession(xbThreadEventObject *pEvent);
	virtual ~xbDnSession()
	{
		eProtectTrace(Destroy("xbDnSession dtor"));
		eProtectTrace(m_poDnPlugin.Destroy());
	}

	void Destroy(LPCSTR pReason);
	void Close(LPCSTR pReason);
//sIxbBase methods
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(sIxbDnSession);
		return xbBase::GetInterface(riid,ppInterf);
	}

//sIxbDnSession methods
	virtual bool	ReadUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbInputStream> *ppStm);
	virtual bool	WriteUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbOutputStream> *ppStm);

	virtual bool	DoDnOp(xbDnData *pCallerData,xbDnOp op,const xbString &url,IxbDnObject *pObj);

	virtual bool isHotPlugCloseEvent() const
	{
		return m_bIsProtoPlug && m_bPlugClosed;
	}
/*	virtual bool	ShouldStopDnOp() const
	{
		xbSectLock lck(&m_sect);
		return m_bDestruction || (m_bIsProtoPlug && m_bPlugClosed);
	}
	virtual void	CheckStopDnOp(const xbString &url) const
	{
		xbSectLock lck(&m_sect);
		if (m_bDestruction)
		{
			lck.Unlock();
			xbThrow xbEdnUser(E_ABORT,"Upload/Download cancelled, download center stopping...",closeReasonLabel()+"Url:"+url+xbEdebug("\nxbDnSession::checkStopDnOp()"));
		}
		if (m_bIsProtoPlug && m_bPlugClosed)
		{
			lck.Unlock();
			xbThrow xbEhotPlug("Upload/Download cancelled, download center updating plugins...",closeReasonLabel()+"Url:"+url+xbEdebug("\nxbDnSession::checkStopDnOp()"));
		}
	}*/
	void ResetStatus()
	{
		xbSectLock lck(&m_sect);
		m_pEvent->ResumeTaskProcess();
		if (m_prDnPlugProto.IsValid())
			return;
		m_bIsProtoPlug = false;
		m_bPlugClosed = false;
		m_closeReason.Empty();
	}
	
	virtual xbString getCloseReason() const
	{
		xbSectLock lck(&m_sect);
		xbString ret(m_closeReason);
		ret.OwnData();
		return ret;
	}
	virtual xbString	closeReasonLabel(bool bLfAfter = true) const
	{
		xbString ret(getCloseReason());
		if (ret.IsEmpty())
			return ret;
		return "(reason: "+ret+(bLfAfter?")\n":")");
	}

protected:
	mutable xbSectShare	m_sect;

	class xbEXPORT PluginPtr:public xbObsPtrExt<IxbHotPlugin>,public sIxbPlugPtr
	{
	public:
		PluginPtr(xbDnSession *pSession)
			:xbObsPtrExt<IxbHotPlugin>(&pSession->m_sect)
		{
			m_pSession = pSession;
		}
		virtual bool GetInterface(REFIID riid, void ** ppInterf)
		{
			xbImplInterface(sIxbPlugPtr);
			
			return xbObsPtrExt<IxbHotPlugin>::GetInterface(riid,ppInterf);
		}
		virtual void onCreatePluggedObject(IxbHotPlugin *pPlugin, IxbObject *pObj)
		{
			xbObsPtrExt<IxbHotPlugin>::operator=(pPlugin);
			try
			{
				xbDnSession *pSession;
				{
					xbSectLock lck(m_pSect);
					pSession = m_pSession;
				}
				if (!pSession)
					return;
				pSession->onCreateDnPlug(pObj);
			}
			catch(...)
			{
				Empty();
				throw;
			}
		}
		virtual void onUnloadPluggedObject(IxbHotPlugin *pPlugin, xbRefPtr<IxbHotPluggedObject> *ppObj)
		{
			Empty();
		}
		void Destroy()
		{
			Empty();
			{
				xbSectLock lck(m_pSect);
				m_pSession = NULL;
			}
		}
	protected:
		virtual void onUnlink(sIxbPtrNotif*pPtrNotif)
		{
			xbDnSession *pSession;
			{
				xbSectLock lck(m_pSect);
				pSession = m_pSession;
			}
			if (!pSession)
				return;
			pSession->askCloseDnPlug("Session plugin pointer unlinked");
		}
		xbDnSession	*m_pSession;
	};
	friend PluginPtr;


	//protected by m_sect
	bool							m_bDestruction,m_bPlugClosed;
	PluginPtr						m_poDnPlugin;
	xbRefPtr<IxbDnPlugFactory>		m_prDnPlugFactory;
	xbRefPtr<IxbDnPlugProto>		m_prDnPlugProto;
	bool							m_bIsProtoPlug,m_bInPlugProtoUpdate;
	xbRefPtr<IxbDnObject>			m_prObj;
	xbString						m_closeReason;
	xbThreadEventObject				*m_pEvent;

	void onCreateDnPlug(IxbObject *pObj);
	void askCloseDnPlug(LPCSTR pReason,bool bForce=false);

	void updateProto(xbDnData *pDnData,IxbDnObject *pObj,xbRefPtr<IxbDnPlugProto> *ppProto);

	bool getBuiltinProto(const xbString &proto,xbDnOp op,xbRefPtr<IxbDnPlugProto> *ppProto);

	void doDnOp(xbDnData *pDnData,IxbDnObject *pObj);
	void doDnLoad(xbDnData *pDnData,IxbDnObject *pObj);
	void doDnWrite(xbDnData *pDnData,IxbDnObject *pObj);
};

class __declspec(uuid("{E96827BB-3B13-41b4-9796-A4E0065CD936}")) 
	xbEXPORT xbDnBuiltinPlugin:public xbObject,public IxbDnPlugProto
{
public:
	xbDnBuiltinPlugin()
	{
	}
	virtual ~xbDnBuiltinPlugin()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();

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

//IxbDnProto
	virtual bool	askStopDnOp(IxbDnObject *pObj) {return false;}

//IxbDnPlugProto
	virtual bool CanWorkWithProto(xbDnData *pDnData,IxbDnObject *pObj,const xbString &szProto) const;

	virtual void askClose(LPCSTR pReason) {}
	virtual xbString getCloseReason() const {return xbString();}

	virtual bool	ReadUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbInputStream> *ppStm); //return true, if op completed -> no need to read bytes (*ppStm in this case is empty)
	virtual bool	WriteUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbOutputStream> *ppStm); //return true, if op completed -> no need to read bytes (*ppStm in this case is empty)
protected:
	bool tryResDownload(xbDnData *pDnData,IxbDnObject *pObj,xbRefPtr<IxbInputStream> *ppStm);
	bool tryDataDownload(xbDnData *pDnData,IxbDnObject *pObj,xbRefPtr<IxbInputStream> *ppStm);
};

#endif
