/***************************************************************************
 *
 *  Copyright (C) 2001 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:		spcallbackobj.cpp
 *
 *  Content:	DP8SIM callback interface object class.
 *
 *  History:
 *   Date      By        Reason
 *  ========  ========  =========
 *  04/23/01  VanceO    Created.
 *
 ***************************************************************************/



#include "dp8simi.h"





#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::CDP8SimCB"
//=============================================================================
// CDP8SimCB constructor
//-----------------------------------------------------------------------------
//
// Description: Initializes the new CDP8SimCB object.
//
// Arguments:
//	CDP8SimSP * pOwningDP8SimSP		- Pointer to owning CDP8SimSP object.
//	IDP8SPCallback * pDP8SPCB		- Pointer to real DPlay callback interface
//										being intercepted.
//
// Returns: None (the object).
//=============================================================================
CDP8SimCB::CDP8SimCB(CDP8SimSP * pOwningDP8SimSP, IDP8SPCallback * pDP8SPCB)
{
	this->m_Sig[0]	= 'S';
	this->m_Sig[1]	= 'P';
	this->m_Sig[2]	= 'C';
	this->m_Sig[3]	= 'B';

	this->m_lRefCount			= 1; // someone must have a pointer to this object

	pOwningDP8SimSP->AddRef();
	this->m_pOwningDP8SimSP		= pOwningDP8SimSP;

	pDP8SPCB->AddRef();
	this->m_pDP8SPCB			= pDP8SPCB;
} // CDP8SimCB::CDP8SimCB






#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::~CDP8SimCB"
//=============================================================================
// CDP8SimCB destructor
//-----------------------------------------------------------------------------
//
// Description: Frees the CDP8SimCB object.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
CDP8SimCB::~CDP8SimCB(void)
{
	DNASSERT(this->m_lRefCount == 0);


	this->m_pOwningDP8SimSP->Release();
	this->m_pOwningDP8SimSP = NULL;

	this->m_pDP8SPCB->Release();
	this->m_pDP8SPCB = NULL;


	//
	// For grins, change the signature before deleting the object.
	//
	this->m_Sig[3]	= 'b';
} // CDP8SimCB::~CDP8SimCB




#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::QueryInterface"
//=============================================================================
// CDP8SimCB::QueryInterface
//-----------------------------------------------------------------------------
//
// Description: Retrieves a new reference for an interfaces supported by this
//				CDP8SimCB object.
//
// Arguments:
//	REFIID riid			- Reference to interface ID GUID.
//	LPVOID * ppvObj		- Place to store pointer to object.
//
// Returns: HRESULT
//	S_OK					- Returning a valid interface pointer.
//	DPNHERR_INVALIDOBJECT	- The interface object is invalid.
//	DPNHERR_INVALIDPOINTER	- The destination pointer is invalid.
//	E_NOINTERFACE			- Invalid interface was specified.
//=============================================================================
STDMETHODIMP CDP8SimCB::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
	HRESULT		hr = DPN_OK;


	DPFX(DPFPREP, 3, "(0x%p) Parameters: (REFIID, 0x%p)", this, ppvObj);


	//
	// Validate the object.
	//
	if (! this->IsValidObject())
	{
		DPFX(DPFPREP, 0, "Invalid DP8Sim object!");
		hr = DPNERR_INVALIDOBJECT;
		goto Failure;
	}


	//
	// Validate the parameters.
	//

	if ((! IsEqualIID(riid, IID_IUnknown)) &&
		(! IsEqualIID(riid, IID_IDP8SPCallback)))
	{
		DPFX(DPFPREP, 0, "Unsupported interface!");
		hr = E_NOINTERFACE;
		goto Failure;
	}

	if ((ppvObj == NULL) ||
		(IsBadWritePtr(ppvObj, sizeof(void*))))
	{
		DPFX(DPFPREP, 0, "Invalid interface pointer specified!");
		hr = DPNERR_INVALIDPOINTER;
		goto Failure;
	}


	//
	// Add a reference, and return the interface pointer (which is actually
	// just the object pointer, they line up because CDP8SimCB inherits from
	// the interface declaration).
	//
	this->AddRef();
	(*ppvObj) = this;


Exit:

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

	return hr;


Failure:

	goto Exit;
} // CDP8SimCB::QueryInterface




#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::AddRef"
//=============================================================================
// CDP8SimCB::AddRef
//-----------------------------------------------------------------------------
//
// Description: Adds a reference to this CDP8SimCB object.
//
// Arguments: None.
//
// Returns: New refcount.
//=============================================================================
STDMETHODIMP_(ULONG) CDP8SimCB::AddRef(void)
{
	LONG	lRefCount;


	DNASSERT(this->IsValidObject());


	//
	// There must be at least 1 reference to this object, since someone is
	// calling AddRef.
	//
	DNASSERT(this->m_lRefCount > 0);

	lRefCount = InterlockedIncrement(&this->m_lRefCount);

	DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);

	return lRefCount;
} // CDP8SimCB::AddRef




#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::Release"
//=============================================================================
// CDP8SimCB::Release
//-----------------------------------------------------------------------------
//
// Description: Removes a reference to this CDP8SimCB object.  When the
//				refcount reaches 0, this object is destroyed.
//				You must NULL out your pointer to this object after calling
//				this function.
//
// Arguments: None.
//
// Returns: New refcount.
//=============================================================================
STDMETHODIMP_(ULONG) CDP8SimCB::Release(void)
{
	LONG	lRefCount;


	DNASSERT(this->IsValidObject());

	//
	// There must be at least 1 reference to this object, since someone is
	// calling Release.
	//
	DNASSERT(this->m_lRefCount > 0);

	lRefCount = InterlockedDecrement(&this->m_lRefCount);

	//
	// Was that the last reference?  If so, we're going to destroy this object.
	//
	if (lRefCount == 0)
	{
		DPFX(DPFPREP, 3, "[0x%p] RefCount hit 0, destroying object.", this);


		//
		// Uninitialize the object.
		//
		this->UninitializeObject();

		//
		// Finally delete this (!) object.
		//
		delete this;
	}
	else
	{
		DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);
	}

	return lRefCount;
} // CDP8SimCB::Release




#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::IndicateEvent"
//=============================================================================
// CDP8SimCB::IndicateEvent
//-----------------------------------------------------------------------------
//
// Description: ?
//
// Arguments:
//	SP_EVENT_TYPE EventType		- Event being indicated.
//	PVOID pvMessage				- Event specific message.
//
// Returns: HRESULT
//=============================================================================
STDMETHODIMP CDP8SimCB::IndicateEvent(SP_EVENT_TYPE EventType, PVOID pvMessage)
{
	HRESULT				hr;
	CDP8SimEndpoint *	pDP8SimEndpoint;
	CDP8SimCommand *	pDP8SimCommand;


	DPFX(DPFPREP, 2, "(0x%p) Parameters: (%u, 0x%p)", this, EventType, pvMessage);


	//
	// Validate (actually assert) the object.
	//
	DNASSERT(this->IsValidObject());


	//
	// Switch on the type of event being indicated.
	//
	switch (EventType)
	{
		case SPEV_DATA:
		{
			SPIE_DATA *			pData;
			DP8SIM_PARAMETERS	dp8sp;
			SPIE_DATA			DataModified;
			CDP8SimReceive *	pDP8SimReceive;
			DWORD				dwBandwidthDelay;
			DWORD				dwLatencyDelay;


			pData = (SPIE_DATA*) pvMessage;


			DNASSERT(pData->pReceivedData->pNext == NULL);


			pDP8SimEndpoint = (CDP8SimEndpoint*) pData->pEndpointContext;
			DNASSERT(pDP8SimEndpoint->IsValidObject());


			//
			// If the endpoint is disconnecting, drop the receive.
			//
			pDP8SimEndpoint->Lock();
			if (pDP8SimEndpoint->IsDisconnecting())
			{
				pDP8SimEndpoint->Unlock();

				DPFX(DPFPREP, 1, "Endpoint 0x%p is disconnecting, dropping receive.",
					pDP8SimEndpoint);

				hr = DPN_OK;
			}
			else
			{
				pDP8SimEndpoint->Unlock();


				//
				// Get the current receive settings.
				//
				ZeroMemory(&dp8sp, sizeof(dp8sp));
				dp8sp.dwSize = sizeof(dp8sp);
				this->m_pOwningDP8SimSP->GetAllReceiveParameters(&dp8sp);


				//
				// Determine if we need to drop this receive.
				//
				if (this->m_pOwningDP8SimSP->ShouldDrop(dp8sp.fPacketLossPercent))
				{
					DPFX(DPFPREP, 2, "Dropping %u bytes of data from endpoint 0x%p.",
						pData->pReceivedData->BufferDesc.dwBufferSize, pDP8SimEndpoint);


					//
					// Update the statistics.
					//
					this->m_pOwningDP8SimSP->IncrementStatsReceiveDropped(pData->pReceivedData->BufferDesc.dwBufferSize);


					//
					// Let the SP reclaim the buffer.
					//
					hr = DPN_OK;
				}
				else
				{
					//
					// Figure out how much latency needs to be added based on
					// the bandwidth and random latency settings.
					//
					// If we're not supposed to delay the receives, indicate it
					// now.  Otherwise submit a timed job to be performed
					// later.
					//
					if (! this->m_pOwningDP8SimSP->GetDelay(dp8sp.dwBandwidthBPS,
															dp8sp.dwPacketHeaderSize,
															pData->pReceivedData->BufferDesc.dwBufferSize,
															dp8sp.dwMinLatencyMS,
															dp8sp.dwMaxLatencyMS,
															&dwBandwidthDelay,
															&dwLatencyDelay))
					{
						//
						// Modify the message before indicating to the caller.
						//

						ZeroMemory(&DataModified, sizeof(DataModified));
						DataModified.hEndpoint			= (HANDLE) pDP8SimEndpoint;
						DataModified.pEndpointContext	= pDP8SimEndpoint->GetUserContext();
						DataModified.pReceivedData		= pData->pReceivedData;


						//
						// Indicate the event to the real callback interface.
						//

						DPFX(DPFPREP, 2, "Indicating event SPEV_DATA (message = 0x%p, data size = %u) to interface 0x%p.",
							pData, pData->pReceivedData->BufferDesc.dwBufferSize,
							this->m_pDP8SPCB);

						hr = this->m_pDP8SPCB->IndicateEvent(SPEV_DATA, &DataModified);

						DPFX(DPFPREP, 2, "Returning from event SPEV_DATA [0x%lx].", hr);


						//
						// Update the statistics.
						//
						this->m_pOwningDP8SimSP->IncrementStatsReceiveTransmitted(pData->pReceivedData->BufferDesc.dwBufferSize,
																				0);
					}
					else
					{
						DPFX(DPFPREP, 6, "Delaying %u byte receive for %u + %u ms.",
							pData->pReceivedData->BufferDesc.dwBufferSize,
							dwBandwidthDelay, dwLatencyDelay);


						//
						// Get a receive object, duplicating the received data
						// structure given to us by our caller for indication
						// some time in the future.
						//
						pDP8SimReceive = (CDP8SimReceive*)g_FPOOLReceive.Get(pData);
						if (pDP8SimReceive == NULL)
						{
							hr = DPNERR_OUTOFMEMORY;
						}
						else
						{
							DPFX(DPFPREP, 7, "New delayed receive 0x%p.", pDP8SimReceive);

							//
							// Store the latency that is about be added to this
							// receive.
							//
							pDP8SimReceive->SetLatencyAdded(dwBandwidthDelay + dwLatencyDelay);

							
							//
							// Transfer local pDP8SimReceive reference to the
							// job queue.
							//


							//
							// Increment the receive counter.
							//
							this->m_pOwningDP8SimSP->IncReceivesPending();


							//
							// Queue it to be indicated at a later time,
							// depending on the latency value requested.  If
							// there's a bandwidth restriction, enforce the
							// receiving order as well so that earlier messages
							// that are still pending hold up later ones.
							//
							hr = AddWorkerJob(DP8SIMJOBTYPE_DELAYEDRECEIVE,
											pDP8SimReceive,
											this->m_pOwningDP8SimSP,
											dwBandwidthDelay,
											dwLatencyDelay,
											DP8SIMJOBFLAG_PERFORMBLOCKINGPHASELAST);
							if (hr != DPN_OK)
							{
								DPFX(DPFPREP, 0, "Couldn't add delayed receive worker job (0x%p)!", 
									pDP8SimReceive);


								//
								// Remove the receive counter.
								//
								this->m_pOwningDP8SimSP->DecReceivesPending();


								//
								// Release the delayed receive reference.
								//
								DPFX(DPFPREP, 7, "Releasing aborted delayed receive 0x%p.", pDP8SimReceive);
								pDP8SimReceive->Release();
								pDP8SimReceive = NULL;
							}
							else
							{
								//
								// Let the real SP know that we're keeping the
								// buffer.
								//
								hr = DPNSUCCESS_PENDING;
							}
						} // end else (successfully got receive object)
					} // end else (delaying receives)
				} // end else (not dropping receive)
			} // end else (endpoint is not disconnecting yet)
			break;
		}

		case SPEV_CONNECT:
		{
			SPIE_CONNECT *	pConnect;
			SPIE_CONNECT	ConnectModified;


			pConnect = (SPIE_CONNECT*) pvMessage;


			pDP8SimCommand = (CDP8SimCommand*) pConnect->pCommandContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT((pDP8SimCommand->GetType() == CMDTYPE_CONNECT) || (pDP8SimCommand->GetType() == CMDTYPE_LISTEN));


			//
			// Get a new endpoint object from the pool.
			//
			pDP8SimEndpoint = (CDP8SimEndpoint*)g_FPOOLEndpoint.Get(pConnect->hEndpoint);
			if (pDP8SimEndpoint == NULL)
			{
				hr = DPNERR_OUTOFMEMORY;
			}
			else
			{
				DPFX(DPFPREP, 7, "New %s endpoint 0x%p.",
					((pDP8SimCommand->GetType() == CMDTYPE_CONNECT) ? _T("outbound") : _T("inbound")),
					pDP8SimEndpoint);


				//
				// Modify the message before indicating to the caller.
				//

				ZeroMemory(&ConnectModified, sizeof(ConnectModified));
				ConnectModified.hEndpoint			= (HANDLE) pDP8SimEndpoint;
				//ConnectModified.pEndpointContext	= NULL;									// the user fills this in
				ConnectModified.pCommandContext		= pDP8SimCommand->GetUserContext();


				//
				// Indicate the event to the real callback interface.
				//

				DPFX(DPFPREP, 2, "Indicating event SPEV_CONNECT (message = 0x%p) to interface 0x%p.",
					&ConnectModified, this->m_pDP8SPCB);

				hr = this->m_pDP8SPCB->IndicateEvent(SPEV_CONNECT, &ConnectModified);

				DPFX(DPFPREP, 2, "Returning from event SPEV_CONNECT [0x%lx].", hr);


				if (hr == DPN_OK)
				{
					//
					// Update the endpoint context with what the user returned.
					//
					pDP8SimEndpoint->SetUserContext(ConnectModified.pEndpointContext);

					//
					// Return our endpoint context.
					//
					pConnect->pEndpointContext = pDP8SimEndpoint;
				}
				else
				{
					//
					// Release the endpoint reference.
					//
					DPFX(DPFPREP, 7, "Releasing aborted endpoint 0x%p.", pDP8SimEndpoint);
					pDP8SimEndpoint->Release();
					pDP8SimEndpoint = NULL;
				}
			}

			break;
		}

		case SPEV_DISCONNECT:
		{
			SPIE_DISCONNECT *	pDisconnect;
			SPIE_DISCONNECT		DisconnectModified;


			pDisconnect = (SPIE_DISCONNECT*) pvMessage;


			pDP8SimEndpoint = (CDP8SimEndpoint*) pDisconnect->pEndpointContext;
			DNASSERT(pDP8SimEndpoint->IsValidObject());


			//
			// Mark the endpoint as disconnecting to prevent additional sends
			// or receives.
			//
			pDP8SimEndpoint->Lock();
			pDP8SimEndpoint->NoteDisconnecting();
			pDP8SimEndpoint->Unlock();


			//
			// Modify the message before indicating to the caller.
			//

			ZeroMemory(&DisconnectModified, sizeof(DisconnectModified));
			DisconnectModified.hEndpoint			= (HANDLE) pDP8SimEndpoint;
			DisconnectModified.pEndpointContext		= pDP8SimEndpoint->GetUserContext();

	
			//
			// Quickly indicate any delayed receives from this endpoint that
			// are still pending.
			//
			FlushAllDelayedReceivesFromEndpoint(pDP8SimEndpoint, FALSE);

			//
			// Kill off any delayed sends that would have gone to this
			// endpoint.
			//
			FlushAllDelayedSendsToEndpoint(pDP8SimEndpoint, TRUE);


			//
			// Indicate the event to the real callback interface.
			//

			DPFX(DPFPREP, 2, "Indicating event SPEV_DISCONNECT (message = 0x%p) to interface 0x%p.",
				&DisconnectModified, this->m_pDP8SPCB);

			hr = this->m_pDP8SPCB->IndicateEvent(SPEV_DISCONNECT, &DisconnectModified);

			DPFX(DPFPREP, 2, "Returning from event SPEV_DISCONNECT [0x%lx].", hr);


			//
			// Release the endpoint reference.
			//
			DPFX(DPFPREP, 7, "Releasing endpoint 0x%p.", pDP8SimEndpoint);
			pDP8SimEndpoint->Release();
			pDP8SimEndpoint = NULL;

			break;
		}


		case SPEV_LISTENSTATUS:
		{
			SPIE_LISTENSTATUS *		pListenStatus;
			SPIE_LISTENSTATUS		ListenStatusModified;


			pListenStatus = (SPIE_LISTENSTATUS*) pvMessage;


			pDP8SimCommand = (CDP8SimCommand*) pListenStatus->pUserContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_LISTEN);


			//
			// Get a new endpoint object from the pool.
			//
			pDP8SimEndpoint = (CDP8SimEndpoint*)g_FPOOLEndpoint.Get(pListenStatus->hEndpoint);
			if (pDP8SimEndpoint == NULL)
			{
				hr = DPNERR_OUTOFMEMORY;
			}
			else
			{
				DPFX(DPFPREP, 7, "New listen endpoint 0x%p, adding reference for listen command.",
					pDP8SimEndpoint);

				//
				// Store an endpoint reference with the command.
				//
				pDP8SimEndpoint->AddRef();
				pDP8SimCommand->SetListenEndpoint(pDP8SimEndpoint);


				//
				// Modify the message before indicating to the caller.
				//

				ZeroMemory(&ListenStatusModified, sizeof(ListenStatusModified));
				ListenStatusModified.ListenAdapter		= pListenStatus->ListenAdapter;
				ListenStatusModified.hResult			= pListenStatus->hResult;
				ListenStatusModified.hCommand			= (HANDLE) pDP8SimCommand;
				ListenStatusModified.pUserContext		= pDP8SimCommand->GetUserContext();
				ListenStatusModified.hEndpoint			= (HANDLE) pDP8SimEndpoint;


				//
				// Indicate the event to the real callback interface.
				//

				DPFX(DPFPREP, 2, "Indicating event SPEV_LISTENSTATUS (message = 0x%p) to interface 0x%p.",
					&ListenStatusModified, this->m_pDP8SPCB);

				hr = this->m_pDP8SPCB->IndicateEvent(SPEV_LISTENSTATUS, &ListenStatusModified);

				DPFX(DPFPREP, 2, "Returning from event SPEV_LISTENSTATUS [0x%lx].", hr);


				//
				// Release the reference we got from new, since we only needed
				// it while we indicated the endpoint up to the user.  The
				// listen command object has the reference it needs.
				//
				DPFX(DPFPREP, 7, "Releasing local listen endpoint 0x%p reference.",
					pDP8SimEndpoint);
				pDP8SimEndpoint->Release();
				pDP8SimEndpoint = NULL;
			}

			break;
		}

		case SPEV_ENUMQUERY:
		{
			SPIE_QUERY *				pQuery;
			ENUMQUERYDATAWRAPPER		QueryWrapper;


			pQuery = (SPIE_QUERY*) pvMessage;

			DNASSERT(pQuery->pAddressSender != NULL);
			DNASSERT(pQuery->pAddressDevice != NULL);


			pDP8SimCommand = (CDP8SimCommand*) pQuery->pUserContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_LISTEN);


			//
			// Modify the message before indicating to the caller.  We need a
			// wrapper so that ProxyEnumQuery can parse back out the original
			// query data pointer.
			//

			ZeroMemory(&QueryWrapper, sizeof(QueryWrapper));
			QueryWrapper.m_Sig[0]	= 'E';
			QueryWrapper.m_Sig[1]	= 'Q';
			QueryWrapper.m_Sig[2]	= 'E';
			QueryWrapper.m_Sig[3]	= 'W';

			QueryWrapper.pOriginalQuery = pQuery;

			hr = pQuery->pAddressSender->Duplicate(&QueryWrapper.QueryForUser.pAddressSender);
			if (hr != DPN_OK)
			{
				DPFX(DPFPREP, 0, "Couldn't duplicate enum query sender's address!");
			}
			else
			{
				hr = QueryWrapper.QueryForUser.pAddressSender->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
				if (hr != DPN_OK)
				{
					DPFX(DPFPREP, 0, "Couldn't change enum query sender's address' SP!");
				}
				else
				{
					hr = pQuery->pAddressDevice->Duplicate(&QueryWrapper.QueryForUser.pAddressDevice);
					if (hr != DPN_OK)
					{
						DPFX(DPFPREP, 0, "Couldn't duplicate enum query device address!");
					}
					else
					{
						hr = QueryWrapper.QueryForUser.pAddressDevice->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
						if (hr != DPN_OK)
						{
							DPFX(DPFPREP, 0, "Couldn't change enum query device address' SP!");
						}
						else
						{
							QueryWrapper.QueryForUser.pReceivedData	= pQuery->pReceivedData;
							QueryWrapper.QueryForUser.pUserContext	= pDP8SimCommand->GetUserContext();


							//
							// Indicate the event to the real callback interface.
							//

							DPFX(DPFPREP, 2, "Indicating SPEV_ENUMQUERY (message = 0x%p) to interface 0x%p.",
								&QueryWrapper.QueryForUser, this->m_pDP8SPCB);

							hr = this->m_pDP8SPCB->IndicateEvent(SPEV_ENUMQUERY, &QueryWrapper.QueryForUser);

							DPFX(DPFPREP, 2, "Returning from SPEV_ENUMQUERY [0x%lx].", hr);
						}

						QueryWrapper.QueryForUser.pAddressDevice->Release();
						QueryWrapper.QueryForUser.pAddressDevice = NULL;
					}
				}

				QueryWrapper.QueryForUser.pAddressSender->Release();
				QueryWrapper.QueryForUser.pAddressSender = NULL;
			}
			break;
		}

		case SPEV_QUERYRESPONSE:
		{
			SPIE_QUERYRESPONSE *	pQueryResponse;
			SPIE_QUERYRESPONSE		QueryResponseModified;


			pQueryResponse = (SPIE_QUERYRESPONSE*) pvMessage;

			DNASSERT(pQueryResponse->pAddressSender != NULL);
			DNASSERT(pQueryResponse->pAddressDevice != NULL);


			pDP8SimCommand = (CDP8SimCommand*) pQueryResponse->pUserContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_ENUMQUERY);


			//
			// Modify the message before indicating to the caller.
			//

			ZeroMemory(&QueryResponseModified, sizeof(QueryResponseModified));

			hr = pQueryResponse->pAddressSender->Duplicate(&QueryResponseModified.pAddressSender);
			if (hr != DPN_OK)
			{
				DPFX(DPFPREP, 0, "Couldn't duplicate query response sender's address!");
			}
			else
			{
				hr = QueryResponseModified.pAddressSender->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
				if (hr != DPN_OK)
				{
					DPFX(DPFPREP, 0, "Couldn't change query response sender's address' SP!");
				}
				else
				{
					hr = pQueryResponse->pAddressDevice->Duplicate(&QueryResponseModified.pAddressDevice);
					if (hr != DPN_OK)
					{
						DPFX(DPFPREP, 0, "Couldn't duplicate query response device address!");
					}
					else
					{
						hr = QueryResponseModified.pAddressDevice->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
						if (hr != DPN_OK)
						{
							DPFX(DPFPREP, 0, "Couldn't change query response device address' SP!");
						}
						else
						{
							QueryResponseModified.pReceivedData		= pQueryResponse->pReceivedData;
							QueryResponseModified.dwRoundTripTime	= pQueryResponse->dwRoundTripTime;
							QueryResponseModified.pUserContext		= pDP8SimCommand->GetUserContext();


							//
							// Indicate the event to the real callback interface.
							//

							DPFX(DPFPREP, 2, "Indicating SPEV_QUERYRESPONSE (message = 0x%p) to interface 0x%p.",
								&QueryResponseModified, this->m_pDP8SPCB);

							hr = this->m_pDP8SPCB->IndicateEvent(SPEV_QUERYRESPONSE, &QueryResponseModified);

							DPFX(DPFPREP, 2, "Returning from SPEV_QUERYRESPONSE [0x%lx].", hr);
						}

						QueryResponseModified.pAddressDevice->Release();
						QueryResponseModified.pAddressDevice = NULL;
					}
				}

				QueryResponseModified.pAddressSender->Release();
				QueryResponseModified.pAddressSender = NULL;
			}
			break;
		}

		case SPEV_LISTENADDRESSINFO:
		{
			SPIE_LISTENADDRESSINFO *	pListenAddressInfo;
			SPIE_LISTENADDRESSINFO		ListenAddressInfoModified;


			pListenAddressInfo = (SPIE_LISTENADDRESSINFO*) pvMessage;

			DNASSERT(pListenAddressInfo->pDeviceAddress != NULL);


			pDP8SimCommand = (CDP8SimCommand*) pListenAddressInfo->pCommandContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_LISTEN);


			//
			// Modify the message before indicating to the caller.
			//

			ZeroMemory(&ListenAddressInfoModified, sizeof(ListenAddressInfoModified));

			hr = pListenAddressInfo->pDeviceAddress->Duplicate(&ListenAddressInfoModified.pDeviceAddress);
			if (hr != DPN_OK)
			{
				DPFX(DPFPREP, 0, "Couldn't duplicate listen address info device address!");
			}
			else
			{
				hr = ListenAddressInfoModified.pDeviceAddress->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
				if (hr != DPN_OK)
				{
					DPFX(DPFPREP, 0, "Couldn't change listen address info device address' SP!");
				}
				else
				{
					ListenAddressInfoModified.hCommandStatus	= pListenAddressInfo->hCommandStatus;
					ListenAddressInfoModified.pCommandContext	= pDP8SimCommand->GetUserContext();


					//
					// Indicate the event to the real callback interface.
					//

					DPFX(DPFPREP, 2, "Indicating SPEV_LISTENADDRESSINFO (message = 0x%p) to interface 0x%p.",
						&ListenAddressInfoModified, this->m_pDP8SPCB);

					hr = this->m_pDP8SPCB->IndicateEvent(SPEV_LISTENADDRESSINFO, &ListenAddressInfoModified);

					DPFX(DPFPREP, 2, "Returning from SPEV_LISTENADDRESSINFO [0x%lx].", hr);
				}

				ListenAddressInfoModified.pDeviceAddress->Release();
				ListenAddressInfoModified.pDeviceAddress = NULL;
			}
			break;
		}

		case SPEV_ENUMADDRESSINFO:
		{
			SPIE_ENUMADDRESSINFO *	pEnumAddressInfo;
			SPIE_ENUMADDRESSINFO	EnumAddressInfoModified;


			pEnumAddressInfo = (SPIE_ENUMADDRESSINFO*) pvMessage;

			DNASSERT(pEnumAddressInfo->pHostAddress != NULL);
			DNASSERT(pEnumAddressInfo->pDeviceAddress != NULL);


			pDP8SimCommand = (CDP8SimCommand*) pEnumAddressInfo->pCommandContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_ENUMQUERY);


			//
			// Modify the message before indicating to the caller.
			//

			ZeroMemory(&EnumAddressInfoModified, sizeof(EnumAddressInfoModified));

			hr = pEnumAddressInfo->pHostAddress->Duplicate(&EnumAddressInfoModified.pHostAddress);
			if (hr != DPN_OK)
			{
				DPFX(DPFPREP, 0, "Couldn't duplicate enum address info host's address!");
			}
			else
			{
				hr = EnumAddressInfoModified.pHostAddress->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
				if (hr != DPN_OK)
				{
					DPFX(DPFPREP, 0, "Couldn't change enum address info host's address' SP!");
				}
				else
				{
					hr = pEnumAddressInfo->pDeviceAddress->Duplicate(&EnumAddressInfoModified.pDeviceAddress);
					if (hr != DPN_OK)
					{
						DPFX(DPFPREP, 0, "Couldn't duplicate enum address info device address!");
					}
					else
					{
						hr = EnumAddressInfoModified.pDeviceAddress->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
						if (hr != DPN_OK)
						{
							DPFX(DPFPREP, 0, "Couldn't change enum address info device address' SP!");
						}
						else
						{
							EnumAddressInfoModified.hCommandStatus	= pEnumAddressInfo->hCommandStatus;
							EnumAddressInfoModified.pCommandContext	= pDP8SimCommand->GetUserContext();


							//
							// Indicate the event to the real callback interface.
							//

							DPFX(DPFPREP, 2, "Indicating SPEV_ENUMADDRESSINFO (message = 0x%p) to interface 0x%p.",
								&EnumAddressInfoModified, this->m_pDP8SPCB);

							hr = this->m_pDP8SPCB->IndicateEvent(SPEV_ENUMADDRESSINFO, &EnumAddressInfoModified);

							DPFX(DPFPREP, 2, "Returning from SPEV_ENUMADDRESSINFO [0x%lx].", hr);
						}

						EnumAddressInfoModified.pDeviceAddress->Release();
						EnumAddressInfoModified.pDeviceAddress = NULL;
					}
				}

				EnumAddressInfoModified.pHostAddress->Release();
				EnumAddressInfoModified.pHostAddress = NULL;
			}
			break;
		}

		case SPEV_CONNECTADDRESSINFO:
		{
			SPIE_CONNECTADDRESSINFO *	pConnectAddressInfo;
			SPIE_CONNECTADDRESSINFO		ConnectAddressInfoModified;


			pConnectAddressInfo = (SPIE_CONNECTADDRESSINFO*) pvMessage;

			DNASSERT(pConnectAddressInfo->pHostAddress != NULL);
			DNASSERT(pConnectAddressInfo->pDeviceAddress != NULL);


			pDP8SimCommand = (CDP8SimCommand*) pConnectAddressInfo->pCommandContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_CONNECT);


			//
			// Modify the message before indicating to the caller.
			//

			ZeroMemory(&ConnectAddressInfoModified, sizeof(ConnectAddressInfoModified));

			hr = pConnectAddressInfo->pHostAddress->Duplicate(&ConnectAddressInfoModified.pHostAddress);
			if (hr != DPN_OK)
			{
				DPFX(DPFPREP, 0, "Couldn't duplicate connect address info host's address!");
			}
			else
			{
				hr = ConnectAddressInfoModified.pHostAddress->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
				if (hr != DPN_OK)
				{
					DPFX(DPFPREP, 0, "Couldn't change connect address info host's address' SP!");
				}
				else
				{
					hr = pConnectAddressInfo->pDeviceAddress->Duplicate(&ConnectAddressInfoModified.pDeviceAddress);
					if (hr != DPN_OK)
					{
						DPFX(DPFPREP, 0, "Couldn't duplicate connect address info device address!");
					}
					else
					{
						hr = ConnectAddressInfoModified.pDeviceAddress->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
						if (hr != DPN_OK)
						{
							DPFX(DPFPREP, 0, "Couldn't change connect address info device address' SP!");
						}
						else
						{
							ConnectAddressInfoModified.hCommandStatus	= pConnectAddressInfo->hCommandStatus;
							ConnectAddressInfoModified.pCommandContext	= pDP8SimCommand->GetUserContext();


							//
							// Indicate the event to the real callback interface.
							//

							DPFX(DPFPREP, 2, "Indicating SPEV_CONNECTADDRESSINFO (message = 0x%p) to interface 0x%p.",
								&ConnectAddressInfoModified, this->m_pDP8SPCB);

							hr = this->m_pDP8SPCB->IndicateEvent(SPEV_CONNECTADDRESSINFO, &ConnectAddressInfoModified);

							DPFX(DPFPREP, 2, "Returning from SPEV_CONNECTADDRESSINFO [0x%lx].", hr);
						}

						ConnectAddressInfoModified.pDeviceAddress->Release();
						ConnectAddressInfoModified.pDeviceAddress = NULL;
					}
				}

				ConnectAddressInfoModified.pHostAddress->Release();
				ConnectAddressInfoModified.pHostAddress = NULL;
			}
			break;
		}

		case SPEV_DATA_UNCONNECTED:
		{
			SPIE_DATA_UNCONNECTED *	pDataUnconnected;
			SPIE_DATA_UNCONNECTED	DataUnconnectedModified;


			pDataUnconnected = (SPIE_DATA_UNCONNECTED*) pvMessage;
			DNASSERT(pDataUnconnected->pvReplyBuffer != NULL);
			DNASSERT(pDataUnconnected->dwReplyBufferSize > 0);


			pDP8SimCommand = (CDP8SimCommand*) pDataUnconnected->pvListenCommandContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_LISTEN);


			//
			// Modify the message before indicating to the caller.
			//

			ZeroMemory(&DataUnconnectedModified, sizeof(DataUnconnectedModified));
			DataUnconnectedModified.pvListenCommandContext		= pDP8SimCommand->GetUserContext();
			DataUnconnectedModified.pReceivedData				= pDataUnconnected->pReceivedData;
			DataUnconnectedModified.dwSenderAddressHash			= pDataUnconnected->dwSenderAddressHash;
			DataUnconnectedModified.pvReplyBuffer				= pDataUnconnected->pvReplyBuffer;
			DataUnconnectedModified.dwReplyBufferSize			= pDataUnconnected->dwReplyBufferSize;


			//
			// Indicate the event to the real callback interface.
			//

			DPFX(DPFPREP, 2, "Indicating event SPEV_DATA_UNCONNECTED (message = 0x%p) to interface 0x%p.",
				&DataUnconnectedModified, this->m_pDP8SPCB);

			hr = this->m_pDP8SPCB->IndicateEvent(SPEV_DATA_UNCONNECTED, &DataUnconnectedModified);

			DPFX(DPFPREP, 2, "Returning from event SPEV_DATA_UNCONNECTED [0x%lx].", hr);

			if (hr == DPNSUCCESS_PENDING)
			{
				//
				// Update the reply buffer size with what the user returned.
				//
				DNASSERT(DataUnconnectedModified.dwReplyBufferSize > 0);
				DNASSERT(DataUnconnectedModified.dwReplyBufferSize <= pDataUnconnected->dwReplyBufferSize);
				pDataUnconnected->dwReplyBufferSize = DataUnconnectedModified.dwReplyBufferSize;
			}

			break;
		}

#ifndef DPNBUILD_NOMULTICAST
		case SPEV_DATA_UNKNOWNSENDER:
		{
			SPIE_DATA_UNKNOWNSENDER *	pDataUnknownSender;
			SPIE_DATA_UNKNOWNSENDER		DataUnknownSenderModified;


			pDataUnknownSender = (SPIE_DATA_UNKNOWNSENDER*) pvMessage;

			DNASSERT(pDataUnknownSender->pSenderAddress != NULL);


			pDP8SimCommand = (CDP8SimCommand*) pDataUnknownSender->pvListenCommandContext;
			DNASSERT(pDP8SimCommand->IsValidObject());
			DNASSERT(pDP8SimCommand->GetType() == CMDTYPE_LISTEN);


			//
			// Modify the message before indicating to the caller.
			//

			ZeroMemory(&DataUnknownSenderModified, sizeof(DataUnknownSenderModified));

			hr = pDataUnknownSender->pSenderAddress->Duplicate(&DataUnknownSenderModified.pSenderAddress);
			if (hr != DPN_OK)
			{
				DPFX(DPFPREP, 0, "Couldn't duplicate listen address info device address!");
			}
			else
			{
				hr = DataUnknownSenderModified.pSenderAddress->SetSP(this->m_pOwningDP8SimSP->GetFakeSP());
				if (hr != DPN_OK)
				{
					DPFX(DPFPREP, 0, "Couldn't change listen address info device address' SP!");
				}
				else
				{
					DataUnknownSenderModified.pvListenCommandContext	= pDP8SimCommand->GetUserContext();
					DataUnknownSenderModified.pReceivedData				= pDataUnknownSender->pReceivedData;


					//
					// Indicate the event to the real callback interface.
					//

					DPFX(DPFPREP, 2, "Indicating SPEV_DATA_UNKNOWNSENDER (message = 0x%p) to interface 0x%p.",
						&DataUnknownSenderModified, this->m_pDP8SPCB);

					hr = this->m_pDP8SPCB->IndicateEvent(SPEV_DATA_UNKNOWNSENDER, &DataUnknownSenderModified);

					DPFX(DPFPREP, 2, "Returning from SPEV_DATA_UNKNOWNSENDER [0x%lx].", hr);
				}

				DataUnknownSenderModified.pSenderAddress->Release();
				DataUnknownSenderModified.pSenderAddress = NULL;
			}
			break;
		}
#endif // ! DPNBUILD_NOMULTICAST

		default:
		{
			DPFX(DPFPREP, 0, "Unrecognized event type %u!", EventType);
			DNASSERT(FALSE);
			hr = E_NOTIMPL;
			break;
		}
	}


	DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);

	return hr;
} // CDP8SimCB::IndicateEvent





#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::CommandComplete"
//=============================================================================
// CDP8SimCB::CommandComplete
//-----------------------------------------------------------------------------
//
// Description: ?
//
// Arguments:
//	HANDLE hCommand		- Handle of command that's completing.
//	HRESULT hrResult	- Result code for completing operation.
//	PVOID pvContext		- Pointer to user context for command.
//
// Returns: HRESULT
//=============================================================================
STDMETHODIMP CDP8SimCB::CommandComplete(HANDLE hCommand, HRESULT hrResult, PVOID pvContext)
{
	HRESULT				hr;
	CDP8SimCommand *	pDP8SimCommand = (CDP8SimCommand*) pvContext;


	DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx, 0x%p)",
		this, hCommand, hrResult, pvContext);


	//
	// Validate (actually assert) the object.
	//
	DNASSERT(this->IsValidObject());


	//
	// Assert the parameters.
	//
	DNASSERT(pDP8SimCommand->IsValidObject());



	//
	// Switch on the command type.
	//
	switch (pDP8SimCommand->GetType())
	{
		case CMDTYPE_SENDDATA_IMMEDIATE:
		{
			//
			// Update the statistics.
			//
			if (hrResult == DPN_OK)
			{
				this->m_pOwningDP8SimSP->IncrementStatsSendTransmitted(pDP8SimCommand->GetMessageSize(),
																		0);
			}
			else
			{
				//
				// The send failed.  Currently we don't track this.
				//
			}
			pDP8SimCommand->SetMessageSize(0);


			//
			// Indicate the completion to the real callback interface.
			//
			DPFX(DPFPREP, 2, "Indicating immediate send command 0x%p complete (result = 0x%lx, context = 0x%p) to interface 0x%p.",
				pDP8SimCommand, hrResult,
				pDP8SimCommand->GetUserContext(),
				this->m_pDP8SPCB);

			hr = this->m_pDP8SPCB->CommandComplete(pDP8SimCommand,
													hrResult,
													pDP8SimCommand->GetUserContext());

			DPFX(DPFPREP, 2, "Returning from command complete [0x%lx].", hr);


			//
			// Remove the send counter.
			//
			this->m_pOwningDP8SimSP->DecSendsPending();

			break;
		}

		case CMDTYPE_SENDDATA_DELAYED:
		{
			CDP8SimSend *	pDP8SimSend;


			//
			// Handle the completion.  It never gets indicated to the user
			// though.
			//

			pDP8SimSend = (CDP8SimSend*) pDP8SimCommand->GetUserContext();
			DNASSERT(pDP8SimSend->IsValidObject());

			DPFX(DPFPREP, 5, "Send 0x%p (command 0x%p) completed.",
				pDP8SimSend, pDP8SimCommand);


			//
			// Update the statistics.
			//
			if (hrResult == DPN_OK)
			{
				this->m_pOwningDP8SimSP->IncrementStatsSendTransmitted(pDP8SimSend->GetMessageSize(),
																		pDP8SimSend->GetLatencyAdded());
			}
			else
			{
				//
				// The send failed.  We don't truly track this, but since we
				// already indicated a successful send to the user, we'll count
				// it as a drop.
				//
				this->m_pOwningDP8SimSP->IncrementStatsSendDropped(pDP8SimSend->GetMessageSize());
			}
			

			//
			// Remove the send counter.
			//
			this->m_pOwningDP8SimSP->DecSendsPending();


			pDP8SimSend->Release();
			pDP8SimSend = NULL;

			hr = DPN_OK;

			break;
		}

		case CMDTYPE_CONNECT:
		case CMDTYPE_DISCONNECT:
		case CMDTYPE_LISTEN:
		case CMDTYPE_ENUMQUERY:
		case CMDTYPE_ENUMRESPOND:
		{
			//
			// Indicate the completion to the real callback interface.
			//
			DPFX(DPFPREP, 2, "Indicating command 0x%p complete (type = %u, result = 0x%lx, context = 0x%p) to interface 0x%p.",
				pDP8SimCommand, pDP8SimCommand->GetType(), hrResult,
				pDP8SimCommand->GetUserContext(), this->m_pDP8SPCB);

			hr = this->m_pDP8SPCB->CommandComplete(pDP8SimCommand,
													hrResult,
													pDP8SimCommand->GetUserContext());

			DPFX(DPFPREP, 2, "Returning from command complete [0x%lx].", hr);


			//
			// If this was a listen, we need to kill the listen endpoint.
			//
			if (pDP8SimCommand->GetType() == CMDTYPE_LISTEN)
			{
				CDP8SimEndpoint *	pDP8SimEndpoint;


				pDP8SimEndpoint = pDP8SimCommand->GetListenEndpoint();
				DNASSERT(pDP8SimEndpoint != NULL);

				pDP8SimCommand->SetListenEndpoint(NULL);

				DPFX(DPFPREP, 7, "Releasing listen endpoint 0x%p.",
					pDP8SimEndpoint);

				pDP8SimEndpoint->Release();
				pDP8SimEndpoint = NULL;
			}
			break;
		}
		
		default:
		{
			DPFX(DPFPREP, 0, "Unrecognized command type %u!", pDP8SimCommand->GetType());
			DNASSERT(FALSE);
			hr = E_NOTIMPL;
			break;
		}
	}


	//
	// Destroy the object.
	//
	DPFX(DPFPREP, 7, "Releasing completed command 0x%p.", pDP8SimCommand);
	pDP8SimCommand->Release();
	pDP8SimCommand = NULL;


	DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);

	return hr;
} // CDP8SimCB::CommandComplete






#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::InitializeObject"
//=============================================================================
// CDP8SimCB::InitializeObject
//-----------------------------------------------------------------------------
//
// Description:    Sets up the object for use like the constructor, but may
//				fail with OUTOFMEMORY.  Should only be called by class factory
//				creation routine.
//
// Arguments: None.
//
// Returns: HRESULT
//	S_OK			- Initialization was successful.
//	E_OUTOFMEMORY	- There is not enough memory to initialize.
//=============================================================================
HRESULT CDP8SimCB::InitializeObject(void)
{
	HRESULT		hr;


	DPFX(DPFPREP, 5, "(0x%p) Enter", this);

	DNASSERT(this->IsValidObject());


	//
	// Create the lock.
	// 

	if (! DNInitializeCriticalSection(&this->m_csLock))
	{
		hr = E_OUTOFMEMORY;
		goto Failure;
	}


	//
	// Don't allow critical section reentry.
	//
	DebugSetCriticalSectionRecursionCount(&this->m_csLock, 0);


	hr = S_OK;

Exit:

	DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);

	return hr;


Failure:

	goto Exit;
} // CDP8SimCB::InitializeObject






#undef DPF_MODNAME
#define DPF_MODNAME "CDP8SimCB::UninitializeObject"
//=============================================================================
// CDP8SimCB::UninitializeObject
//-----------------------------------------------------------------------------
//
// Description:    Cleans up the object like the destructor, mostly to balance
//				InitializeObject.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
void CDP8SimCB::UninitializeObject(void)
{
	DPFX(DPFPREP, 5, "(0x%p) Enter", this);


	DNASSERT(this->IsValidObject());


	DNDeleteCriticalSection(&this->m_csLock);


	DPFX(DPFPREP, 5, "(0x%p) Returning", this);
} // CDP8SimCB::UninitializeObject