/*<<<***************************************************************************
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.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbSocket.h"
#ifndef _PDA
#include "process.h"
#endif

#ifdef _DEBUG
#	define dbgREAD
#endif

#pragma warning(push)
#pragma warning(disable: 4355) //'this' : used in base member initializer list
#pragma warning(disable: 4311) //'<function-style-cast>' : pointer truncation from 'sIxbThread *' to 'xbNptr'

xbSocket::xbSocket()
	:xbThread("Base HotPlug Socket Thread"),
	m_poSocketPlugin(this)
{
	m_lNotif = 0;
	m_bConnectNotifSend = false;
	m_opAsked = NothingAsked;
	m_opInExec = NothingAsked;
	m_bCreated = m_bSocketConnected = m_bSocketShutDown = false;
	m_bTraceData = false;
}


void xbSocket::Create(const xbString &_szHostPort,xbSectLock *pLck)
{
	xbSectLock _lck;
	if (!pLck)
	{
		pLck = &_lck;
		_lck.Lock(m_pSect);
	}

	xbString	szHostPort(_szHostPort);
	bool		bPortRequired = false;
	if (szHostPort.IsValid() 
		&& szHostPort.First() == '!')
	{
		bPortRequired = true;
		szHostPort.Delete(0);
		szHostPort.Trim();
	}
	int cntMax = 10;
	for(;;)
	{
		if (!m_bCreated) 
			break;
		xbRefPtr<IxbHotPlugin> prPlugin;
		xbString subname(szHostPort);
		for(;;)
		{
			subname.TrimLeft();
			subname.TrimRight(". ");
			if (subname.IsEmpty()) 
				break;

			if (m_poSocketPlugin.GetPtr(&prPlugin)
				&& g_xbSocketPlugType.CanWorkWith(prPlugin,subname)
				&& m_prSocketPlug->CanWorkWithPort(szHostPort))
			{
				if (setBusy(xbOp_Optional))
				{
					m_bCreated = true;
					m_szHostPortAsked = szHostPort;
					m_szHostPortAsked.OwnData();
					return;
				}
				break;
			}
			int pos = subname.LastIndexOf('.');
			if (pos <= 0)
			{
				if (bPortRequired)
					break;
				if (subname == "default") 
					break;
				subname = "default";
				continue;
			}
			subname = xbString(subname,pos);
		}
		pLck->Unlock();
		if (--cntMax < 0)
			xbThrow xbEsocket(xbHR_TIMEOUT,"Socket creation failure on port "+xbString(_szHostPort),"xbSocket::Create()");
		destroy("xbSocket recreation, waiting previous thread destruction");
		::xbSleep(200);
		pLck->Lock(&m_sect);
	}

	xbAssert(m_prSocketPlug.IsEmpty());

	try
	{
		pLck->Unlock();

		bool bPluginInUpdate = g_xbSocketPlugType.IsInUpdate();
		xbString subname(szHostPort);
		for(;;)
		{
			subname.TrimLeft();
			subname.TrimRight(". ");
			if (subname.IsValid() 
				&& g_xbSocketPlugType.CreatePluggedObject(&m_poSocketPlugin,subname))
				break;
			int pos = subname.LastIndexOf('.');
			if (pos <= 0)
			{
				if (bPortRequired || !g_xbSocketPlugType.CreatePluggedObject(&m_poSocketPlugin,"default"))
				{
					if (!bPluginInUpdate && !g_xbSocketPlugType.IsInUpdate())
						xbThrow xbEsocketUser("Can't create network connection on port '"+szHostPort+"' !\nReason : socket type invalid or not supported","xbSocket::Create()");

					xbThrow xbEhotPlug("Can't create network connection on port '"+szHostPort+"' !\nReason : socket plugin not ready","xbSocket::Create()");
				}
				break;
			}
			subname = xbString(subname,pos);
			bPortRequired = true;
		}

		xbThread::Create(true,pLck);
	}
	catch(...)
	{
		onStop(NULL,-1);
		throw;
	}

	m_bCreated = true;
	m_szHostPortAsked = szHostPort;
	m_szHostPortAsked.OwnData();
}

void xbSocket::onCreateSocket(IxbObject *pObj)
{
	xbRefQIPtr<IxbSocketPlug> prSocketPlug(pObj);

	xbRefPtr<IxbSocketEv> prEvSock;
	prSocketPlug->EventCreate(&prEvSock);

	xbSectLock	lck(&m_sect);
	m_opAsked = NothingAsked;
	m_prEvSock = prEvSock;
	m_prSocketPlug = prSocketPlug;

	xbAssert(!m_bCreated);
}

DWORD	xbSocket::onStop(sIxbThread *pTh,DWORD ret)
{
	xbSectLock	lck(&m_sect);
	if (!xbMustBe(pTh == m_pTh))
	{
		xbRelOutput("xbSocket::Thread::onStop("+xbString(ret)+") : thread cb attached to another thread (stopped="
			+xbString(xbNptr(pTh),xbStringNum_hexa)+", actual="+xbString(xbNptr(m_pTh),xbStringNum_hexa)+")");
		return ret;
	}
	ret = xbThread::onStop(pTh,ret);

	m_bCreated = false;
	xbRefPtr<IxbSocketPlug> prSocketPlug(m_prSocketPlug);
	xbRefPtr<IxbSocket>		prExtSock(m_prExtSock);
	xbRefPtr<IxbSocket>		prSockAsked(m_prSockAsked);
	xbRefPtr<IxbSocket>		prSock(m_prSock); 
	xbRefPtr<IxbSocketEv>	prEvSock(m_prEvSock);
	m_prEvSock.Empty();
	m_prSocketPlug.Empty();
	xbAssert(m_prExtSock.IsEmpty());
	m_prExtSock.Empty();
	xbAssert(m_prSock.IsEmpty());
	m_prSock.Empty();
	m_prSockAsked.Empty();
	unsetBusy();
	lck.Unlock();

	return ret;
}

int xbSocket::GetData(xbData &s)
{
	xbSectLock	lck(&m_sect);
	if (m_arrBuf.IsEmpty())
	{
		s.Clear();
		return 0;
	}
	s.Attach(&m_arrBuf.First());
	m_arrBuf.Delete(0);
	if (m_bTraceData) xbDataTrace("INPUT",s);
	return s.GetLength();
}

bool	xbSocket::HasData() const
{
//	xbSectLock	lck(&m_sect);
	return m_arrBuf.IsValid();
}

void xbSocket::SendData (const xbData &data)
{ 
	if (data.IsEmpty()) return;
	if (m_bTraceData) xbDataTrace("OUTPUT",data);
	xbRefPtr<IxbSocket> prSock;
	{
		xbSectLock	lck(&m_sect);
		prSock = m_prExtSock;
		if (!prSock)
			 xbThrow xbEsocketUser("Socket not connected !",closeReasonLabel()+"xbSocket::SendData()");
	}

	const char *ptr = (const char*)(const void *)data;
	long		sz = data.GetLength();
	while(sz > 0)
	{
		int ret = prSock->WriteData(ptr,sz,0);
		if (ret <= 0)
		{
			prSock->EventWait(xbSOCKET_WRITETIMEOUT,NULL,0);
		}
		else 
		{
			sz -= ret;
			ptr += ret;
		}
	}
}

xbString	xbSocket::GetHostName()
{
	xbSectLock	lck(&m_sect);
	if (!m_bCreated)
	{
		xbString szPort(m_szHostPortAsked);
		szPort.OwnData();
		lck.Unlock();
		Create(szPort);
		lck.Lock(&m_sect);
	}
	if (!m_bCreated) xbThrow xbEsys("xbSocket::GetHostName() : socket not created !");
	return m_prSocketPlug->GetHostName();
}

xbString	xbSocket::GetHostNameByAddr(const xbString &addr,xbOpMode opm)
{
	xbSectLock	lck(&m_sect);
	if (!m_bCreated)
	{
		xbString szPort(m_szHostPortAsked);
		szPort.OwnData();
		lck.Unlock();
		Create(szPort);
		lck.Lock(&m_sect);
	}
	if (!m_bCreated) xbThrow xbEsys("xbSocket::GetHostNameByAddr() : socket not created !");
	return m_prSocketPlug->GetHostNameByAddr(addr,opm);
}

xbString	xbSocket::GetHostAddrByName(const xbString &name,xbOpMode opm)
{
	xbSectLock	lck(&m_sect);
	if (!m_bCreated)
	{
		xbString szPort(m_szHostPortAsked);
		szPort.OwnData();
		lck.Unlock();
		Create(szPort);
		lck.Lock(&m_sect);
	}
	if (!m_bCreated) xbThrow xbEsys("xbSocket::GetHostAddrByName() : socket not created !");
	return m_prSocketPlug->GetHostAddrByName(name,opm);
}

int			xbSocket::GetHostNameByAddr(const xbString &addr,xbArray<xbString> *pArrName,xbOpMode opm)
{
	xbSectLock	lck(&m_sect);
	if (!m_bCreated)
	{
		xbString szPort(m_szHostPortAsked);
		szPort.OwnData();
		lck.Unlock();
		Create(szPort);
		lck.Lock(&m_sect);
	}
	if (!m_bCreated) xbThrow xbEsys("xbSocket::GetHostNameByAddr(2) : socket not created !");
	return m_prSocketPlug->GetHostNameByAddr(addr,pArrName,opm);
}

int			xbSocket::GetHostAddrByName(const xbString &name,xbArray<xbString> *pArrAddr,xbOpMode opm)
{
	xbSectLock	lck(&m_sect);
	if (!m_bCreated)
	{
		xbString szPort(m_szHostPortAsked);
		szPort.OwnData();
		lck.Unlock();
		Create(szPort);
		lck.Lock(&m_sect);
	}
	if (!m_bCreated) xbThrow xbEsys("xbSocket::GetHostAddrByName(2) : socket not created !");
	return m_prSocketPlug->GetHostAddrByName(name,pArrAddr,opm);
}

xbString xbSocket::GetPeerIP(xbString *pPort)
{ 
	xbRefPtr<IxbSocket> prSock;
	{
		xbSectLock	lck(&m_sect);
		prSock = m_prExtSock;
		if (!prSock)
			 xbThrow xbEsocketUser("Socket not connected !",closeReasonLabel()+"xbSocket::GetPeerIP()");
	}
	return prSock->GetPeerIP(pPort);
}

xbString xbSocket::GetSockIP(xbString *pPort)
{ 
	xbRefPtr<IxbSocket> prSock;
	{
		xbSectLock	lck(&m_sect);
		prSock = m_prExtSock;
		if (!prSock)
			 xbThrow xbEsocketUser("Socket not connected !",closeReasonLabel()+"xbSocket::GetSockIP()");
	}
	return prSock->GetSockIP(pPort);
}

bool xbSocket::SocketOptionGet(LPCSTR pOptionName,xbString *pOptionValue,xbOpMode opm,int cRawByteOrArg) const
{
	xbRefPtr<IxbSocket> prSock;
	{
		xbSectLock	lck(&m_sect);
		prSock = m_prExtSock;
		if (!prSock)
		{
			if (opm == xbOp_Required)
				 xbThrow xbEsocketUser("Socket not connected !",closeReasonLabel()+"xbSocket::SocketOptionGet()");
			return false;
		}
	}
	return prSock->SocketOptionGet(pOptionName,pOptionValue,opm,cRawByteOrArg);
}

bool xbSocket::SocketOptionSet(LPCSTR pOptionName,LPCSTR pOptionValue,xbOpMode opm,int cRawByteOrArg)
{
	xbRefPtr<IxbSocket> prSock;
	{
		xbSectLock	lck(&m_sect);
		prSock = m_prExtSock;
		if (!prSock)
		{
			if (opm == xbOp_Required)
				 xbThrow xbEsocketUser("Socket not connected !",closeReasonLabel()+"xbSocket::SocketOptionSet()");
			return false;
		}
	}
	return prSock->SocketOptionSet(pOptionName,pOptionValue,opm,cRawByteOrArg);
}

void xbSocket::Connect(const xbString &szHostAddress,const xbString &szHostPort)
{
	Create(szHostPort);
	xbSectLock	lck(&m_sect);
	if (!m_bCreated) xbThrow xbEsys("xbSocket::Connect() : socket not created !");
	m_closeReason.Empty();
	m_shutReason.Empty();
	m_szHostAddressAsked = szHostAddress;
	m_szHostAddressAsked.OwnData();
	m_prSockAsked.Empty();
	askOp(ConnectAsked);
}


void xbSocket::Listen(const xbString &szHostPort,LPCSTR pszHostAddress)
{
	Create(szHostPort);
	xbSectLock	lck(&m_sect);
	if (!m_bCreated) xbThrow xbEsys("xbSocket::Listen() : socket not created !");
	m_closeReason.Empty();
	m_shutReason.Empty();
	if (pszHostAddress && *pszHostAddress)
	{
		m_szHostAddressAsked = pszHostAddress;
		m_szHostAddressAsked.OwnData();
	}
	else
		m_szHostAddressAsked.Empty();
	m_prSockAsked.Empty();
	askOp(ListenAsked);
}

void xbSocket::Accept(xbSocket &SocketListening)
{
	xbRefPtr<IxbSocket> prSock;
	xbString			szHostPort;
	{
		xbSectLock	lck(&SocketListening.m_sect);
		if (!SocketListening.m_bCreated
			|| SocketListening.m_prExtSock.IsEmpty()) 
			xbThrow xbEsocket("Invalid listening socket !","xbSocket::Accept()");
		prSock = SocketListening.m_prExtSock;
		szHostPort = SocketListening.m_szHostPort;
		szHostPort.OwnData();
	}

	Create(szHostPort);
	xbSectLock	lck(&m_sect);
	if (!m_bCreated) xbThrow xbEsys("xbSocket::Accept() : socket not created !");
	m_closeReason.Empty();
	m_shutReason.Empty();
	m_prSockAsked = prSock;
	m_szHostAddressAsked.Empty();
	askOp(AcceptAsked);
}

void xbSocket::Close(LPCSTR pReason)
{
	xbSectLock	lck(&m_sect);
	if (!m_bCreated) return;
	//xbThrow TExcept("xbSocket::Connect() : socket not created !");

	m_prSockAsked.Empty();
	m_szHostAddressAsked.Empty();

	setCloseReason(pReason);

	askOp(CloseAsked);
}

void xbSocket::ShutDown(LPCSTR pReason)
{
	xbSectLock	lck(&m_sect);
	if (!m_bCreated) return;
	//xbThrow TExcept("xbSocket::Connect() : socket not created !");

	m_prSockAsked.Empty();
	m_szHostAddressAsked.Empty();

	setShutDownReason(pReason);

	askOp(ShutDownAsked);
}

void xbSocket::askOp(OpAsked op)
{
	xbSectLock	lck(&m_sect);
	m_opAsked = op;
	m_eventPtr->Set();
}

void xbSocket::doConnect()
{
	xbRefPtr<IxbSocketPlug> prSockPlug;
	xbString			szHost,szPort;

	{
		xbSectLock	lck(&m_sect);
		prSockPlug = m_prSocketPlug;
		if (prSockPlug.IsEmpty() || IsCloseAskedInt()) return;

		szHost = m_szHostAddressAsked;
		szHost.OwnData();
		szPort = m_szHostPortAsked;
		szPort.OwnData();
	}

	doClose("xbSocket new connection, closing previous",true,0);

	xbDbgOutput("   > xbSocket::doConnect()...");
	OnBeforeDoConnect();

	m_bConnectNotifSend = false;

	prSockPlug->SocketConnect(m_prEvSock,szHost,szPort,&m_prSock);

	{
		xbSectLock	lck(&m_sect);
		m_prExtSock = m_prSock;
		m_szHostPort = szPort;
		m_szHostPort.OwnData();
	}	
}

void xbSocket::doListen()
{
	xbRefPtr<IxbSocketPlug> prSockPlug;
	xbString			szHost,szPort;

	{
		xbSectLock	lck(&m_sect);
		prSockPlug = m_prSocketPlug;
		if (!prSockPlug || IsCloseAskedInt()) return;
		szHost = m_szHostAddressAsked;
		szHost.OwnData();
		szPort = m_szHostPortAsked;
		szPort.OwnData();
	}

	doClose("xbSocket new connections to listen, closing previous",true,0);

	xbDbgOutput("   > xbSocket::doListen()...");
	OnBeforeDoConnect();

	m_bConnectNotifSend = false;

	prSockPlug->SocketListen(m_prEvSock,szPort,szHost,&m_prSock);

	{
		xbSectLock	lck(&m_sect);
		m_prExtSock = m_prSock;
		m_bSocketConnected = true;
		m_bSocketShutDown = false;
		m_szHostPort = szPort;
		m_szHostPort.OwnData();
	}
	try
	{
		xbDbgOutput("  -- xbSocket::doListen() / socket listening !");
		OnStateChange(xbSocketConnect,0);
		m_bConnectNotifSend = true;
	}
	catch(...)
	{
		doClose("xbSocket listen failed, connection closed",false,0);
		throw;
	}
}

void xbSocket::doAccept()
{
	xbRefPtr<IxbSocketPlug> prSockPlug;
	xbRefPtr<IxbSocket>		prSockListening;
	xbString				szPort;

	{
		xbSectLock	lck(&m_sect);
		prSockListening = m_prSockAsked;
		m_prSockAsked.Empty();
		prSockPlug = m_prSocketPlug;
		if (!prSockPlug || IsCloseAskedInt() || !prSockListening) return;
		szPort = m_szHostPortAsked;
		szPort.OwnData();
	}

	doClose("xbSocket new accepted connection, previous connection closed",true,0);

	xbDbgOutput("   > xbSocket::doAccept()...");
	OnBeforeDoConnect();

	m_bConnectNotifSend = false;

	prSockPlug->SocketAccept(m_prEvSock,prSockListening,&m_prSock);

	{
		xbSectLock	lck(&m_sect);
		m_prExtSock = m_prSock;
		m_szHostPort = szPort;
		m_szHostPort.OwnData();
	}
}

void xbSocket::doClose(LPCSTR pReason,bool bToReuse,int closectxerr)
{
	{
		xbSectLock	lck(&m_sect);
		m_prExtSock.Empty();
		m_bSocketConnected = false;
		if (m_prSock.IsValid() && pReason && m_closeReason.IsEmpty())
			m_closeReason = pReason;
	}

	xbDbgOutput("   > xbSocket::doClose()...");
	bool bClose = m_prSock.IsValid();
	if (bClose)
		m_prSock->askClose(pReason);
	m_prSock.Empty();

	m_buf.Clear();

	OnAfterDoClose();
	xbDbgOutput("  -- xbSocket::doClose() / socket closed !");
	OnStateChange(!bToReuse && bClose?xbSocketClose:xbSocketCloseSig,closectxerr);

	{
		xbSectLock	lck(&m_sect);
		m_arrBuf.Clear();
	}

	if (!bToReuse) 
		unsetBusy();
}

void xbSocket::doShutDown(LPCSTR pReason)
{
	{
		xbSectLock	lck(&m_sect);
		m_bSocketShutDown = true;
	}

	xbDbgOutput("   > xbSocket::doShutDown()...");
	if (m_prSock.IsValid())
		m_prSock->askShutDown(pReason);
}

DWORD xbSocket::onThreadProc(sIxbThread *pTh)
{
	xbDbgOutput("!! xbSocket::threadProc() started...");

	bool		bStop = false;
	while(!bStop)
	{
		xbSocketCtx ctx = xbSocketUnknown;
		int			ctxerr = 0;

		try
		{
			int		cnt = 0,dbgti=0;
			for(;;)
			{
				if (IsCloseAskedInt())
				{
					ctx = xbSocketClose;
					xbDbgOutput("  == xbSocket::threadProc() / destroy socket asked by xbThread::askClose()...");
					doClose("xbSocket thread close asked", false,0);
					bStop = true;
					break;
				}
				if (!OnWaitTimeOut(cnt))
				{
					ctx = xbSocketClose;
					xbDbgOutput("  == xbSocket::threadProc() / destroy socket asked by OnWaitTimeOut()...");
					if (!m_bSocketShutDown)
					{
						doShutDown("xbSocket connection timeout");
						cnt = 0;
					}
					else
					{
						doClose("xbSocket connection timeout",false,0);
						bStop = true;
						break;
					}
				}

				int ret;
				HANDLE hEv = m_eventPtr->GetHandle();
				if (m_prSock.IsEmpty())
				{
					if ((ret = ::xbWait(dbgti=GetWaitTimeOut(),&hEv,1)) > 0)
						break;
				}
				else
				{
					if ((ret = m_prSock->EventWait(dbgti=GetWaitTimeOut(),&hEv,1)) > 0)
						break;
				}
				if (ret == -1)
					cnt++;
			}

			if (bStop) break;

			if (m_prSock.IsValid() && m_prSock->GetEvents())
			{
				bool ok = false,bRead = false;
				bool bConnEv = m_prSock->HasConnectEvent();
				if (m_prSock->HasReadEvent())
				{
					ctx = xbSocketRead;
					ctxerr = m_prSock->GetReadError();
#ifdef dbgREAD
					xbDbgOutput("  -- xbSocket::threadProc() / data received  (err="+xbString(ctxerr)+")");
#endif
					if (ctxerr == 0)
					{
						doRead();
						bRead=true;
					}
					xbSectLock lck(&m_sect);
					if (m_lNotif == 0 || ctxerr != 0)
					{
						if (ctxerr == 0 && !m_bConnectNotifSend && !bConnEv)
							bConnEv = true;
						lck.Unlock();
						OnStateChange(ctx,ctxerr);
					}
					ok = true;
				}
				if (m_prSock->HasWriteEvent())
				{
					ctx = xbSocketWrite;
					ctxerr = m_prSock->GetWriteError();
					if (ctxerr == 0 && !bRead)
					{
						doRead(); //Cas du ssl ou de epgate: le socket peut avoir à écrire pour lire et inversement (sous protocole) 
						bRead = true;
					}
					xbDbgOutput("  -- xbSocket::threadProc() / socket ready to write (err="+xbString(ctxerr)+")");
//					if (ctxerr != 0)
						OnStateChange(ctx,ctxerr);
					ok = true;
				}
				if (m_prSock->HasAcceptEvent())
				{
					ctx = xbSocketAccept;
					ctxerr = m_prSock->GetAcceptError();
					xbDbgOutput("  -- xbSocket::threadProc() / connection requested (err="+xbString(ctxerr)+")");

					xbSectLock lck(&m_sect);
					if (m_lNotif == 0 || ctxerr != 0)
					{
						if (!m_bConnectNotifSend && !bConnEv)
							bConnEv = true;
						lck.Unlock();
						OnStateChange(ctx,ctxerr);
					}
					ok = true;
				}
				if (bConnEv)
				{
					ctx = xbSocketConnect;
					ctxerr = m_prSock->GetConnectError();
					if (ctxerr == 0) 
					{
						xbSectLock	lck(&m_sect);
						m_bSocketConnected = true;
						m_bSocketShutDown = false;
					}
					xbDbgOutput("  -- xbSocket::threadProc() / socket connected  (err="+xbString(ctxerr)+")");
					OnStateChange(ctx,ctxerr);

					m_bConnectNotifSend = true;
					
					ok = true;
				}
				if (m_prSock->HasCloseEvent())
				{
					ctx = xbSocketClose;
					ctxerr = m_prSock->GetCloseError();
					if (ctxerr == 0 && !bRead)
					{
						eProtectRdbgTrace(doRead();); //Lire les données de fin de connec
						bRead = true;
					}
					{
						xbSectLock	lck2(&m_sect);
						m_bSocketConnected = false;
					}
					xbDbgOutput("  -- xbSocket::threadProc() / socket closed  (err="+xbString(ctxerr)+")");
					doClose("xbSocket close event",false,ctxerr);
//					OnStateChange(ctx,ctxerr);
					ok = true;
					bStop = true;
				}
				if (!ok)
					xbRdbgOutput("  !- xbSocket::threadProc() / unknown event !");
			}

			ctx = xbSocketUnknown;
			OpAsked op;
			{
				xbSectLock lck(&m_sect);
				if (m_opAsked == NothingAsked) 
					continue;

				op = m_opAsked;
				m_opAsked = NothingAsked;
				m_opInExec = op;
			}

			OnDoOp(op,ctx);

			m_opInExec = NothingAsked;
		}
		catch(xbEbase *e)
		{
			m_LastErrorMsg = xbGetMsg("raw",e);
			m_LastErrorCtx = xbGetCtx("raw",e);
			eProtectTrace(OnStateChange(ctx,xbGetHR(e)););
			xbRdbgOutput("  !! xbSocket::threadProc() : exception : "+m_LastErrorMsg);
			m_opInExec = NothingAsked;
			e->Delete();
		}
		catch(...)
		{
			m_LastErrorMsg = "Unknown type exception !";
			m_LastErrorCtx.Empty();
			eProtectTrace(OnStateChange(ctx,E_POINTER););
			xbRdbgOutput("  !! xbSocket::threadProc() : unknown type exception");
			m_opInExec = NothingAsked;
		}
	}
	
	xbDbgOutput("!! xbSocket::threadProc() ended...");
	return 0;
}

void xbSocket::OnDoOp(OpAsked op, xbSocketCtx &ctx)
{
	switch(op)
	{
	case ConnectAsked:
		ctx = xbSocketConnect;
		xbDbgOutput("  == xbSocket::OnDoOp() / connection asked...");
		doConnect();
		return;
	case ListenAsked:
		ctx = xbSocketAccept;
		xbDbgOutput("  == xbSocket::OnDoOp() / listen asked...");
		doListen();
		return;
	case AcceptAsked:
		ctx = xbSocketAccept;
		xbDbgOutput("  == xbSocket::OnDoOp() / accept asked...");
		doAccept();
		return;
	case CloseAsked:
		ctx = xbSocketClose;
		xbDbgOutput("  == xbSocket::OnDoOp() / close connection asked...");
		doClose("xbSocket::OnDoOp close asked",false,0);
		return;
/*	case DestroyAsked:
		ctx = xbSocketClose;
		xbDbgOutput("  == xbSocket::OnDoOp() / destroy socket asked...");
		doClose("xbSocket::OnDoOp destroy asked",false,0);
		return false;*/
	case ShutDownAsked:
		ctx = xbSocketClose;
		xbDbgOutput("  == xbSocket::OnDoOp() / shutdown connection asked...");
		doShutDown("xbSocket::OnDoOp shutdown asked");
		return;
	};

	xbThrow xbEsocket("xbSocket::OnDoOp() : unknown op("+xbString(int(op))+") asked");
}

void xbSocket::doRead()
{
	int bufsz = 8192; //m_prSock->GetReceivedDataSize();
	m_buf.SetLength(bufsz);

	xbData dta;
	for(;;)
	{
		int ret = m_prSock->ReadData((void*)((const void *)m_buf),(int)bufsz,0);
		if (ret <= 0) break;
		dta.Add(m_buf,ret);
//		if (ret < (int)bufsz) break; //OPTIMTODO: à enlever ?
	}
	if (dta.IsEmpty()) 
	{
		xbDbgOutput("   -> xbSocket::doRead() : nothing received !");
		return;
	}
#ifdef dbgREAD
	xbDbgOutput("   -> xbSocket::doRead() : "+xbString(dta.GetLength())+" bytes received.");
	if (m_bTraceData) xbDataTrace("RECEIVED",dta);
#endif
	OnDataRead(dta);
}

void xbSocket::OnDataRead(xbData &dta)
{
	xbSectLock	lck(&m_sect);
	int no = m_arrBuf.IncLength();
	m_arrBuf.unsafeGetAt(no).Attach(&dta);
}

void	xbSocket::onCloseAsked(sIxbThread *pTh,LPCSTR pReason)
{
	setCloseReason(pReason);
	m_eventPtr.StopProcess();

	xbRefPtr<IxbSocket>		prSock;
	xbRefPtr<IxbSocketPlug>	prSockPlug;
	{
		xbSectLock	lck(&m_sect);
		prSock = m_prExtSock;
		m_prExtSock.Empty();
		prSockPlug = m_prSocketPlug;
		m_prSocketPlug.Empty();
		if (pReason && m_closeReason.IsEmpty())
			m_closeReason=pReason;
	}

	if (prSock.IsValid())
		prSock->askClose(pReason);
	if (prSockPlug.IsValid())
		prSockPlug->askClose(pReason);
}

#pragma warning(pop)
