/*==========================================================================
 *
 *  Copyright (C) 2000 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       DPLConnect.cpp
 *  Content:    DirectPlay Lobby Connection Functions
 *@@BEGIN_MSINTERNAL
 *  History:
 *   Date       By      Reason
 *   ====       ==      ======
 *   02/21/00	mjn		Created
 *   05/08/00   rmt     Bug #33616 -- Does not run on Win9X 
 *   05/30/00	rmt		Bug #35700 - ConnectApp(h), Release(h), Release(h) returns OK
 *                      Added an additional release, handles were never getting destroyed
 *  06/15/00    rmt     Bug #33617 - Must provide method for providing automatic launch of DirectPlay instances   
 *  06/28/00	rmt		Prefix Bug #38082
 *  07/08/2000	rmt		Bug #38725 - Need to provide method to detect if app was lobby launched
 *				rmt		Bug #38757 - Callback messages for connections may return AFTER WaitForConnection returns
 *				rmt		Bug #38755 - No way to specify player name in Connection Settings
 *				rmt		Bug #38758 - DPLOBBY8.H has incorrect comments
 *				rmt		Bug #38783 - pvUserApplicationContext is only partially implemented
 *				rmt		Added DPLHANDLE_ALLCONNECTIONS and dwFlags (reserved field to couple of funcs).
 *  08/05/2000  RichGr  IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
 *  08/18/2000	rmt		Bug #42751 - DPLOBBY8: Prohibit more than one lobby client or lobby app per process 
 *  08/30/2000	rmt		Bug #171827 - Prefix Bug 
 *  01/04/2001	rodtoll	WinBug #94200 - Remove BUGBUGs from Code.   
 *@@END_MSINTERNAL
 *
 ***************************************************************************/

#include "dnlobbyi.h"


//**********************************************************************
// Constant definitions
//**********************************************************************

//**********************************************************************
// Macro definitions
//**********************************************************************

//**********************************************************************
// Structure definitions
//**********************************************************************

//**********************************************************************
// Variable definitions
//**********************************************************************

//**********************************************************************
// Function prototypes
//**********************************************************************

//**********************************************************************
// Function definitions
//**********************************************************************


#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionNew"

HRESULT	DPLConnectionNew(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
						 DPNHANDLE *const phConnect,
						 DPL_CONNECTION **const ppdplConnection)
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;
	DPNHANDLE		handle;

	DPFX(DPFPREP, 3,"Parameters: phConnect [0x%p], ppdplConnection [0x%p]",phConnect,ppdplConnection);

	if( ppdplConnection == NULL )
	{
		DPFERR( "ppdplConnection param is NULL -- this should not happen" );
		DNASSERT( FALSE );
		return DPNERR_GENERIC;
	}

	// Create connection entry
	if ((pdplConnection = static_cast<DPL_CONNECTION*>(DNMalloc(sizeof(DPL_CONNECTION)))) == NULL)
	{
		DPFERR("Could not allocate Connection entry");
		return(DPNERR_OUTOFMEMORY);
	}

	// Create connection handle
	if ((hResultCode = H_Create(&pdpLobbyObject->hsHandles,
			static_cast<void*>(pdplConnection),&handle)) != DPN_OK)
	{
		DPFERR("Could not create Connection handle");
		DisplayDNError(0,hResultCode);
		DNFree(pdplConnection);
		return(hResultCode);
	}

	// Create connect event
	pdplConnection->hConnectEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	if (pdplConnection->hConnectEvent == NULL)
	{
		DPFERR("Could not create connection connect event");
		H_Destroy(&pdpLobbyObject->hsHandles,handle);
		DNFree(pdplConnection);
		return(DPNERR_OUTOFMEMORY);
	}

	// Initialize entry
	pdplConnection->hConnect = handle;
	pdplConnection->dwTargetPID = 0;
	pdplConnection->pSendQueue = NULL;
	pdplConnection->lRefCount = 1;
	pdplConnection->pConnectionSettings = NULL;
	pdplConnection->pvConnectContext = NULL;

    if (DNInitializeCriticalSection( &pdplConnection->csLock ) == FALSE)
	{
		DPFERR("Could not initialize connection CS");
		CloseHandle(pdplConnection->hConnectEvent);
		H_Destroy(&pdpLobbyObject->hsHandles,handle);
		DNFree(pdplConnection);
		return(DPNERR_OUTOFMEMORY);
	}

	*phConnect = handle;
	if (ppdplConnection != NULL)
		*ppdplConnection = pdplConnection;

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}


#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionFind"

HRESULT DPLConnectionFind(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
						  const DPNHANDLE hConnect,
						  DPL_CONNECTION **const ppdplConnection,
						  const BOOL bAddRef)
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;

	DPFX(DPFPREP, 3,"Parameters: hConnect [0x%lx], ppdplConnection [0x%p], bAddRef [%ld]",
			hConnect,ppdplConnection,bAddRef);

	DNASSERT(pdpLobbyObject != NULL);
	DNASSERT(hConnect != NULL);
	DNASSERT(ppdplConnection != NULL);

	if ((hResultCode = H_Retrieve(&pdpLobbyObject->hsHandles,hConnect,
			reinterpret_cast<void**>(&pdplConnection))) != DPN_OK)
	{
		DPFERR("Could not retrieve handle");
		return(hResultCode);
	}

	if (bAddRef)
	{
		InterlockedIncrement(&pdplConnection->lRefCount);
	}

	*ppdplConnection = pdplConnection;

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}

// DPLConnectionGetConnectSettings
//
// This function gets the connection settings attached to the specified connection.
//
#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionGetConnectSettings"
HRESULT DPLConnectionGetConnectSettings( DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
						 DPNHANDLE const hConnect, 
						 DPL_CONNECTION_SETTINGS * const pdplConnectSettings,
						 DWORD * const pdwDataSize )	
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;

    hResultCode = DPLConnectionFind(pdpLobbyObject, hConnect, &pdplConnection, TRUE );

    if( FAILED( hResultCode ) )
    {
        DPFERR( "Unable to find specified connection" );
        return hResultCode;
    }

    // Grab lock to keep people from interfering.
    DNEnterCriticalSection( &pdplConnection->csLock );

    if( !pdplConnection->pConnectionSettings )
    {
    	*pdwDataSize = 0;
    	hResultCode = DPNERR_DOESNOTEXIST;
    	goto GETCONNECTIONSETTINGS_EXIT;
    }

    hResultCode = pdplConnection->pConnectionSettings->CopyToBuffer( (BYTE *) pdplConnectSettings, pdwDataSize );

GETCONNECTIONSETTINGS_EXIT:
  
    DNLeaveCriticalSection( &pdplConnection->csLock );        

    // Release this function's reference
    DPLConnectionRelease( pdpLobbyObject, hConnect );            

    return hResultCode;
    
}


// DPLConnectionSetConnectSettings
//
// This function sets the connection settings attached to the specified connection.
//
#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionSetConnectSettings"
HRESULT DPLConnectionSetConnectSettings( 
                    DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
					const DPNHANDLE hConnect, 
					CConnectionSettings * pConnectionSettings )
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;

    hResultCode = DPLConnectionFind(pdpLobbyObject, hConnect, &pdplConnection, TRUE );

    if( FAILED( hResultCode ) )
    {
        DPFERR( "Unable to find specified connection" );
        return hResultCode;
    }

    // Grab lock to prevent other people from interfering
    DNEnterCriticalSection( &pdplConnection->csLock );

	// Free old one if there is one
	if( pdplConnection->pConnectionSettings )
	{
		delete pdplConnection->pConnectionSettings;
		pdplConnection->pConnectionSettings = NULL;
	}

	pdplConnection->pConnectionSettings = pConnectionSettings;

    hResultCode = DPN_OK;

    DNLeaveCriticalSection( &pdplConnection->csLock );

    DPLConnectionRelease( pdpLobbyObject, hConnect );

    return DPN_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionGetContext"
HRESULT DPLConnectionGetContext(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
								const DPNHANDLE hConnection, 
								PVOID *ppvConnectContext )
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;

    hResultCode = DPLConnectionFind(pdpLobbyObject, hConnection, &pdplConnection, TRUE );

    if( FAILED( hResultCode ) )
    {
		*ppvConnectContext = NULL;
        DPFERR( "Unable to find specified connection" );
        return hResultCode;
    }

	// Set connection context for the found handle
	DNEnterCriticalSection( &pdplConnection->csLock );
	*ppvConnectContext = pdplConnection->pvConnectContext;
    DNLeaveCriticalSection( &pdplConnection->csLock );

	// Release our reference to the connection 
    DPLConnectionRelease( pdpLobbyObject, hConnection );

	return DPN_OK;
}


#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionSetContext"
HRESULT DPLConnectionSetContext(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
								const DPNHANDLE hConnection, 
								PVOID pvConnectContext )
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;

    hResultCode = DPLConnectionFind(pdpLobbyObject, hConnection, &pdplConnection, TRUE );

    if( FAILED( hResultCode ) )
    {
        DPFERR( "Unable to find specified connection" );
        return hResultCode;
    }

	// Set connection context for the found handle
	DNEnterCriticalSection( &pdplConnection->csLock );
	pdplConnection->pvConnectContext = pvConnectContext;
    DNLeaveCriticalSection( &pdplConnection->csLock );

	// Release our reference to the connection 
    DPLConnectionRelease( pdpLobbyObject, hConnection );

	return DPN_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionRelease"

HRESULT DPLConnectionRelease(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
							 const DPNHANDLE hConnect)
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;
	LONG			lRefCount;

	DPFX(DPFPREP, 3,"Parameters: hConnect [0x%lx]",hConnect);

	if ((hResultCode = H_Retrieve(&pdpLobbyObject->hsHandles,hConnect,
		reinterpret_cast<void**>(&pdplConnection))) != DPN_OK)
	{
		DPFERR("Could not retrieve connection");
		DisplayDNError(0,hResultCode);
    	return(hResultCode);
	}

	if (InterlockedDecrement(&pdplConnection->lRefCount) == 0)
	{
		H_Destroy(&pdpLobbyObject->hsHandles,hConnect);
		
		DPFX(DPFPREP, 5,"Freeing object");
		if (pdplConnection->pSendQueue)
		{
			pdplConnection->pSendQueue->Close();
			delete pdplConnection->pSendQueue;
			pdplConnection->pSendQueue = NULL;

            delete pdplConnection->pConnectionSettings;
			pdplConnection->pConnectionSettings = NULL;
			DNDeleteCriticalSection( &pdplConnection->csLock );
		}

		CloseHandle(pdplConnection->hConnectEvent);

    	DNFree(pdplConnection);
	}
	
	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}


#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionConnect"

HRESULT DPLConnectionConnect(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
							 const DPNHANDLE hConnect,
							 const DWORD dwProcessId, 
							 const BOOL fApplication )
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;

	DPFX(DPFPREP, 3,"Parameters: hConnect [0x%lx], dwProcessId [0x%lx]",
			hConnect,dwProcessId);

	DNASSERT(pdpLobbyObject != NULL);
	DNASSERT(hConnect != NULL);
	DNASSERT(dwProcessId != 0);

	if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hConnect,&pdplConnection,TRUE)) != DPN_OK)
	{
		DPFERR("Could not find connection");
		DisplayDNError(0,hResultCode);
		return(hResultCode);
	}

	pdplConnection->pSendQueue = new CMessageQueue;

	if( !pdplConnection->pSendQueue )
	{
		DPFERR("Could not allocate queue out of memory");
		DPLConnectionRelease(pdpLobbyObject,hConnect);
		hResultCode = DPNERR_OUTOFMEMORY;
		return(hResultCode);
	}

	hResultCode = pdplConnection->pSendQueue->Open(dwProcessId,
												   (fApplication) ? DPL_MSGQ_OBJECT_SUFFIX_APPLICATION : DPL_MSGQ_OBJECT_SUFFIX_CLIENT,
												   DPL_MSGQ_SIZE,
												   0, INFINITE);
	if (hResultCode != DPN_OK)
	{
		DPFERR("Could not open message queue");
		DisplayDNError(0,hResultCode);
		delete pdplConnection->pSendQueue;
		pdplConnection->pSendQueue = NULL;
		DPLConnectionRelease(pdpLobbyObject,hConnect);
		return(hResultCode);
	}

	// Ensure other side is still connected to MsgQ
	if (!pdplConnection->pSendQueue->IsReceiving())
	{
		DPFERR("Application is not receiving");
		pdplConnection->pSendQueue->Close();
		delete pdplConnection->pSendQueue;
		pdplConnection->pSendQueue = NULL;
		DPLConnectionRelease(pdpLobbyObject,hConnect);
		return(DPNERR_DOESNOTEXIST);
	}

	DPLConnectionRelease(pdpLobbyObject,hConnect);

	return(hResultCode);
}


#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionDisconnect"

HRESULT DPLConnectionDisconnect(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
								const DPNHANDLE hConnect )
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;
	DPL_INTERNAL_MESSAGE_DISCONNECT	Msg;

	DPFX(DPFPREP, 3,"Parameters: hConnect [0x%lx]",hConnect);

	DNASSERT(pdpLobbyObject != NULL);
	DNASSERT(hConnect != NULL);

	if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hConnect,&pdplConnection,TRUE)) != DPN_OK)
	{
		DPFERR("Could not find connection");
		DisplayDNError(0,hResultCode);
		return(hResultCode);
	}

	Msg.dwMsgId = DPL_MSGID_INTERNAL_DISCONNECT;
	Msg.dwPID = pdpLobbyObject->dwPID;

	hResultCode = pdplConnection->pSendQueue->Send(reinterpret_cast<BYTE*>(&Msg),
			sizeof(DPL_INTERNAL_MESSAGE_DISCONNECT),INFINITE,DPL_MSGQ_MSGFLAGS_USER1,0);

	// Release the reference for the Find above
	DPLConnectionRelease(pdpLobbyObject,hConnect);

	// Release the interface's reference
	DPLConnectionRelease(pdpLobbyObject,hConnect);

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}


//	DPLConnectionEnum
//
//	Enumerate outstanding connections

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionEnum"

HRESULT DPLConnectionEnum(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
						  DPNHANDLE *const prghConnect,
						  DWORD *const pdwNum)
{
	HRESULT		hResultCode;
	HRESULT		hr;

	DPFX(DPFPREP, 3,"Parameters: prghConnect [0x%p], pdwNum [0x%p]",prghConnect,pdwNum);

	hr = H_Enum(&pdpLobbyObject->hsHandles,pdwNum,prghConnect);
	if (hr == S_OK)
	{
		hResultCode = DPN_OK;
	}
	else if (hr == E_POINTER)
	{
		hResultCode = DPNERR_BUFFERTOOSMALL;
	}
	else
	{
		hResultCode = DPNERR_GENERIC;
	}

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}


//	DPLConnectionSendREQ
//
//	Send a request to connect to another process.
//	We will provide the handle of the current Connection to the other side
//		to send back as the SenderContext with messages to the local process
//		so that we can easily lookup info.
//	We will also provide the local PID so the other side can connect to us

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionSendREQ"

HRESULT DPLConnectionSendREQ(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
							 const DPNHANDLE hConnect,
							 const DWORD dwPID,
							 DPL_CONNECT_INFO *const pInfo)
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;
	DPL_INTERNAL_MESSAGE_CONNECT_REQ	*pMsg = NULL;
	DWORD			dwSize;
	CPackedBuffer	PackedBuffer;
	DWORD           dwConnectSettingsSize;
	CConnectionSettings *pConnectSettings = NULL;
	PBYTE			pbTmpBuffer = NULL;

	DPFX(DPFPREP, 3,"Parameters: hConnect [0x%lx]",hConnect);

	DNASSERT(pdpLobbyObject != NULL);
	DNASSERT(hConnect != NULL);

	if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hConnect,&pdplConnection,TRUE)) != DPN_OK)
	{
		DPFERR("Could not find connection");
		DisplayDNError(0,hResultCode);
		return(hResultCode);
	}

	if (!pdplConnection->pSendQueue->IsReceiving())
	{
		DPFERR("Other side is not receiving");
		DPLConnectionRelease(pdpLobbyObject,hConnect);
		return(DPNERR_DOESNOTEXIST);
	}

	DNEnterCriticalSection( &pdplConnection->csLock );

	if( pInfo->pdplConnectionSettings )
	{
		pConnectSettings = new CConnectionSettings();

		if( !pConnectSettings )
		{
			DPFERR("Error allocating memory");
			hResultCode = DPNERR_OUTOFMEMORY;
			goto CONNECTREQ_EXIT;
		}

		hResultCode = pConnectSettings->InitializeAndCopy( pInfo->pdplConnectionSettings );

		if( FAILED( hResultCode ) )
		{
			DPFX(DPFPREP, 0, "Error copying settings hr [0x%x]", hResultCode );
			goto CONNECTREQ_EXIT;
		}
	}

	PackedBuffer.Initialize( NULL, 0 );	

	// Determine size of message to send.
	PackedBuffer.AddToFront(NULL,sizeof(DPL_INTERNAL_MESSAGE_CONNECT_REQ_HEADER));

	// Add connect settings if they exist
	if( pInfo->pdplConnectionSettings )
		pConnectSettings->BuildWireStruct(&PackedBuffer);

	// Add lobby connect data
	PackedBuffer.AddToBack(NULL,pInfo->dwLobbyConnectDataSize);

	pbTmpBuffer = new BYTE[PackedBuffer.GetSizeRequired()];

	if( !pbTmpBuffer )
	{
		DPFERR("Error allocating memory" );
		hResultCode = DPNERR_OUTOFMEMORY;
		goto CONNECTREQ_EXIT;
	}

	pMsg = (DPL_INTERNAL_MESSAGE_CONNECT_REQ *) pbTmpBuffer;

	PackedBuffer.Initialize( pMsg, PackedBuffer.GetSizeRequired() );

	hResultCode = PackedBuffer.AddToFront( pMsg, sizeof( DPL_INTERNAL_MESSAGE_CONNECT_REQ_HEADER ) );

	if( FAILED( hResultCode ) )
	{
		DPFX( DPFPREP, 0, "Internal error! hr [0x%x]", hResultCode );
		goto CONNECTREQ_EXIT;
	}

	pMsg->dwMsgId = DPL_MSGID_INTERNAL_CONNECT_REQ;
	pMsg->hSender = hConnect;
	pMsg->dwSenderPID = dwPID;	

	if( pInfo->pdplConnectionSettings )
	{
		hResultCode = pConnectSettings->BuildWireStruct(&PackedBuffer);

		if( FAILED( hResultCode ) )
		{
			DPFX( DPFPREP, 0, "Error building wire struct for settings hr [0x%x]", hResultCode );
			goto CONNECTREQ_EXIT;
		}
		
		pMsg->dwConnectionSettingsSize = 1;		
	}
	else
	{
		pMsg->dwConnectionSettingsSize = 0;
	}

	hResultCode = PackedBuffer.AddToBack(pInfo->pvLobbyConnectData, pInfo->dwLobbyConnectDataSize, FALSE);

	if( FAILED( hResultCode ) )
	{
		DPFX( DPFPREP, 0, "Error adding connect data hr [0x%x]", hResultCode );
		goto CONNECTREQ_EXIT;
	}

	pMsg->dwLobbyConnectDataOffset = PackedBuffer.GetTailOffset();
	pMsg->dwLobbyConnectDataSize = pInfo->dwLobbyConnectDataSize;

	hResultCode = DPLConnectionSetConnectSettings( pdpLobbyObject, hConnect,pConnectSettings );

	if( FAILED( hResultCode ) )
	{
	    DPFERR( "Could not set local copy of connection settings" );
	    goto CONNECTREQ_EXIT;
	}

	hResultCode = pdplConnection->pSendQueue->Send(reinterpret_cast<BYTE*>(pMsg),
												   PackedBuffer.GetSizeRequired(),
												   INFINITE,
												   DPL_MSGQ_MSGFLAGS_USER1,
												   0);
	if (hResultCode != DPN_OK)
	{
		DPFERR("Could not send connect info");
		goto CONNECTREQ_EXIT;
	}

CONNECTREQ_EXIT:	

	DNLeaveCriticalSection( &pdplConnection->csLock );	

    if( pbTmpBuffer )
    	delete [] pbTmpBuffer;

	DPLConnectionRelease(pdpLobbyObject,hConnect);

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);

	if( FAILED( hResultCode ) )
	{
		if( pConnectSettings )
			delete pConnectSettings;
	}
	
	return(hResultCode);
}


//	DPLConnectionReceiveREQ
//
//	Receive a request to connect.
//	Attempt to connect to the requesting process using the PID supplied.
//	Keep the supplied SenderContext for future sends directed at that process.
//	Send a connect acknowledge

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionReceiveREQ"

HRESULT DPLConnectionReceiveREQ(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
								BYTE *const pBuffer)
{
	HRESULT			hResultCode;
	DPNHANDLE		handle;
	DPL_CONNECTION	*pdplConnection;
	DPL_INTERNAL_MESSAGE_CONNECT_REQ	*pMsg;
	DPL_MESSAGE_CONNECT		MsgConnect;
	DPL_CONNECTION_SETTINGS *pSettingsBuffer = NULL;
	DWORD                   dwSettingsBufferSize = 0;
	BOOL			fLobbyLaunching = FALSE;
	CConnectionSettings *pConnectSettings = NULL;
	BYTE *pbTmpBuffer = NULL; 


	DPFX(DPFPREP, 3,"Parameters: pBuffer [0x%p]",pBuffer);

	DNASSERT(pdpLobbyObject != NULL);
	DNASSERT(pBuffer != NULL);

	pMsg = reinterpret_cast<DPL_INTERNAL_MESSAGE_CONNECT_REQ*>(pBuffer);

	if ((hResultCode = DPLConnectionNew(pdpLobbyObject,&handle,&pdplConnection)) != DPN_OK)
	{
		DPFERR("Could not create new connection");
		DisplayDNError(0,hResultCode);
		return(hResultCode);
	}

	if ((hResultCode = DPLConnectionConnect(pdpLobbyObject,handle,pMsg->dwSenderPID,FALSE)) != DPN_OK)
	{
		DPFERR("Could not perform requested connection");
		goto CONNECTRECVREQ_ERROR;
	}

	pdplConnection->pSendQueue->SetSenderHandle(pMsg->hSender);
	pdplConnection->dwTargetPID = pMsg->dwSenderPID;

	if ((hResultCode = DPLConnectionSendACK(pdpLobbyObject,handle)) != DPN_OK)
	{
		DPFERR("Could not send connection acknowledge");
		goto CONNECTRECVREQ_ERROR;
	}

    if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_MULTICONNECT)
    {
        DPFX(DPFPREP,  1, "Multiconnect flag specified, returning app to available status" );
        pdpLobbyObject->pReceiveQueue->MakeAvailable();
    }

    if( pMsg->dwConnectionSettingsSize )
    {
	 	pConnectSettings = new CConnectionSettings();

	 	if( !pConnectSettings )
	 	{
			DPFERR("Error allocating structure");
			hResultCode = DPNERR_OUTOFMEMORY;
			goto CONNECTRECVREQ_ERROR;
	 	}

	 	hResultCode = pConnectSettings->Initialize( &pMsg->dplConnectionSettings, (UNALIGNED BYTE *) pMsg );

	 	if( FAILED( hResultCode ) )
	 	{
	 		DPFX( DPFPREP, 0, "Error copying connection settings from wire hr=[0x%x]", hResultCode );
			goto CONNECTRECVREQ_ERROR;
	 	}
    }

    // Update the local connection settings
    hResultCode = DPLConnectionSetConnectSettings( pdpLobbyObject, handle, pConnectSettings );

 	if( FAILED( hResultCode ) )
 	{
 		DPFX( DPFPREP, 0, "Error setting connection settings from wire hr=[0x%x]", hResultCode );
		goto CONNECTRECVREQ_ERROR;
	}	

	// Indicate connection to application
	MsgConnect.dwSize = sizeof(DPL_MESSAGE_CONNECT);
	MsgConnect.hConnectId = handle;

 	if( pMsg->dwLobbyConnectDataSize )
 	{
		// Got to copy the connect data locally to an aligned buffer to ensure alignment -- ack
	 	pbTmpBuffer = new BYTE[pMsg->dwLobbyConnectDataSize];

		if( !pbTmpBuffer )
	 	{
			DPFERR("Error allocating structure");
			hResultCode = DPNERR_OUTOFMEMORY;
			goto CONNECTRECVREQ_ERROR;
	 	}

		memcpy( pbTmpBuffer, pBuffer + pMsg->dwLobbyConnectDataOffset, pMsg->dwLobbyConnectDataSize );
		MsgConnect.pvLobbyConnectData = pbTmpBuffer;
		MsgConnect.dwLobbyConnectDataSize = pMsg->dwLobbyConnectDataSize;
 	}
 	else
 	{
 		MsgConnect.pvLobbyConnectData = NULL;
 		MsgConnect.dwLobbyConnectDataSize = 0;
 	}

	MsgConnect.pvConnectionContext = NULL;

	if( pConnectSettings )
	{
		MsgConnect.pdplConnectionSettings = pConnectSettings->GetConnectionSettings();
	}
	else
	{
		MsgConnect.pdplConnectionSettings = NULL;		
	}

	// If we're lobby launching set the connect event before calling the message handler
	// otherwise we may encounter deadlock then timeout if user blocks in callback
	if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOOKINGFORLOBBYLAUNCH ) 
	{
		fLobbyLaunching = TRUE;
		pdpLobbyObject->dpnhLaunchedConnection = handle;
	}

	hResultCode = (pdpLobbyObject->pfnMessageHandler)(pdpLobbyObject->pvUserContext,
													  DPL_MSGID_CONNECT,
													  reinterpret_cast<BYTE*>(&MsgConnect));

	if( FAILED( hResultCode ) )
	{
		DPFX( DPFPREP, 0, "Error returned from user's callback -- ignoring hr [0x%x]", hResultCode );
	}

	// Set the context for this connection
	DPLConnectionSetContext( pdpLobbyObject, handle, MsgConnect.pvConnectionContext );

	if( pbTmpBuffer )
		delete [] pbTmpBuffer;

	// If we're looking for a lobby launch, set the dpnhLaunchedConnection to cache the connection handle
	SetEvent(pdpLobbyObject->hConnectEvent);

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",DPN_OK);
	return(DPN_OK);

CONNECTRECVREQ_ERROR:

	if( pbTmpBuffer )
		delete [] pbTmpBuffer;

	if( pConnectSettings )
 		delete pConnectSettings;
	
	DPLConnectionDisconnect(pdpLobbyObject,handle);
	DPLConnectionRelease(pdpLobbyObject,handle);

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);	
	
	return(hResultCode); 		
	
}

//	DPLConnectionSendACK
//
//	Send a connect acknowledge.
//	Provide the local handle for the connection to the other side for future
//		sends to the local process

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionSendACK"

HRESULT DPLConnectionSendACK(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
							 const DPNHANDLE hConnect)
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;
	DPL_INTERNAL_MESSAGE_CONNECT_ACK	Msg;

	DPFX(DPFPREP, 3,"Parameters: hConnect [0x%lx]",hConnect);

	DNASSERT(pdpLobbyObject != NULL);
	DNASSERT(hConnect != NULL);

	if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hConnect,&pdplConnection,TRUE)) != DPN_OK)
	{
		DPFERR("Could not find connection");
		DisplayDNError(0,hResultCode);
		return(hResultCode);
	}

	Msg.dwMsgId = DPL_MSGID_INTERNAL_CONNECT_ACK;
	Msg.hSender = hConnect;

	hResultCode = pdplConnection->pSendQueue->Send(reinterpret_cast<BYTE*>(&Msg),
												   sizeof(DPL_INTERNAL_MESSAGE_CONNECT_ACK),
												   INFINITE,
												   DPL_MSGQ_MSGFLAGS_USER1, 
												   0);
	if (hResultCode != DPN_OK)
	{
		DPFERR("Could not send connection acknowledge");
		DisplayDNError(0,hResultCode);
		DPLConnectionRelease(pdpLobbyObject,hConnect);
		return(hResultCode);
	}

	DPLConnectionRelease(pdpLobbyObject,hConnect);

	hResultCode = DPN_OK;

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}


//	DPLConnectionReceiveACK
//
//	Receive a connect acknowledge
//	Keep the supplied SenderContext for future sends directed at that process.

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionReceiveACK"

HRESULT DPLConnectionReceiveACK(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
								const DPNHANDLE hSender,
								BYTE *const pBuffer)
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;
	DPL_INTERNAL_MESSAGE_CONNECT_ACK	*pMsg;

	DPFX(DPFPREP, 3,"Parameters: hSender [0x%lx], pBuffer [0x%p]",hSender,pBuffer);

	DNASSERT(pdpLobbyObject != NULL);
	DNASSERT(pBuffer != NULL);

	pMsg = reinterpret_cast<DPL_INTERNAL_MESSAGE_CONNECT_ACK*>(pBuffer);

	if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hSender,&pdplConnection,TRUE)) != DPN_OK)
	{
		DPFERR("Could not find sender's connection");
		DisplayDNError(0,hResultCode);
		return(hResultCode);
	}

	pdplConnection->pSendQueue->SetSenderHandle(pMsg->hSender);

	SetEvent(pdplConnection->hConnectEvent);

	DPLConnectionRelease(pdpLobbyObject,hSender);

	// Indicate that a connection was made by setting event
	SetEvent(pdpLobbyObject->hConnectEvent);

	hResultCode = DPN_OK;

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}



//	DPLConnectionReceiveDisconnect
//
//	Receive a disconnect
//	Terminate the connection

#undef DPF_MODNAME
#define DPF_MODNAME "DPLConnectionReceiveDisconnect"

HRESULT DPLConnectionReceiveDisconnect(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
									   const DPNHANDLE hSender,
									   BYTE *const pBuffer,
									   const HRESULT hrDisconnectReason )
{
	HRESULT			hResultCode;
	DPL_CONNECTION	*pdplConnection;
	DPL_MESSAGE_DISCONNECT	MsgDisconnect;

	DPFX(DPFPREP, 3,"Parameters: hSender [0x%lx]",hSender);

	DNASSERT(pdpLobbyObject != NULL);

	if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hSender,&pdplConnection,TRUE)) != DPN_OK)
	{
		DPFERR("Could not find sender's connection");
		DisplayDNError(0,hResultCode);
		return(hResultCode);
	}

	// Indicate disconnect to user
	MsgDisconnect.dwSize = sizeof(DPL_MESSAGE_DISCONNECT);
	MsgDisconnect.hDisconnectId = hSender;
	MsgDisconnect.hrReason = hrDisconnectReason;

	// Return code is irrelevant, at this point we're going to indicate regardless
	hResultCode = DPLConnectionGetContext( pdpLobbyObject, hSender, &MsgDisconnect.pvConnectionContext );

	if( FAILED( hResultCode ) )
	{
		DPFX(DPFPREP,  0, "Error getting connection context for 0x%x hr=0x%x", hSender, hResultCode );
	}
	 
	hResultCode = (pdpLobbyObject->pfnMessageHandler)(pdpLobbyObject->pvUserContext,
													  DPL_MSGID_DISCONNECT,
													  reinterpret_cast<BYTE*>(&MsgDisconnect));

//  Fixed memory leak, DPLConnectionRelease will free the send queue
//	pdplConnection->pSendQueue->Close();
//	pdplConnection->pSendQueue = NULL;

	DPLConnectionRelease(pdpLobbyObject,hSender);

	DPLConnectionRelease(pdpLobbyObject,hSender);

	hResultCode = DPN_OK;

	DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
	return(hResultCode);
}