using System;
using System.Windows.Forms;
using System.Threading;
using System.Timers;

using Microsoft.DirectX.DirectPlay;


namespace DarkStrideToolbox
{
	public struct FindHostsResponseInformation
	{
		public int LastFoundTime;
		public ApplicationDescription ApplicationDesc;
		public int RoundTripLatencyMs;
		public Address sender;
		public Address device;
		public override string ToString()
		{
			if (ApplicationDesc.MaxPlayers > 0)
				return ApplicationDesc.SessionName + " (" + ApplicationDesc.CurrentPlayers + "/" + ApplicationDesc.MaxPlayers + ") (" + RoundTripLatencyMs + "ms)";
			else
				return ApplicationDesc.SessionName + " (" + ApplicationDesc.CurrentPlayers + ") (" + RoundTripLatencyMs + "ms)";
		}
	};

	public class DSNetworkWrapper
	{
		#region Properties
		#region Delegate Variables
		private CallbackPlayerJoined		m_odDelegate_PlayerJoined;
		private CallbackPlayerQuit			m_odDelegate_PlayerQuit;
		private CallbackHostMigrated		m_odDelegate_HostMigrated;
		private CallbackDataReceived		m_odDelegate_DataReceived;
		private CallbackSessionTerminated	m_odDelegate_SessionTerminated;
		private CallbackConnection			m_odDelegate_Connection;
		private CallbackGameListUpdate		m_odDelegate_GameListUpdate;
		private CallbackCanceledGameSearch	m_odDelegate_CanceledGameSearch;
		private CallbackError				m_odDelegate_Error;

		public delegate void CallbackPlayerJoined(DSNetworkPlayer oPlayerJoined,bool bUs);
		public delegate void CallbackPlayerQuit(DSNetworkPlayer oPlayerQuit);
		public delegate void CallbackHostMigrated(DSNetworkPlayer oNewHost);
		public delegate void CallbackDataReceived(DSNetworkPacket oPacket);
		public delegate void CallbackSessionTerminated(Microsoft.DirectX.DirectPlay.ResultCode nReason);
		public delegate void CallbackConnection( Microsoft.DirectX.DirectPlay.ResultCode oCode,string sDescription,bool bConnectionSuccessful );
		public delegate void CallbackGameListUpdate( FindHostsResponseInformation dpGameInfo );
		public delegate void CallbackCanceledGameSearch();
		private delegate void PeerCloseCallback(); // This delegate will be called when the session terminated event is fired.

		public delegate void CallbackError( System.Exception oError );
		#endregion

		private const long m_cPINGPACKETREQUEST = 0;
		private const long m_cPINGPACKETREPLY = 1;
		private int m_nDefaultPort = 2502;

		private Peer m_oPeerObject = null; // Main DPlay object
		private Guid m_oAppGUID;
		private ConnectWizard m_oConnectWizard = null; // The wizard to create/join a DPlay Session
		private DSSortedList m_oPlayerList = new DSSortedList();
		private DSNetworkPlayer m_oUs = null;

		//Pinging variables
		private double m_nSecondsBetweenPings = 0;
		private Thread m_oPingThread = null;

		private Address m_oOurAddress = null;
		private Address m_oSearchingHostAddress = null;
		private ApplicationDescription m_oAppDesc;
		private int m_nFindHostHandle = 0;
		private int m_nConnectHandle = 0;
		private bool m_bSearchingForGames = false;
		private System.Timers.Timer m_oConnectingTimer = null;
		private bool m_bConnected = false;		

		private bool m_bHasBeenInitialized = false;
		#endregion


		public DSNetworkWrapper(){}


		public void Dispose()
		{
			// Cleanup DPlay
			if (m_oPeerObject != null)
				m_oPeerObject.Dispose();

			m_oPeerObject = null;

			if( m_oConnectWizard != null )
				m_oConnectWizard = null;

			m_oConnectWizard = null;
		}
	

		//This returns true if it was successfully initialized, false if canceled
		public bool Initialize( Guid oAppGUID )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.Initialize";
			bool bSucceded = false;

			try
			{
				if( m_bHasBeenInitialized == false )
				{
					bSucceded = Initialize( oAppGUID,true );
				}
				else
				{
					bSucceded = true;
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}

			return( bSucceded );
		}
		public bool Initialize( Guid oAppGUID,bool bUseWizard )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.Initialize";
			bool bSucceded = false;
			
			try
			{
				m_oAppGUID = oAppGUID;

				//Initialize the network connections
				m_oPeerObject = new Peer();

				// First set up our event handlers (We only need events for the ones we care about)
				m_oPeerObject.PlayerCreated		+= new PlayerCreatedEventHandler(this.PlayerJoined);
				m_oPeerObject.PlayerDestroyed	+= new PlayerDestroyedEventHandler(this.PlayerQuit);
				m_oPeerObject.HostMigrated		+= new HostMigratedEventHandler(this.HostMigrated);
				m_oPeerObject.Receive			+= new ReceiveEventHandler(this.DataReceived);
				m_oPeerObject.SessionTerminated += new SessionTerminatedEventHandler(this.SessionTerminated);

				if( bUseWizard == true )
				{
					m_oConnectWizard = new ConnectWizard( m_oPeerObject, m_oAppGUID, "Chat Peer" );

					m_oConnectWizard.DefaultPort = m_nDefaultPort;

					bSucceded = m_oConnectWizard.StartWizard();
				}
				else
				{
					bSucceded = true;
				}

				//Start the pinging thread
				m_oPingThread = new Thread( new ThreadStart( this.PingLoop ) );
				m_oPingThread.IsBackground = true;
				m_oPingThread.Start();
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}

			return( bSucceded );
		}

		public void DeInitialize()
		{
			const string sRoutineName = "DarkStrideToolbox.Network.DeInitialize";

			try
			{
				//Initialize the network connections
				m_oPeerObject = null;
				m_oConnectWizard = null;
				m_oPlayerList.Clear();
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		public virtual void HostGame( ServiceProviderInformation oServiceProvider, Guid oAppGuid, string sSessionName, string sMyPlayerName,int nPort )
		{
			PlayerInformation oMyInformation = new PlayerInformation();

			
			m_oOurAddress = new Address();
			m_oOurAddress.ServiceProvider = oServiceProvider.Guid;

			m_oAppDesc = new ApplicationDescription();
			m_oAppDesc.GuidApplication = oAppGuid;
			m_oAppDesc.SessionName = sSessionName;
			    
			m_oAppDesc.Flags = 0;
			m_oAppDesc.Flags |= SessionFlags.MigrateHost;
			m_oAppDesc.Flags |= SessionFlags.FastSigned;
			m_oAppDesc.Flags |= SessionFlags.NoDpnServer;

			m_oOurAddress.AddComponent( Address.KeyPort,nPort );
			
			oMyInformation.Name = sMyPlayerName;
			this.Peer.SetPeerInformation( oMyInformation,SyncFlags.PeerInformation );
			this.Peer.Host( m_oAppDesc,m_oOurAddress,HostFlags.OkToQueryForAddressing );
			m_bConnected = true;
		}

		public void SearchFormGames( ServiceProviderInformation oServiceProvider,Guid oAppGuid,string sIP,int nPort )
		{
			FindHostsFlags nFlags = 0;


			//Set up the event handlers
			this.Peer.FindHostResponse += new FindHostResponseEventHandler( FindHostResponseMessage );
			this.Peer.ConnectComplete += new ConnectCompleteEventHandler(ConnectResult);
			this.Peer.AsyncOperationComplete += new AsyncOperationCompleteEventHandler( CancelAsync );
 
			//Do something...
			m_oOurAddress = new Address();
			m_oOurAddress.ServiceProvider = oServiceProvider.Guid;

			//Get ourselves a host object
			if( m_oSearchingHostAddress != null )
			{
				m_oSearchingHostAddress.Dispose();
			}
			m_oSearchingHostAddress = new Address();
			m_oSearchingHostAddress.ServiceProvider = oServiceProvider.Guid;
			m_oSearchingHostAddress.AddComponent( Address.KeyHostname,sIP );
			m_oSearchingHostAddress.AddComponent( Address.KeyPort,nPort );

			//Time to enum our hosts
			m_oAppDesc = new ApplicationDescription();
			m_oAppDesc.GuidApplication = oAppGuid;
		    
			this.Peer.FindHosts( m_oAppDesc,m_oSearchingHostAddress,m_oOurAddress,
				null,Timeout.Infinite,0,Timeout.Infinite, nFlags, out m_nFindHostHandle);
			m_bSearchingForGames = true;
		}

		public virtual void JoinGame( FindHostsResponseInformation oGame,string sMyPlayerName )
		{
			PlayerInformation oMyInformation = new PlayerInformation();


			oMyInformation.Name = sMyPlayerName;
			this.Peer.SetPeerInformation( oMyInformation,SyncFlags.PeerInformation );

			this.Peer.Connect( oGame.ApplicationDesc,oGame.sender,oGame.device,null,
				out m_nConnectHandle,ConnectFlags.OkToQueryForAddressing );

			//Set up our connect timer to check the results
			m_oConnectingTimer = new System.Timers.Timer( 100 ); //A 100ms interval
			m_oConnectingTimer.Elapsed += new System.Timers.ElapsedEventHandler( this.ConnectingTimer );
			//m_oConnectingTimer.SynchronizingObject = (System.ComponentModel.ISynchronizeInvoke)this;
			m_oConnectingTimer.Start();
		}

		public void CancelGameSearch()
		{
			if( m_bSearchingForGames == true && m_nFindHostHandle != 0 )
			{
				this.Peer.CancelAsyncOperation( m_nFindHostHandle );
				m_bSearchingForGames = false;
			}
		}

		public void CancelGameJoin()
		{
			if( m_nConnectHandle != 0 )
			{
				this.Peer.CancelAsyncOperation( m_nConnectHandle );
			}
		}


		public void PingAllPlayers()
		{
			DSNetworkPacket oNetPacket = null;
			DSNetworkPlayer oLoopPlayer = null;
			long nStartCounter = 0;


			nStartCounter = DSMisc.GetQueryPerformanceCounter();

			for( int nPlrIndex=0 ; nPlrIndex<m_oPlayerList.Count ; nPlrIndex++ )
			{
				oLoopPlayer = (DSNetworkPlayer)m_oPlayerList.GetByIndex( nPlrIndex );

				oLoopPlayer.PingStartCounter = nStartCounter;
				oLoopPlayer.PingStopCounter = 0;
			}

			oNetPacket = new DSNetworkPacket( m_cPINGPACKETREQUEST,m_oUs.SocketID.ToString() );
			oNetPacket.SystemMessage = true;
			SendMsg( oNetPacket );
		}

		public void PingLoop()
		{
			while( true )
			{
				//Delay, woulden't want this thread to spin to much
				if( m_nSecondsBetweenPings == 0 )
				{
					Thread.Sleep( 10 * 1000 );
				}
				else
				{
					Thread.Sleep( (int)( m_nSecondsBetweenPings * 1000 ) );

					//Send out a ping
					PingAllPlayers();
				}
			}
		}


		//09/09/2005 Chris Hill  These functions are for the creation of a game.
		public ServiceProviderInformation[] GetServiceProviders()
		{
			return( this.Peer.GetServiceProviders( false ) );
		}
		public ServiceProviderInformation FindServiceProvider( string sGUID )
		{
			ServiceProviderInformation oRetVal = new ServiceProviderInformation();


			foreach( ServiceProviderInformation oSPInfo in this.GetServiceProviders() )
			{
				if( oSPInfo.Guid.ToString().CompareTo( sGUID ) == 0 )
				{
					oRetVal = oSPInfo;
					break;
				}
			}


			return( oRetVal );
		}

		//Returns true if the given provider requires a port component
		//<param name="provider">ServiceProvider Guid</param>
		static public bool ProviderRequiresPort( Guid provider )
		{
			return (provider != Address.ServiceProviderSerial &&
					provider != Address.ServiceProviderModem &&
					provider != Address.ServiceProviderBlueTooth);
		}

		//A host was found and responded to our query
		private void FindHostResponseMessage( object sender, FindHostResponseEventArgs dpMessage )
		{
			FindHostsResponseInformation dpInfo = new FindHostsResponseInformation();


			dpInfo.ApplicationDesc = dpMessage.Message.ApplicationDescription;
			dpInfo.device = dpMessage.Message.AddressDevice;
			dpInfo.sender = dpMessage.Message.AddressSender;
			dpInfo.RoundTripLatencyMs = dpMessage.Message.RoundTripLatencyMs;
			dpInfo.LastFoundTime = Environment.TickCount;

			GameListUpdate( dpInfo );
		}
		//An asynchronous operation was cancelled
		private void CancelAsync( object sender, AsyncOperationCompleteEventArgs e )
		{
			if( e.Message.AsyncOperationHandle == m_nFindHostHandle )
			{
				m_bSearchingForGames = false;
				m_nFindHostHandle = 0;
				CanceledGameSearch();
			}
			if( e.Message.AsyncOperationHandle == m_nConnectHandle )
			{
			}
		}
		//Fired when a connect complete message is received from DirectPlay.  Fire our event to notify the sample we've connected
		private void ConnectResult(object sender, ConnectCompleteEventArgs e)
		{
			m_bConnected = ( e.Message.ResultCode == 0 );
			Connection( e.Message.ResultCode,MSDXPlayResultCodeToString( e.Message.ResultCode ),m_bConnected );
		}
		private static string MSDXPlayResultCodeToString( Microsoft.DirectX.DirectPlay.ResultCode oCode )
		{
			string sRetVal = "";

				 if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.ConnectionLost ){ sRetVal = "ConnectionLost"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.UserCancel ){ sRetVal = "UserCancel"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.TimedOut ){ sRetVal = "TimedOut"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.SessionFull ){ sRetVal = "SessionFull"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.PlayerNotReachable ){ sRetVal = "PlayerNotReachable"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.PlayerNotInGroup ){ sRetVal = "PlayerNotInGroup"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.PlayerLost ){ sRetVal = "PlayerLost"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.PlayerAlreadyInGroup ){ sRetVal = "PlayerAlreadyInGroup"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.NotReady ){ sRetVal = "NotReady"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.NotHost ){ sRetVal = "NotHost"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.NotAllowed ){ sRetVal = "NotAllowed"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.NoResponse ){ sRetVal = "NoResponse"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.NoHostPlayer ){ sRetVal = "NoHostPlayer"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.NoConnection ){ sRetVal = "NoConnection"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.HostTerminatedSession ){ sRetVal = "HostTerminatedSession"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.HostRejectedConnection ){ sRetVal = "HostRejectedConnection"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.DoesNotExist ){ sRetVal = "DoesNotExist"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.CannotCreatePlayer ){ sRetVal = "CannotCreatePlayer"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.CannotCreateGroup ){ sRetVal = "CannotCreateGroup"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.CannotCancel ){ sRetVal = "CannotCancel"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.Aborted ){ sRetVal = "Aborted"; }
			else if( oCode == Microsoft.DirectX.DirectPlay.ResultCode.Success ){ sRetVal = "Success"; }

			return( sRetVal );
		}
		
		//Wait for a connect to complete
		private void ConnectingTimer( object sender, System.Timers.ElapsedEventArgs e )
		{
			if (m_oConnectingTimer != null)
			{
				if( m_bConnected == true )
				{
					m_oConnectingTimer.Stop();
					m_oConnectingTimer = null;
				}
				else
				{
					//Need to set a timeout amount
					//Chris
					//					MessageBox.Show(this,"Failed to connect.","Failed to connect: " + MSDXPlayResultCodeToString( m_nConnectResultCode ),MessageBoxButtons.OK,MessageBoxIcon.Information);
				}
			}
		}


		public void SendMsg( DSNetworkPacket oPacket )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SendMsg";

			try
			{
				if( this.IsInitialized == false )
				{
					throw new System.Exception( "No network connections were found.  Peer object is closed." );
				}
				else
				{
					// Now we've got the data setup, send it off.
					m_oPeerObject.SendTo((int)PlayerID.AllPlayers, oPacket.GetPacket(), 0, SendFlags.Guaranteed);
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
	
		public void SendMsg( long nMsgType )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SendMsg";
			DSNetworkPacket oPacket = null;

			try
			{
				oPacket = new DSNetworkPacket( nMsgType );
				SendMsg( oPacket );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void SendMsg( long nMsgType,string sParam1 )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SendMsg";
			DSNetworkPacket oPacket = null;

			try
			{
				oPacket = new DSNetworkPacket( nMsgType,sParam1 );
				SendMsg( oPacket );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void SendMsg( long nMsgType,string sParam1,string sParam2 )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SendMsg";
			DSNetworkPacket oPacket = null;

			try
			{
				oPacket = new DSNetworkPacket( nMsgType,sParam1,sParam2 );
				SendMsg( oPacket );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void SendMsg( long nMsgType,string sParam1,string sParam2,string sParam3 )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SendMsg";
			DSNetworkPacket oPacket = null;

			try
			{
				oPacket = new DarkStrideToolbox.DSNetworkPacket( nMsgType,sParam1,sParam2,sParam3 );
				SendMsg( oPacket );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void SendMsg( long nMsgType,string sParam1,string sParam2,string sParam3,string sParam4 )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SendMsg";
			DSNetworkPacket oPacket = null;

			try
			{
				oPacket = new DSNetworkPacket( nMsgType,sParam1,sParam2,sParam3,sParam4 );
				SendMsg( oPacket );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void SendMsg( long nMsgType,string sParam1,string sParam2,string sParam3,string sParam4,string sParam5 )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SendMsg";
			DSNetworkPacket oPacket = null;

			try
			{
				oPacket = new DSNetworkPacket( nMsgType,sParam1,sParam2,sParam3,sParam4,sParam5 );
				SendMsg( oPacket );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		/*private void PeerClose()
		{
			const string sRoutineName = "DarkStrideToolbox.Network.PeerClose";

			try
			{
				// The session was terminated, go ahead and shut down
//				this.Dispose();
			}
			catch( System.Exception oEx )
			{
				ShowErrors( new System.Exception( sRoutineName + " Failed.",oEx ),"Network Thread" );
			}
		}
*/

		public DSNetworkPlayer GetPlayer( long nSocketID )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.GetPlayer";
			DSNetworkPlayer oRetVal = null;

			try
			{
				oRetVal = (DSNetworkPlayer)m_oPlayerList.GetByKey( nSocketID );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}

			return( oRetVal );
		}


		//This routine walks the list of players and sets the host flag
		private void UpdateHost( long nSocketID )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.UpdateHost";

			try
			{
				foreach( DSNetworkPlayer oNetPlayer in m_oPlayerList.GetValueList() )
				{
					oNetPlayer.IAmHost = ( oNetPlayer.SocketID == nSocketID );
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		#region Delegate Functions
		public virtual void PlayerJoined(object sender, PlayerCreatedEventArgs e)
		{
			const string sRoutineName = "DarkStrideToolbox.Network.PlayerJoined";			
			PlayerInformation peerInfo;
			DSNetworkPlayer oPlayer = null;

			try
			{
				peerInfo = m_oPeerObject.GetPeerInformation(e.Message.PlayerID);
				oPlayer = new DSNetworkPlayer();
				oPlayer.SocketID = e.Message.PlayerID;
				oPlayer.Name = peerInfo.Name;
				oPlayer.IAmHost = peerInfo.Host;
				oPlayer.IsMe = peerInfo.Local;

				//We lock the data here since it is shared across multiple threads.
				lock (m_oPlayerList)
				{
					m_oPlayerList.Add( oPlayer.SocketID,oPlayer );
				}

				//Save this player id if it's ourselves
				if( peerInfo.Local == true )
				{
					m_oUs = oPlayer;
				}
				if( m_odDelegate_PlayerJoined != null )
				{
					m_odDelegate_PlayerJoined( oPlayer,peerInfo.Local );
				}
			}
			catch( System.Exception oEx )
			{
				if( m_odDelegate_Error == null )
				{
					DSMisc.ShowErrors( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
				else
				{
					m_odDelegate_Error( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
			}
		}

		public virtual void PlayerQuit(object sender, PlayerDestroyedEventArgs e)
		{
			const string sRoutineName = "DarkStrideToolbox.Network.PlayerQuit";
			DSNetworkPlayer oPlayer = null;

			try
			{
				// Remove this player from our list
				// We lock the data here since it is shared across multiple threads.
				lock (m_oPlayerList)
				{
					oPlayer = GetPlayer(e.Message.PlayerID);
					m_oPlayerList.Remove( oPlayer.SocketID );

					// Update our number of player and our button
					//m_oTest.SetLabelText( m_oPlayerList.Count.ToString() );
				}

				if( m_odDelegate_PlayerQuit != null )
				{
					m_odDelegate_PlayerQuit( oPlayer );
				}
			}
			catch( System.Exception oEx )
			{
				if( m_odDelegate_Error == null )
				{
					DSMisc.ShowErrors( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
				else
				{
					m_odDelegate_Error( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
			}
		}

		public virtual void HostMigrated(object sender, HostMigratedEventArgs e)
		{
			const string sRoutineName = "DarkStrideToolbox.Network.HostMigrated";
			DSNetworkPlayer oPlayer = null;

			try
			{
				UpdateHost( e.Message.NewHostID );

				oPlayer = GetPlayer( e.Message.NewHostID );

				if( m_odDelegate_HostMigrated != null )
				{
					m_odDelegate_HostMigrated( oPlayer );
				}
			}
			catch( System.Exception oEx )
			{
				if( m_odDelegate_Error == null )
				{
					DSMisc.ShowErrors( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
				else
				{
					m_odDelegate_Error( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
			}
		}

		public virtual void DataReceived( int nSenderSocketID,DSNetworkPacket oPacket )
		{
			const string sRoutineName = "DarkStrideToolbox.Network.DataReceived";
			DSNetworkPlayer oNetPlayer = null;
			DSNetworkPacket oNetPacket = null;

			try
			{
				if( nSenderSocketID != m_oUs.SocketID )
				{
					if( oPacket.SystemMessage == true )
					{
						if( oPacket.MsgType == m_cPINGPACKETREQUEST )
						{
							oNetPacket = new DSNetworkPacket( m_cPINGPACKETREPLY,
															  oPacket.GetParamater( 0 ) );
							oNetPacket.SystemMessage = true;
							SendMsg( oNetPacket );
						}
						else if( oPacket.MsgType == m_cPINGPACKETREPLY )
						{
							if( Convert.ToInt32( oPacket.GetParamater( 0 ) ) == m_oUs.SocketID )
							{
								oNetPlayer = GetPlayer( nSenderSocketID );
								if( oNetPlayer != null )
								{
									oNetPlayer.PingStopCounter = DSMisc.GetQueryPerformanceCounter();
									oNetPlayer.LastPingTime = DateTime.Now;
									oNetPlayer.LastPingInSeconds = (double)( oNetPlayer.PingStopCounter - oNetPlayer.PingStartCounter ) / 
																   (double)DSMisc.GetQueryPerformanceFrequency();
								}
							}
						}
					}
					else if( m_odDelegate_DataReceived != null )
					{
						m_odDelegate_DataReceived( oPacket );
					}
				}
			}
			catch( System.Exception oEx )
			{
				if( m_odDelegate_Error == null )
				{
					DSMisc.ShowErrors( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
				else
				{
					m_odDelegate_Error( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
			}
		}
		public virtual void DataReceived(object sender, ReceiveEventArgs e)
		{
			DSNetworkPacket oPacket = null;


			oPacket = new DSNetworkPacket( e );
			DataReceived( e.Message.SenderID,oPacket);

			e.Message.ReceiveData.Dispose(); // We no longer need the data, Dispose the buffer
		}

		public virtual void SessionTerminated(object sender, SessionTerminatedEventArgs e)
		{
			const string sRoutineName = "DarkStrideToolbox.Network.SessionTerminated";

			try
			{
				// Well, this session is being terminated, let the user know
				if (e.Message.ResultCode ==  Microsoft.DirectX.DirectPlay.ResultCode.HostTerminatedSession)
				{
					MessageBox.Show("The Host has terminated this session.  This sample will now exit.", "Exiting", MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
				else
				{
					MessageBox.Show("The session has been lost.  This sample will now exit.", "Exiting", MessageBoxButtons.OK, MessageBoxIcon.Information);
				}

				if( m_odDelegate_SessionTerminated != null )
				{
					m_odDelegate_SessionTerminated( e.Message.ResultCode );
				}
			}
			catch( System.Exception oEx )
			{
				if( m_odDelegate_Error == null )
				{
					DSMisc.ShowErrors( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
				else
				{
					m_odDelegate_Error( new System.Exception( sRoutineName + " Failed.",oEx ) );
				}
			}
		}
		
		public virtual void Connection( Microsoft.DirectX.DirectPlay.ResultCode oCode,string sDescription,bool bConnectionSuccessful )
		{
			if( m_odDelegate_Connection != null )
			{
				m_odDelegate_Connection( oCode,sDescription,bConnectionSuccessful );
			}
		}
		public virtual void GameListUpdate( FindHostsResponseInformation oGameInfo )
		{
			if( m_odDelegate_GameListUpdate != null )
			{
				m_odDelegate_GameListUpdate( oGameInfo );
			}
		}
		public virtual void CanceledGameSearch()
		{
			if( m_odDelegate_CanceledGameSearch != null )
			{
				m_odDelegate_CanceledGameSearch();
			}
		}


		public void RegisterDelegate_PlayerJoined( CallbackPlayerJoined odPlayerJoined )
		{
			const string sRoutineName = "DarkStrideToolbox.GameEngine.RegisterDelegate_PlayerJoined";

			try
			{
				m_odDelegate_PlayerJoined += odPlayerJoined;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RegisterDelegate_PlayerQuit( CallbackPlayerQuit odPlayerQuit )
		{
			const string sRoutineName = "DarkStrideToolbox.GameEngine.RegisterDelegate_PlayerQuit";

			try
			{
				m_odDelegate_PlayerQuit += odPlayerQuit;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RegisterDelegate_HostMigrated( CallbackHostMigrated odHostMigrated )
		{
			const string sRoutineName = "DarkStrideToolbox.GameEngine.RegisterDelegate_HostMigrated";

			try
			{
				m_odDelegate_HostMigrated += odHostMigrated;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RegisterDelegate_DataReceived( CallbackDataReceived odDataReceived )
		{
			const string sRoutineName = "DarkStrideToolbox.GameEngine.RegisterDelegate_DataReceived";

			try
			{
				m_odDelegate_DataReceived += odDataReceived;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RegisterDelegate_SessionTerminated( CallbackSessionTerminated odSessionTerminated )
		{
			const string sRoutineName = "DarkStrideToolbox.GameEngine.RegisterDelegate_SessionTerminated";

			try
			{
				m_odDelegate_SessionTerminated += odSessionTerminated;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public void RegisterDelegate_Connection( CallbackConnection odConnection )
		{
			m_odDelegate_Connection += odConnection;
		}

		public void RegisterDelegate_Error( CallbackError odError )
		{
			const string sRoutineName = "DarkStrideToolbox.Input.RegisterDelegate_Error";

			try
			{
				m_odDelegate_Error += odError;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RegisterDelegate_GameListUpdate( CallbackGameListUpdate odGameListUpdate )
		{
			m_odDelegate_GameListUpdate += odGameListUpdate;
		}
		public void RegisterDelegate_CanceledGameSearch( CallbackCanceledGameSearch odCanceledGameSearch )
		{
			m_odDelegate_CanceledGameSearch += odCanceledGameSearch;
		}
		#endregion


		#region Properties
		public int DefaultPort
		{
			get
			{
				return( m_nDefaultPort );
			}
			set
			{
				m_nDefaultPort = value;
			}
		}
		public bool IsInitialized
		{
			get
			{
				return( m_oPeerObject != null );
			}
		}
		public DSSortedList Players
		{
			get
			{
				return( m_oPlayerList );
			}
		}
		public virtual bool IAmTheHost
		{
			get
			{
				return( m_oUs.IAmHost == true );
			}
		}
		public long LocalSocketID
		{
			get
			{
				return( m_oUs.SocketID );
			}
		}
		public Peer Peer
		{
			get
			{
				return( m_oPeerObject );
			}
		}
		public DSNetworkPlayer Me
		{
			get
			{
				return( m_oUs );
			}
		}
		public Address Address
		{
			get
			{
				return( m_oOurAddress );
			}
		}
		public bool SearchingForGames
		{
			get
			{
				return( m_bSearchingForGames );
			}
		}
		public double SecondsBetweenPings
		{
			get
			{
				return( m_nSecondsBetweenPings );
			}
			set
			{
				m_nSecondsBetweenPings = value;
			}
		}
		#endregion
	}

	public class DSNetworkPlayer
	{
		#region Properties
		private string m_sName = "";
		private long m_nSocketID = -1;
		private bool m_bIAmHost = false;
		private bool m_bIsMe = false;
		private double m_nLastPingInSeconds = 0;
		private DateTime m_dtLastPingTime = DateTime.MinValue;
		private long m_nPingStartCounter = 0;
		private long m_nPingStopCounter = 0;
		#endregion

		public DSNetworkPlayer(){}


		#region Properties
		public string Name
		{
			get
			{
				return( m_sName );
			}
			set
			{
				m_sName = value;
			}
		}
		public long SocketID
		{
			get
			{
				return( m_nSocketID );
			}
			set
			{
				m_nSocketID = value;
			}
		}
		public bool IAmHost
		{
			get
			{
				return( m_bIAmHost );
			}
			set
			{
				m_bIAmHost = value;
			}
		}
		public bool IsMe
		{
			get
			{
				return( m_bIsMe );
			}
			set
			{
				m_bIsMe = value;
			}
		}
		public double LastPingInSeconds
		{
			get
			{
				return( m_nLastPingInSeconds );
			}
			set
			{
				m_nLastPingInSeconds = value;
			}
		}
		public DateTime LastPingTime
		{
			get
			{
				return( m_dtLastPingTime );
			}
			set
			{
				m_dtLastPingTime = value;
			}
		}
		public long PingStartCounter
		{
			get
			{
				return( m_nPingStartCounter );
			}
			set
			{
				m_nPingStartCounter = value;
			}
		}
		public long PingStopCounter
		{
			get
			{
				return( m_nPingStopCounter );
			}
			set
			{
				m_nPingStopCounter = value;
			}
		}
		#endregion
	}
}
