///////////////////////////////////////////////////////////////////////////////
/// @file deConnectionBin.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 "deConnectionBin.hpp"
#include "deConnection.hpp"
#include "deThreadPool.hpp"
#include "deThread.hpp"

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

// static declarations
//-------------------------------------------
DWORD deConnectionBin::m_iNumMaxConnections = 256;

DWORD BinRun( void * pInfo )
{
	// get parent bin
	deConnectionBin * bin = *(reinterpret_cast< deConnectionBin** >( pInfo ));
	deASSERT( bin );
	
	DWORD dwRtn = 0;

	while( bin->IsRunning() )
	{
		bin->OnWorkerThread( bin );
	}

	return 0;
}

deConnectionBin::deConnectionBin(void)
{
	m_ServiceThread			= NULL;
	m_NewConnectionEvent	= CreateEvent( NULL, FALSE, FALSE, NULL );
	m_bIsRunning			= false;
	m_iNumConnections		= 0;
}

deConnectionBin::~deConnectionBin( void )
{
	if( m_ServiceThread )
	{
		m_ServiceThread->Stop( true );
		m_ServiceThread = NULL;
	}

	CloseHandle( m_NewConnectionEvent );
	m_aConnections.clear();
	m_aConnectionsActivated.clear();
}

_inline deSynchObject * deConnectionBin::GetSynchObject(void)
{
	return &m_SynchObject;
}

inline DWORD deConnectionBin::GetNumConnections( void )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	return m_iNumConnections;
}

inline bool deConnectionBin::IsConnectionActivated( DWORD pIndex )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	bool bResult = false;
		
	deASSERT( pIndex < m_aConnectionsActivated.size() );
	return m_aConnectionsActivated[ pIndex ];
}

inline void deConnectionBin::SetConnectionActivated( DWORD pIndex, bool pValue )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );
	
	deASSERT( pIndex < m_aConnectionsActivated.size() );
	m_aConnectionsActivated[ pIndex ] = pValue;
}

inline deConnection * deConnectionBin::GetConnectionAt( DWORD pIndex )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );
		
	deASSERT( pIndex < m_aConnections.size() );
	return m_aConnections[ pIndex ];
}

inline HANDLE deConnectionBin::GetNewConnectionEvent( void )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	return m_NewConnectionEvent;
}

inline DWORD deConnectionBin::GetNumMaxConnections( void )
{
	/*
	m_SynchObject.Lock( NULL );
	DWORD tmp = m_iNumMaxConnections;
	m_SynchObject.Unlock( );
	return tmp;
	*/
	return m_iNumMaxConnections;
}

inline bool deConnectionBin::IsEmpty( void )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	return m_iNumConnections <= 0;
}

inline bool deConnectionBin::IsRunning( void )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	return m_bIsRunning;
}

void deConnectionBin::SetMaxConnections( DWORD pNum )
{
	/*
	m_SynchObject.Lock( this );

	deASSERT( pNum > 0 );
	m_iNumMaxConnections	= pNum;

	if( pNum < m_iNumConnections )
	{
		NET_LOG( "** Max Connections per bin set to value lower than current bin size **\n" );
	}

	m_SynchObject.Unlock();
	*/

	m_iNumMaxConnections = pNum;
}

inline void deConnectionBin::Stop( void )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	if( m_ServiceThread )
	{
		m_ServiceThread->Sleep();
		m_ServiceThread = NULL;
	}

	m_bIsRunning			= false;
	m_iNumConnections		= 0;
	m_aConnections.clear();
}

eNetError deConnectionBin::Run( void )
{
	deConnectionBin * bin = this;
	m_ServiceThread = deThreadPool::GetMgr()->ActivateIdleThread(	BinRun, 
																	&bin, sizeof( deConnectionBin* ),
																	NULL, true );

	if( m_ServiceThread == NULL )
	{
		NET_LOG( "** Unable to create thread! **\n" );
		return CODE_RESOURCE_ERROR;
	}
	else
	{
		// ensure this is thread-safe and locked
		ENSURE_LOCK( this, this->GetSynchObject() );

		m_bIsRunning = true;

		return CODE_OK;
	}
}

eNetError deConnectionBin::AddConnection( deConnection * pConn )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	deASSERT( pConn );
	eNetError r = CODE_OK;

	// are we running?
	if( !m_bIsRunning )
	{
		r = Run();
	}

	if( r == CODE_OK )
	{
		// set event
		SetEvent( m_NewConnectionEvent );

		// add the queue
		pConn->SetConnectionBin( this );
		m_aConnections.push_back( pConn );
		m_aConnectionsActivated.push_back( false );
		m_iNumConnections++;
	}
	
	return CODE_OK;
}

eNetError deConnectionBin::RemoveConnection( deConnection * pConn )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	deASSERT( pConn );
	
	aConnections::iterator itr = m_aConnections.begin();
	aBools::iterator boolitr = m_aConnectionsActivated.begin();
	while( itr != NULL && itr != m_aConnections.end() )
	{
		if( (*itr) == pConn )
		{
			// remove it
			m_aConnections.erase( itr );
			m_aConnectionsActivated.erase( boolitr );
			break;
		}
		++itr;
		++boolitr;
	}

	m_iNumConnections--;
	
	deASSERT( m_iNumConnections == m_aConnections.size() );

	return CODE_OK;
}