#ifndef _xbSOCKET_
#define _xbSOCKET_
/*<<<***************************************************************************
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: xbSocket
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<11/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifndef _xbDATA_
#	include "xbData.h"
#endif

#ifndef _xbMSG_
#	include "xbMsg.h"
#endif

#ifndef _xbTHREAD_
#	include "xbThread.h"
#endif

#ifndef _xbSOCKETITF_
#	include "xbSocketItf.h"
#endif

#ifndef _xbWINBASE_
#	include "xbWinBase.h"
#endif

#define xbSOCKET_WRITETIMEOUT	1000

class xbSocket;

class xbEXPORT xbSocket:protected xbThread,public sIxbNotifLock
{
public:
	xbSocket();

	virtual ~xbSocket()
	{
		eProtect(destroy("xbSocket destruction"));
	}

	void Create(const xbString &szHostPort,xbSectLock *pLck = NULL);
	void Connect(const xbString &szHostAddress,const xbString &szHostPort);
	void Listen(const xbString &szHostPort,LPCSTR pszHostAddress = NULL);
	void Accept(xbSocket &SocketListening);

	void Close(LPCSTR pReason);
	void ShutDown(LPCSTR pReason);

	virtual void NotifLock()
	{
		xbSectLock lck(&m_sect);
		m_lNotif++;
	}
	virtual void NotifUnlock()
	{
		xbSectLock lck(&m_sect);
		m_lNotif--;
	}

	bool	HasData() const;
	int		GetData (xbData &s);	
	void	SendData (const xbData &data);

	void	SetDbgTrace(bool trace)
	{
		xbSectLock lck(&m_sect);
		m_bTraceData = trace;
	}

	bool	IsConnected() const
	{
//		xbSectLock lck(&m_sect);
		return m_bSocketConnected;
	}
	bool	IsClosed() const
	{
//		xbSectLock lck(&m_sect);
		return !m_bSocketConnected;
	}
	bool	IsShutDown() const
	{
//		xbSectLock lck(&m_sect);
		return m_bSocketShutDown || !m_bSocketConnected;
	}
	inline DWORD		GetThreadId(xbOpMode opMode = xbOp_Required)
	{
		xbLock lck;
		sIxbThread *pTh = GetThread(&lck,opMode);
		if (!pTh) return 0;
		return pTh->Id;
	}
	xbString		GetHostName();
	xbString		GetHostNameByAddr(const xbString &addr,xbOpMode opm =xbOp_Required);
	xbString		GetHostAddrByName(const xbString &name,xbOpMode opm =xbOp_Required);
	int				GetHostNameByAddr(const xbString &addr,xbArray<xbString> *pArrName,xbOpMode opm =xbOp_Required);
	int				GetHostAddrByName(const xbString &name,xbArray<xbString> *pArrAddr,xbOpMode opm =xbOp_Required);

	xbString		GetPeerIP(xbString *pPort = NULL);
	xbString		GetSockIP(xbString *pPort = NULL);

	void destroy(LPCSTR pReason)
	{
		xbThread::destroy(pReason);
	}
	bool AskDestroy(LPCSTR pReason)
	{
		return xbThread::AskDestroy(pReason);
	}
	enum OpAsked
	{
		NothingAsked=0,
			ConnectAsked=1,
			ListenAsked=2,
			AcceptAsked=3,
			CloseAsked=4,
//			DestroyAsked=5,
			ShutDownAsked=6
	};
	OpAsked GetOpAsked() const
	{
		return m_opAsked;
	}
	OpAsked GetOpInExec() const
	{
		return m_opInExec;
	}
	xbString getCloseReason() const
	{
		xbSectLock lck(&m_sect);
		xbString ret(m_closeReason);
		if (ret.IsEmpty())
			ret = m_shutReason;
		ret.OwnData();
		return ret;
	}
	xbString getShutDownReason() const
	{
		xbSectLock lck(&m_sect);
		xbString ret(m_shutReason);
		ret.OwnData();
		return ret;
	}
	virtual xbString	closeReasonLabel(bool bLfAfter = true) const
	{
		xbString ret(getCloseReason());
		if (ret.IsEmpty())
			return ret;
		return "(close reason: "+ret+(bLfAfter?")\n":")");
	}
	virtual bool SocketOptionGet(LPCSTR pOptionName,xbString *pOptionValue,xbOpMode opm =xbOp_Required,int cRawByteOrArg = 0) const;
	virtual bool SocketOptionSet(LPCSTR pOptionName,LPCSTR pOptionValue,xbOpMode opm =xbOp_Required,int cRawByteOrArg = 0);

protected:
//Overridable methods
	virtual int		GetWaitTimeOut() {return m_bSocketShutDown?10000: 1000 /*-1*/;}
	virtual bool	OnWaitTimeOut(int cnt) 
	{
		if (cnt > 2 && m_bSocketShutDown)
			return false;
		return true;
	}

	virtual void	OnStateChange(xbSocketCtx ctx,int errcode) = 0;
	virtual void	OnBeforeDoConnect() {}
	virtual void	OnAfterDoClose() {}
	virtual void	OnDataRead(xbData &dta);

	virtual void	OnDoOp(OpAsked op,xbSocketCtx &ctx);

//sIxbThreadCb overrides
	virtual DWORD	onThreadProc(sIxbThread *pTh);
	virtual void	onCloseAsked(sIxbThread *pTh,LPCSTR pReason);
	virtual DWORD	onStop(sIxbThread *pTh,DWORD ret);

//Implementation
	void onCreateSocket(IxbObject *pObj);

	void doConnect();
	void doListen();
	void doAccept();
	void doClose(LPCSTR pReason,bool bToReuse,int closectxerr/* = 0*/);
	void doRead();
	void doShutDown(LPCSTR pReason);

	void askOp(OpAsked op);

	void setShutDownReason(LPCSTR pReason)
	{
		xbSectLock lck(m_pSect);
		if (pReason && m_shutReason.IsEmpty())
			m_shutReason = pReason;
	}

	class xbEXPORT PluginPtr:public xbObsPtrExt<IxbHotPlugin>,public sIxbPlugPtr
	{
	public:
		PluginPtr(xbSocket *pSocket)
			:xbObsPtrExt<IxbHotPlugin>(&pSocket->m_sect)
		{
			m_pSocket = pSocket;
		}
		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
			{
				m_pSocket->onCreateSocket(pObj);
			}
			catch(...)
			{
				Empty();
				throw;
			}
		}
		virtual void onUnloadPluggedObject(IxbHotPlugin *pPlugin, xbRefPtr<IxbHotPluggedObject> *ppObj)
		{
			Empty();
		}
	protected:
		virtual void onUnlink(sIxbPtrNotif*pPtrNotif)
		{
			m_pSocket->askClose(NULL,"Socket plugin pointer unlinked");
		}
		xbSocket	*m_pSocket;
	};
	friend PluginPtr;


	//protected by m_sect
	PluginPtr			m_poSocketPlugin;
	bool				m_bCreated,m_bSocketConnected,m_bSocketShutDown;

	xbRefPtr<IxbSocketPlug> m_prSocketPlug;
	xbRefPtr<IxbSocket>		m_prExtSock;

	OpAsked				m_opAsked,m_opInExec;

	xbString			m_szHostPort;

	xbString			m_szHostAddressAsked;
	xbString			m_szHostPortAsked;

	xbRefPtr<IxbSocket> m_prSockAsked;
	
	//not protected by m_sect, used only by thread
	xbRefPtr<IxbSocket>		m_prSock; 

	xbRefPtr<IxbSocketEv>	m_prEvSock;

	long				m_lNotif;
	bool				m_bConnectNotifSend;
	xbString			m_LastErrorMsg,m_LastErrorCtx;

	xbData				m_buf;
	xbArray<xbData>		m_arrBuf;

	xbString			m_shutReason;
	bool				m_bTraceData;
};

class xbEXPORT xbWinSocket:public xbSocket
{
public:
	xbWinSocket(xbWinBase *pParent)
	{
		m_pParent = pParent;
	}
protected:
	virtual void	OnStateChange(xbSocketCtx ctx,int errcode)
	{
		if (!m_pParent || !m_pParent->IsValid()) 
			return;
		DWORD msgId = xbWMCTRL_WINSOCK_NOTIF.GetMsg();
		if (msgId == 0)
			return;
		xbPostMessage(m_pParent->GetHandle(),msgId,WPARAM(ctx),errcode);
	}

	xbWinBase				*m_pParent;
};


#endif
