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

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

//----------------------------------------------------------
// static declarations
//----------------------------------------------------------
DWORD deConnectionProvider::m_iNumTotalConnections		= 0;
WORD  deConnectionProvider::m_iNumTotalConnectionBins	= 0;

deConnectionProvider::deConnectionProvider(void)
: m_aConnections(NULL), m_aConnectionBins(NULL)
{
	m_iNumConnectionBins	= 0;
	m_iNumConnections		= 0;
	m_bReady				= false;
}

deConnectionProvider::~deConnectionProvider(void)
{
	if( m_bReady )
		Shutdown();
}

eNetError deConnectionProvider::Init( DWORD dwFlags )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	eNetError r = this->OnInit( dwFlags );

	if( r )
	{
		// error!
		m_bReady = false;
	}
	else
	{
		m_bReady = true;
	}
	
	return r;
}

eNetError deConnectionProvider::Shutdown( DWORD dwFlags )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	if( m_aConnections )
	{
		// delete all connection objects
		for( int i=0; i<m_iNumConnections; i++ )
		{
			deConnection * conn = this->GetConnectionAt( i );
			if( conn->IsReady() )
			{
				conn->Disconnect( dwFlags );
			}
		}
	}

	eNetError r = this->OnShutdown( dwFlags );

	if( r == CODE_OK )
	{
		m_bReady				= false;	
		m_iNumConnections		= 0;
		m_iNumConnectionBins	= 0;
	}

	return r;
}

_inline WORD deConnectionProvider::GetNumProvidedConnectionBins(void)
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	return m_iNumConnectionBins;
}

_inline WORD deConnectionProvider::GetNumTotalConnectionBins(void)
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	return m_iNumTotalConnectionBins;
}

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

	deASSERT( pConn );

	deConnectionBin * bin = this->GetAvailableBin();
	deASSERT( bin );

	return bin->AddConnection( pConn );
}

eNetError deConnectionProvider::CreateConnection( deConnection ** pOut, void * pProviderInfo, DWORD dwFlags )
{
	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	eNetError r = CODE_OK;
	*pOut		= NULL;

	if( m_iNumTotalConnections >= deNet_GetMaxConnections() )
	{
		return CODE_TOO_MANY_CONNECTIONS;
	}

	// find an empty connection
	*pOut = this->GetUnusedConnection();
	
	if( *pOut )
		r = this->OnCreateConnection( pOut, pProviderInfo, dwFlags );
	else
		r = CODE_UNABLE_TO_CREATE_CONNECTION;

	if( r )
	{
		// error!
		*pOut = NULL;
	}
	else
	{
		// if we provided a structure from the provider (i.e. a socket for TCP)
		// then the connection is already ok
		if( pProviderInfo )
			(*pOut)->m_bIsReady = true;

		// now add it to the list
		(*pOut)->m_dwDesc	= dwFlags;		
		m_iNumConnections++;
		m_iNumTotalConnections++;
	}

	return r;
}

eNetError deConnectionProvider::CloseConnection( deConnection ** pCon, DWORD dwFlags )
{
	if( !(*pCon) )
		return CODE_INVALID_PARAM;

	// ensure this is thread-safe and locked
	ENSURE_LOCK( this, this->GetSynchObject() );

	eNetError r;

	r = CODE_INVALID_PARAM;

	for( int i=0; i<m_iNumConnections; i++ )
	{
		deConnection * conn = this->GetConnectionAt( i );

		if( conn == *pCon )
		{
			eNetError r = this->OnCloseConnection( pCon, dwFlags );
			
			if( r )
			{
				// error!				
			}
			else
			{
				// ok!
				*pCon = NULL;
				m_iNumConnections--;
				m_iNumTotalConnections--;
			}
		}
	}

	return r;
}