///////////////////////////////////////////////////////////////////////////////
/// @file deTCPConnectionBin.cpp
///
/// @brief 
///
/// @author trajar
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Jun 2002
/// @author trajar
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deTCPConnectionBin.hpp"
#include "deTCPConnection.hpp"
#include "deNetUser.hpp"
#include "deNetMsg.hpp"

#ifdef	_DEBUG
 #include	<crtdbg.h> 
 #define new new(_NORMAL_BLOCK,__FILE__, __LINE__)
#endif

//-----------------------------------------------------------------
// found in deTCPConnection.cpp
//-----------------------------------------------------------------
int PostWSARecv( int pPosts, deTCPConnection * pConn, deTCPConnectionBin * pBin );

//-----------------------------------------------------------------

inline deTCPConnectionBin::deTCPConnectionBin(void)
{ }

inline deTCPConnectionBin::~deTCPConnectionBin(void)
{ 
	// destroy stack
	while( !m_stkBuffers.empty() )
	{
		// get top
		WSABUF buff = m_stkBuffers.top();

		// deallocate mem
		SAFE_DELETE_ARRAY( buff.buf );

		// pop the stack
		m_stkBuffers.pop();
	}

	// release pending
	aBuffers::iterator itr = m_aPendingBuffers.begin();
	while( itr != NULL && itr != m_aPendingBuffers.end() )
	{
		// get top
		WSABUF buff = *itr;

		// deallocate mem
		SAFE_DELETE_ARRAY( buff.buf );

		++itr;
	}
}

eNetError deTCPConnectionBin::OnWorkerThread( deConnectionBin * pBin )
{
	DWORD dwRtn = WaitForSingleObjectEx( GetNewConnectionEvent(), 5000, true );

	if( dwRtn != WAIT_TIMEOUT )
	{
		// we have a new connection(s)
		deTCPConnection *	tcpConn	= NULL;
		deConnection *		conn	= NULL;

		DWORD iNum = GetNumConnections();

		for( int i=0; i<iNum; i++ )
		{
			if( !IsConnectionActivated( i ) )
			{
				conn	= this->GetConnectionAt( i );
				deASSERT( conn );
				tcpConn = static_cast<deTCPConnection*>( conn );
				deASSERT( tcpConn );

				// post two recieve commands to start with
				int iRet = PostWSARecv( 1, tcpConn, this );

				if( iRet )
				{
					NET_LOG_ERROR( CODE_WINSOCK_ERROR );
					this->SetConnectionActivated( i, false );
				}
				else
				{
					// it is now activated
					this->SetConnectionActivated( i, true );
				}
			}			


			iNum = GetNumConnections();
		}
	}

	return CODE_OK;
}

_inline void deTCPConnectionBin::AddBuffer( WSABUF pBuff )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	deASSERT( pBuff.buf );

	// is this a pending operation?
	aBuffers::iterator itr = m_aPendingBuffers.begin();
	while( itr != NULL && itr != m_aPendingBuffers.end() )
	{
		// if they have the same ptrs, they are the same
		if( itr->buf == pBuff.buf )
		{
			m_aPendingBuffers.erase( itr );
			break;
		}

		++itr;
	}

	if( pBuff.len < deConnection::GetMaxPacketSize() )
	{
		// this is too small
		SAFE_DELETE_ARRAY( pBuff.buf );
		pBuff.len = 0;

		WSABUF newBuff;
		newBuff.len	= deConnection::GetMaxPacketSize();
		newBuff.buf	= new char[newBuff.len];
	}
	else	
		m_stkBuffers.push( pBuff );
}

_inline int deTCPConnectionBin::GetNumFreeBuffers( void )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	return m_stkBuffers.size();
}

_inline WSABUF deTCPConnectionBin::GetFreeBuffer( void )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	WSABUF buff;

	if( !m_stkBuffers.empty() )
	{
		buff = m_stkBuffers.top();
		m_stkBuffers.pop();
	}
	else
	{
		buff.len	= deConnection::GetMaxPacketSize();
		buff.buf	= new char[buff.len];
	}

	// add to pending buffer list
	m_aPendingBuffers.push_back( buff );

	return buff;
}