///////////////////////////////////////////////////////////////////////////////
/// @file deServer.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 "deServer.hpp"
#include "deNetMsg.hpp"
#include <SEARCH.H>

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

deServer::deServer(void)
{
	m_iNumClients	= 0;
	
	if( deNet_GetMaxConnections() <= 0 )
	{
		m_aClients = NULL;
	}
	else
	{
		m_aClients		= new pdeConnection[ deNet_GetMaxConnections() ];
		deASSERT( m_aClients );
		memset( m_aClients, NULL, sizeof(deConnection*) * deNet_GetMaxConnections() );
	}
}

deServer::~deServer(void)
{
	if( m_Status < STATUS_DESTROYED )
	{
		this->Destroy( NULL );
	}

	SAFE_DELETE_ARRAY( m_aClients );
}

eNetError deServer::OnInit( int pType, DWORD pFlags )
{
	eNetError r = CODE_OK;

	// ensure we have our array
	if( m_aClients == NULL )
	{
		deASSERT( deNet_GetMaxConnections() > 0 );
		m_aClients		= new pdeConnection[ deNet_GetMaxConnections() ];
		deASSERT( m_aClients );
		memset( m_aClients, NULL, sizeof(deConnection*) * deNet_GetMaxConnections() );
	}

	// bind the connection
	r = m_Connection->BindAsServer( NULL );

	return r;
}

eNetError deServer::OnDestroy( DWORD pFlags )
{
	deConnection ** conns = m_aClients;
	for( int i=0; i<m_iNumClients; i++ )
	{
		if( (*conns) && (*conns)->IsReady() )
			(*conns)->Disconnect( pFlags );

		(*conns) = NULL;
		conns++;
	}

	m_iNumClients = 0;

	return CODE_OK;
}

deConnection * deServer::GetConnectionByUser( hNetUser pUser )
{
	deConnection * conn = deNetUser::GetConnectionByUser( pUser );

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

	if( !conn )
	{
		deConnection ** conns = m_aClients;
		for( int i=0; i<m_iNumClients; i++ )
		{
			if( (*conns) && (*conns)->GetConnDesc()->m_hRemoteUser == pUser )
			{
				conn = *conns;
				break;
			}
			conns++;
		}		
	}

	return conn;
}

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

	deConnectionDesc * desc = NULL;

	if( m_iNumClients >= deNet_GetMaxConnections() )
		return CODE_TOO_MANY_CONNECTIONS;

	if( pConn->IsReady() &&
		pConn->GetType() == m_Connection->GetType() )
	{
		pConn->GetSynchObject()->Lock( this );

		// add connection to client list
		deConnection ** conns = m_aClients;
		for( int i=0; i<m_iNumClients; i++ )
		{
			if( (*conns) == NULL || !(*conns)->IsReady() )
			{
				*conns	= pConn;
				m_iNumClients++;
				break;
			}

			conns++;
		}

		// did we insert it?
		if( *conns != pConn )
		{
			// add it to the end
			m_aClients[ m_iNumClients ] = pConn;
			m_iNumClients++;
		}

			// sort array - desc so nulls are at the end
			if( m_iNumClients >= deNet_GetMaxConnections() )
				qsort( m_aClients, m_iNumClients, sizeof(deConnection*), deConnection::CompareConnections );
			else
				qsort( m_aClients, m_iNumClients + 1, sizeof(deConnection*), deConnection::CompareConnections );

		// set owner
		pConn->SetOwner( this );

		// unlock
		pConn->GetSynchObject()->Unlock();

		return CODE_OK;
	}
	else
		return CODE_INVALID_PARAM;
}

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

	deConnection ** conns = m_aClients;
	for( int i=0; i<m_iNumClients; i++ )
	{
		if( (*conns) == pConn )
		{
			if( pConn->IsReady() )
				pConn->Disconnect( NULL );

			*conns	= NULL;
			m_iNumClients--;

			// sort array - desc so nulls are at the end
			if( m_iNumClients >= deNet_GetMaxConnections() )
				qsort( m_aClients, m_iNumClients, sizeof(deConnection*), deConnection::CompareConnections );
			else
				qsort( m_aClients, m_iNumClients + 1, sizeof(deConnection*), deConnection::CompareConnections );

			return CODE_OK;
		}

		conns++;
	}

	return CODE_INVALID_PARAM;
}

eNetError deServer::OnCloseConnection( DWORD dwFlags )
{
	return CODE_OK;
}

eNetError deServer::OnConnectionLost( deConnectionLost * pLost )
{
	deASSERT( pLost );

	eNetError r = this->OnRemoveConnnection( pLost->pConn );

	if( r != CODE_OK )
		return r;

	// is this the main connection?
	if( pLost->pConn == m_Connection )
	{
		// shut everything down
		return Destroy( NULL );
	}
	else
	{
		// remove the stale/invalid connection
		return this->RemoveClient( pLost->pConn );
	}
}

eNetError deServer::OnConnectionRequest( deConnectionRequest * pRequest )
{
	deASSERT( pRequest );

	deConnectionReply	reply;
	deConnection *		conn = this->GetConnectionByUser( pRequest->hUserID );

	eNetError r = this->OnAcceptConnection( pRequest, conn );

	// fill out reply
	if( r == CODE_OK )
	{
		deConnectionDesc *	desc = const_cast<deConnectionDesc*>( conn->GetConnDesc() );

		reply.bAccepted				= true;
		reply.dwReason				= 0;
		reply.hAssignedID			= pRequest->hUserID;
		reply.hServerID				= this->GetNetHandle();
		reply.iServerUserType		= this->GetNetType();
		reply.iServerProviderType	= m_Connection->GetType();
		reply.ServerAddress			= *this->GetAddress();

		r = this->OnNewConnection( conn );
	}

	
	if( r != CODE_OK )
	{
		reply.bAccepted				= false;
		reply.dwReason				= deConnectionReply::REASON_INVALID_PERMISSIONS;
		reply.hAssignedID			= 0;
		reply.hServerID				= 0;
		reply.iServerProviderType	= -1;
		reply.iServerUserType		= -1;
		reply.ServerAddress			= *this->GetAddress();
	}

	// send it!
	return this->SendTo(	deNetMsg::MSG_CONNECTION_REPLY, pRequest->hUserID, 
							&reply, sizeof(deConnectionReply), deConnection::FLAG_CONFIRM_DELIVERY );


}

eNetError deServer::OnConnectionReply( deConnectionReply * pReply )
{
	deASSERT( pReply );
	return CODE_NOT_SUPPORTED_BY_OBJECT;
}

eNetError deServer::SendToAll( WORD pMsgID, void * pData, DWORD pDataSize, DWORD dwFlags )
{
	/*
	skip deNetUsre::SendToAll() because it will only return the binded connection
	eNetError r = deNetUser::SendToAll( pMsgID, pData, pDataSize, dwFlags );
	*/
	eNetError r = CODE_OK;

	// send to slaves
	if( r == CODE_OK )
	{
		deConnection ** connections = m_aClients;
		for( int i=0; i<m_iNumClients; i++ )
		{
			if( *connections )
				r = this->SendTo( pMsgID, (*connections)->GetConnDesc()->m_hRemoteUser, pData, pDataSize, dwFlags );

			if( r != CODE_OK )
				return r;

			connections++;
		}
	}

	return CODE_OK;
}