using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectPlay;
using Microsoft.DirectX.DirectInput;
using Microsoft.DirectX.DirectSound;


/*
ToDo For Game Engine
--------------------
Add a way for loaded in forms to be removed, but then retreived if you do a show on them
Add font capability to controls
Find out why there is blue at the edge of the horz scroll bar
Reinit mouse if its not inited properly the first time
System is not closing when an exception occurs
And make it so errors are meaningfull if graphics files are not found
Add way to trap enter hit in text box
Fix it so if you type over the length of a text box characters fall off the beginning
Get Ctrl clicks in list boxes to work
Make forms visible
Make forms have move event
Make forms have center on startup property
Make controls use standard names, events and properties
Strip out error handling and replace with new System  
Add a way to shut down the network after its started
Make non-exclusive mouse mode do better with the cursor, then hide our fake cursor
Figure out how to determine the length text will take to display
Make the label control truncte text that is too long
And change color of focus bar if the control isn't the focus
add property for what is Selected to list box control
Add click and key events to change selected item in list box
Add ability to deselect items in list box control
*/
namespace DarkStrideToolbox
{
	public enum RenderMode
	{
		NotSet,
		ThreeD,
		TwoD
	};
	public class DSGameEngineArgs: EventArgs 
	{
		public DSGameEngineArgs( double dElapsedTime,double dAppTime ) 
		{
			this.m_dElapsedTime = dElapsedTime;
			this.m_dAppTime = dAppTime;
		}

		private double m_dElapsedTime;
		private double m_dAppTime;
		public double ElapsedTime
		{
			get
			{
				return( m_dElapsedTime );
			}
		}
		public double AppTime
		{
			get
			{
				return( m_dAppTime );
			}
		}
	}


	public class DSGameEngine : DSGraphicsWrapper
	{
		#region Properties
			#region FPS Functions & Timing API Calls
			private string m_sUserFrameStats = "";
			private double m_nSecondsSpentRendering = 0;
			private double m_nSecondsSpentMoving = 0;

			//This is the number of times we have rendered the screen in one second
			private double m_dFPS = 0.0;
			private long m_nFramesSinceLastFPSCalc = 0;
			private double m_dAppTime = 0.0;				// Current time in seconds
			private double m_dElapsedTime = 0.0;			// Time elapsed since last frame
			private double m_dElapsedTimeSinceLastFPSCalc = 0.0;	// Time elapsed since last frame
			private double m_dLastFrameTime = 0;		// The last time
			//private long m_nAppStartTime = 0;
			//private long m_nLastFPSCalcTime = 0;		// The last time			
			private long m_nLastCounterQuery = 0;		// The last time			
			private long m_nTicksPerSecond = 0;
			private long m_nPolygonsRenderedLastFPSCalc = 0;
			private long m_nPolygonsRenderedForCurrentFPSCalc = 0;
			private long m_nPolygonsRenderedSinceLastFrame = 0;
			private long m_nPolygonsRenderedPerFrame = 0;

			//07/15/2005 Chris Hill  This doesn't appear to be working so turning it off by default for now.
			//11/30/2004 Chris Hill  This is the max frame rate we will allow... roughly.  The best human eye can only handle 72 
			//frames per second, the average only 60.  Their is no sence in the CPU dying from overwork to display 350 FPS.  This
			//is also to help keep the game running smooth as graphics cause the FPS to dip and rise.  The second variable is to 
			//keep track of how much FPS we've accumulated so far.  Keep in mind that with DX timers calling them to get their
			//value also resets them (for get elapsedtime).
			private double m_nMaxFPS = 0.0f;
			#endregion

			#region Font Stuff
			// Interop to call get device caps
			private const int m_cLOGPIXELSY = 90;
			[System.Runtime.InteropServices.DllImport("gdi32.dll")]
			private static extern int GetDeviceCaps(IntPtr hdc, int cap);
			#endregion

			private DSSortedList m_oLoadedStateBlocks = new DSSortedList();

			private DSParticleSystem m_oParticleSystem = null;
			private const string m_cPARTICLESYSTEM_STATEBLOCKKEY = "Particle System State Block Key";
			private DSForms m_oForms = null;
			
			private bool m_bPaused = false;		//11/30/2004 Chris Hill  This changes the advancement of the system not the rendering.  It is very useful for pausing a game.

			//SkyBox
			private bool m_bShowSkyBox = false;
			private DSMesh m_oSkyBox = null;
		
			#region DirectSound Variables
			private DSSoundWrapper m_oSound = null;
			#endregion
			#region DirectInput Variables
			private DSGameEngineInput m_oInput = new DSGameEngineInput();
			//08/17/2006 Chris Hill  Switched to a hardware rendered mouse so this is no longer necessary.
			//private bool m_bShowMouseCursor = true;
			private LoadedTexture m_oMouseCursor_Texture;
			private const string m_cMOUSECURSOR_VERTEXBUFFERKEY = "SYS_MouseCursor_VertexBuffer";
			private Vector2 m_cMOUSECURSORSIZE = new Vector2( 14,14 );
			public const string m_cMOUSECURSORNAME = "System_MouseCursor_";
			#endregion
			#region DirectPlay Variables
			private DSNetworkBuffered m_oNetwork = new DSNetworkBuffered();
			private System.Exception m_oDirectPlayError = null;
			#endregion
			
			private Vector3 m_vWorldTransformView_From;
			private Vector3 m_vWorldTransformView_At;
			private RenderMode m_oRenderMode = RenderMode.NotSet;

			private System.Drawing.Color m_oBackgroundClearColor = System.Drawing.Color.Black;

			//11/20/2004 Chris Hill  This tracks weather or not we are in debug mode.  Note that
			//this is different than compile-debug, this is weather or not we write out a log.
			private bool m_bDebugMode = false;
			private string m_sDebugPath = "";

			#region Event Handlers
			public event EventHandler OneTimeSceneInitialization;
			public event EventHandler SetDeviceStates;
			public event EventHandler FrameMove;
			public event EventHandler Render2DBeforeForms;
			public event EventHandler Render2DAfterForms;
			public event EventHandler Render3D;
			public event EventHandler Dispose;		
			#endregion
		#endregion	


		public DSGameEngine( System.Windows.Forms.Control oForm,RenderMode oMode )
		{
			Constructor( oForm,oMode,false );
		}
		public DSGameEngine( System.Windows.Forms.Control oForm,RenderMode oMode,bool bDebugMode )
		{
			Constructor( oForm,oMode,bDebugMode );
		}
		private void Constructor( System.Windows.Forms.Control oForm,RenderMode oMode,bool bDebugMode )
		{
			long nCurrentTime = 0;


			this.DebugMode = bDebugMode;
			m_oRenderMode = oMode;

			m_nTicksPerSecond = DSMisc.GetQueryPerformanceFrequency();
			nCurrentTime = DSMisc.GetQueryPerformanceCounter();
			//m_nAppStartTime = nCurrentTime;

			//Initialize the graphics engine
			Initialize( oForm );

			//Initialize our sound engine
			m_oSound = new DSSoundWrapper( oForm );
			//Initialize our global resource cache
			DSResourceManager.GetGlobalInstance().OnCreateDevice( Direct3DDevice );
			//Load our embeded resources
			LoadEmbededResourcesFromProject();
			//Initialize our input
			m_oInput.Initialize( this.Form );
			//Initialize our menu system
			m_oForms = new DSForms( m_oInput,this );
			m_oInput.Forms = m_oForms;
			//Initialize our particle system
			m_oParticleSystem = new DSParticleSystem( this.Direct3DDevice );
			//Initialize our network
			m_oNetwork.RegisterDelegate_Error( new DSNetworkWrapper.CallbackError( this.NetworkError ) );
			//Take care of things like resizing so we can handle our changed device
			this.Form.Resize += new EventHandler( this.OnResize );

			//Initialize our mouse cursor message pump
			if( oForm.GetType().BaseType == typeof( DSWinForm ) )
			{
				((DSWinForm)oForm).MessageReceived += new EventHandler( this.OnWndProcMsg );
				SetupTheMouse();
			}
		}

		private void LoadEmbededResourcesFromProject()
		{
			System.Reflection.Assembly oAssembly = null;
			string[] saResourceNames = null;
			string sResKey = "";
			string sResFileName = "";
			string sSourceFolder = "DarkStrideToolbox.Graphics.";
			int nPos = 0;


			oAssembly = System.Reflection.Assembly.GetExecutingAssembly();
			saResourceNames = oAssembly.GetManifestResourceNames();

			//Walk down the list of resources in the graphics directory and load them in
			for( int i=0; i<saResourceNames.Length ; i++ )
			{
				sResFileName = saResourceNames[i];
				if( sResFileName.Length >= sSourceFolder.Length )
				{
					if( sResFileName.Substring( 0,sSourceFolder.Length ).Equals( sSourceFolder ) == true )
					{
						nPos = sResFileName.IndexOf( "Graphics.",0 ) + 1;
						sResKey = sResFileName.Substring( sSourceFolder.Length );
						sResKey = "System_" + sResKey.Substring( 0,sResKey.Length - 4 );

						if( DSMisc.Left( sResKey,m_cMOUSECURSORNAME.Length ).Equals( m_cMOUSECURSORNAME ) == true )
						{		
							DSResourceManager.GetGlobalInstance().LoadTexture( sResKey,sResFileName,System.Drawing.Color.Black,true );
						}
						else
						{
							DSResourceManager.GetGlobalInstance().LoadTexture( sResKey,sResFileName,true );
						}
					}
				}
			}
		}


		public void Run()
		{
			double nTimeToWait  = 0;
			double nNewElapsedTime = 0;
			long nNewCurrentTime = 0;
			long nCurrentTime = 0;
			long nRenderStart = 0;
			long nRenderStop = 0;
			long nMoveStop = 0;


			RaiseOneTimeSceneInitializationEvent();
			RaiseSetDeviceStatesEvent();
			this.TopmostForm.Show();

			//04/01/2005 Chris Hill  Interesting, C# stops processing the line if the first paramater isn't valid.  Handy!
			while( this.TopmostForm != null && this.TopmostForm.Created == true )
			{
				//Calculate our time changes
				m_nLastCounterQuery = nCurrentTime;
				nCurrentTime = DSMisc.GetQueryPerformanceCounter();

				//Don't let the first time check be huge
				if( m_nLastCounterQuery == 0 )
				{
					m_nLastCounterQuery = nCurrentTime;
				}
				nNewElapsedTime = ( (double)( nCurrentTime - m_nLastCounterQuery ) ) / (double)m_nTicksPerSecond;
				m_dElapsedTime += nNewElapsedTime;
				m_dElapsedTimeSinceLastFPSCalc += nNewElapsedTime;

				//We don't add in the first one so that app time starts at 0.
				if( m_nLastCounterQuery != 0 )
				{
					m_dAppTime += nNewElapsedTime;
				}

				//12/06/2006 Chris Hill  Are we over-rendering?  The human eye can only see around 40-60 FPS.
				//So many times we want to slow down the rendering, why waste the CPU.  
				if( m_dElapsedTime < 1.0f / m_nMaxFPS && m_nMaxFPS != 0 )
				{
					DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,0,"Too Fast To Render, ElapsedTime:" + m_dElapsedTime.ToString() );
					//Start by calculating how long we need to wait to hit exactly our FPS rating, then wait
					nTimeToWait = ( ( 1.0f / m_nMaxFPS ) - m_dElapsedTime ) * 1000;
					System.Threading.Thread.Sleep( (int)nTimeToWait );

					//Now update our timing stats to show that we waited.  
					nNewCurrentTime = DSMisc.GetQueryPerformanceCounter();
					nNewElapsedTime = ( (double)( nNewCurrentTime - nCurrentTime ) ) / (double)m_nTicksPerSecond;
					m_dElapsedTime += nNewElapsedTime;
					m_dElapsedTimeSinceLastFPSCalc += nNewElapsedTime;
					m_dAppTime += nNewElapsedTime;
				}

				m_dLastFrameTime = m_dAppTime;
				m_nPolygonsRenderedPerFrame = this.PolygonsRendered - m_nPolygonsRenderedSinceLastFrame;
				m_nPolygonsRenderedSinceLastFrame = this.PolygonsRendered;

				if( m_dElapsedTimeSinceLastFPSCalc >= 1.0f )
				{
					m_nPolygonsRenderedForCurrentFPSCalc = this.PolygonsRendered - m_nPolygonsRenderedLastFPSCalc;
					m_nPolygonsRenderedLastFPSCalc = this.PolygonsRendered;
					m_dFPS = (double)m_nFramesSinceLastFPSCalc / m_dElapsedTimeSinceLastFPSCalc;
					m_dElapsedTimeSinceLastFPSCalc = 0.0;
					m_nFramesSinceLastFPSCalc = 0;
				}

				CheckForNetworkErrors();

				//Track some timing information
				nRenderStart = DSMisc.GetQueryPerformanceCounter();

				//If we are minimized don't render
				if( this.TopmostForm.WindowState != System.Windows.Forms.FormWindowState.Minimized )
				{
					//Render the scene
					OnRender();
				}

				//Track some timing information
				nRenderStop = DSMisc.GetQueryPerformanceCounter();

				// Frame move the scene
				OnFrameMove( m_dElapsedTime,m_dAppTime );
				RaiseFrameMoveEvent( m_dElapsedTime,m_dAppTime );

				//Track some timing information
				nMoveStop = DSMisc.GetQueryPerformanceCounter();

				m_dElapsedTime = 0;


				//Yield some CPU time to other processes
				//If we aren't minimized still render but do so more slowly.
				if( this.TopmostForm.WindowState == System.Windows.Forms.FormWindowState.Minimized )
				{
					System.Threading.Thread.Sleep( 100 );
				}
				else if( this.HasFocus == false || System.Windows.Forms.Form.ActiveForm != this.TopmostForm )
				{
					System.Threading.Thread.Sleep( 10 );
				}
				System.Windows.Forms.Application.DoEvents();


				//Calculate our timing stats
				m_nSecondsSpentRendering = ( ( (double)nRenderStop - (double)nRenderStart ) / (double)m_nTicksPerSecond );
				m_nSecondsSpentMoving = ( ( (double)nMoveStop - (double)nRenderStop ) / (double)m_nTicksPerSecond );
			}

			m_oInput.Dispose();
			m_oNetwork.Dispose();
			RaiseDisposeEvent();
		}
			

		private void OnRender()
		{
			Matrix matWorld;
			Vector3 vFrom;
			Vector3 vMouse_RayPickOrig = new Vector3();
			Vector3 vMouse_RayPick = new Vector3();			
			float fAspect = 0;
			Viewport oViewPort;


			//11/20/2004 Chris Hill  Added debugging.
			DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,0,"Starting Render3DEnvironment( Time:" + m_dAppTime.ToString() + " Elapsed:" + m_dElapsedTime.ToString() + " )" );

			//Find out if we can render the scene
			if( this.Direct3DDevice != null && 
				this.TopmostForm.WindowState != FormWindowState.Minimized || 
				this.Form.Visible == true )
			{
				//Render the scene as normal
				if( this.Direct3DDevice.Viewport.X != 0 || this.Direct3DDevice.Viewport.Y != 0 ||
					this.Direct3DDevice.Viewport.Width != this.Form.ClientSize.Width || 
					this.Direct3DDevice.Viewport.Height != this.Form.ClientSize.Height )
				{
					//We are creating a viewport so we can be sure that our mouse cursor is on top
					oViewPort = this.Direct3DDevice.Viewport;
					oViewPort.X = 0;
					oViewPort.Y = 0;
					oViewPort.MinZ = 0;
					oViewPort.MaxZ = 0;
					oViewPort.Width = this.Form.ClientSize.Width;
					oViewPort.Height = this.Form.ClientSize.Height;
					this.Direct3DDevice.Viewport = oViewPort;
				}

				this.Direct3DDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, m_oBackgroundClearColor, 1.0f, 0);
				this.Direct3DDevice.BeginScene();	


				///////////////////////////////////////////////////////////////////////////////////////////
				//Start by rendering the 2D portion of the world
				///////////////////////////////////////////////////////////////////////////////////////////
				this.SpriteEngine.Begin( Microsoft.DirectX.Direct3D.SpriteFlags.AlphaBlend );				

					RaiseRender2DBeforeFormsEvent( m_dElapsedTime,m_dAppTime );
					
					//07/29/2005 Chris Hill  Render the forms after the user draws things, that way they will 
					//be on top of any backgrounds he wants.
					m_oForms.Render();

					//08/09/2005 Chris Hill  There are times we want to draw something on top of the forms, in
					//order to acomplish this we need a second event.
					RaiseRender2DAfterFormsEvent( m_dElapsedTime,m_dAppTime );

					if( m_oRenderMode == RenderMode.TwoD )
					{
						//m_oParticleSystem.Render2D( WorldViewUpperLeft );
					}

					//08/16/2006 Chris Hill  I am now using a hardware rendered mouse, so this isn't needed.
					//Render the mouse cursor
					//RenderTheMouse();

				this.SpriteEngine.End();
				///////////////////////////////////////////////////////////////////////////////////////////

				///////////////////////////////////////////////////////////////////////////////////////////
				//Rendering the 3D portion of the world
				///////////////////////////////////////////////////////////////////////////////////////////
				//SkyBox
				if( m_bShowSkyBox == true )
				{
					//For the projection matrix, we set up a perspective transform (which transforms geometry from 3D view space to 2D viewport space, with
					//a perspective divide making objects smaller in the distance). To build a perpsective transform, we need the field of view (1/4 pi is common),
					//the aspect ratio, and the near and far clipping planes (which define at what distances geometry should be no longer be rendered).
					fAspect = (float)this.Direct3DDevice.PresentationParameters.BackBufferWidth / (float)this.Direct3DDevice.PresentationParameters.BackBufferHeight;
					this.Direct3DDevice.Transform.Projection = Matrix.PerspectiveFovLH((float)(Math.PI/4), fAspect, 1.0f, 100.0f );

					//Get our from vector just in case we need it
					vFrom = DSMath.FromVectorFromALHViewMatrix( this.Direct3DDevice.Transform.View );

					matWorld = Matrix.Translation( (float)vFrom.X, (float)vFrom.Y, (float)vFrom.Z );
					DSResourceManager.GetGlobalInstance().GetMesh( "SkyBox" ).Render( matWorld );
				}

				RaiseRender3DEvent( m_dElapsedTime,m_dAppTime );
				
				//Render Particle System
				/*CaptureStateBlock( m_cPARTICLESYSTEM_STATEBLOCKKEY );
				if( m_oRenderMode == RenderMode.ThreeD )
				{						
					m_oParticleSystem.Render3D();
				}
				ApplyStateBlock( m_cPARTICLESYSTEM_STATEBLOCKKEY );*/
				///////////////////////////////////////////////////////////////////////////////////////////


				this.Direct3DDevice.EndScene();
				this.Direct3DDevice.Present();

				//11/20/2004 Chris Hill  Added debugging.
				DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,0,"Starting Direct3DDevice.Present" );
				//State update
				m_nFramesSinceLastFPSCalc++;
			}
		}
		private void OnResize( object sender, EventArgs e )
        {
			StateBlock oLoopStateBlock = null;


			//Go through all our state blocks and blow them away
			for( int i=0 ; i<m_oLoadedStateBlocks.Count ; i++ )
			{
				oLoopStateBlock = (StateBlock)m_oLoadedStateBlocks.GetByIndex( i );
				oLoopStateBlock.Dispose();
				oLoopStateBlock = null;
			}
			m_oLoadedStateBlocks = new DSSortedList();

			this.Direct3DDevice.Reset( this.Direct3DDevice.PresentationParameters );
			DSResourceManager.GetGlobalInstance().OnRestore3DDeviceObjects();

			RaiseSetDeviceStatesEvent();
		}
		private void SetupTheMouse()
		{
			string sTemporaryMouseCursorTextureKey = "";
			string sFinalMouseTextureKey = "";
			Microsoft.DirectX.Direct3D.Surface oSurface = null;
			//Viewport oViewPort;
			//System.Drawing.Rectangle oCursorLocation;
			LoadedTexture oTexture = null;


			//08/17/2006 Chris Hill  Switched to a hardware rendered mouse so this is no longer necessary.
			/*if( m_bShowMouseCursor == true )
			{
				//Do we have a standard screen sized viewport?  if not fix that.  
				if( this.Direct3DDevice.Viewport.X != 0 || this.Direct3DDevice.Viewport.Y != 0 ||
					this.Direct3DDevice.Viewport.Width != this.Form.ClientSize.Width || 
					this.Direct3DDevice.Viewport.Height != this.Form.ClientSize.Height )
				{
					//We are creating a viewport so we can be sure that our mouse cursor is on top
					oViewPort = this.Direct3DDevice.Viewport;
					oViewPort.X = 0;
					oViewPort.Y = 0;
					oViewPort.MinZ = 0;
					oViewPort.MaxZ = 0;
					oViewPort.Width = this.Form.ClientSize.Width;
					oViewPort.Height = this.Form.ClientSize.Height;
					this.Direct3DDevice.Viewport = oViewPort;
				}*/

				//Find out if the DSForm system wants to change the mouse cursor
				sTemporaryMouseCursorTextureKey = m_oForms.GetCurrentCursorTextureKey( this.DirectInput.MouseCursor );
				if( sTemporaryMouseCursorTextureKey != null && sTemporaryMouseCursorTextureKey.Length == 0 )
				{
					sTemporaryMouseCursorTextureKey = null;
				}

				//Do we have a temporary mouse cursor?  If so use that
				if( sTemporaryMouseCursorTextureKey != null )
				{
					sFinalMouseTextureKey = sTemporaryMouseCursorTextureKey;
				}
				//Do we have a preset texture?
				else if( m_oMouseCursor_Texture != null )
				{
					sFinalMouseTextureKey = m_oMouseCursor_Texture.sFileName;
				}
				//Otherwise use the built in mouse cursor
				else
				{
					sFinalMouseTextureKey = "";//System_MouseCursor_Hand";
				}

            	//Finally draw our mouse
				if( sFinalMouseTextureKey.Length > 0 )
				{
					oTexture = DSResourceManager.GetGlobalInstance().GetLoadedTexture( sFinalMouseTextureKey );
					/*oCursorLocation = new System.Drawing.Rectangle( (int)MouseCursor.X, (int)MouseCursor.Y,
																	(int)oTexture.Size.X, (int)oTexture.Size.Y );
					RenderTexture2D( sFinalMouseTextureKey, System.Drawing.Rectangle.Empty, oCursorLocation, 
									new Vector2(), 0, 0, false, System.Drawing.Color.White.ToArgb() );*/

					oSurface = Surface.FromStream( this.Direct3DDevice,oTexture.oStream,Pool.Default );
					//oSurface = Surface.FromBitmap( this.Direct3DDevice, bm, Pool.Default ); 
					this.Direct3DDevice.SetCursorProperties( 0,0,oSurface ); 
					this.Direct3DDevice.ShowCursor( true ); 

					oSurface.ReleaseGraphics();
					oSurface.Dispose();
				}
			//}
		}
		public void OnWndProcMsg( object sender, EventArgs e )
		{
			DSMessageArgs oArgs = (DSMessageArgs)e;


			if( oArgs.Msg == DSNativeMethods.WindowMessage.MouseMove ||
				oArgs.Msg == DSNativeMethods.WindowMessage.SetCursor )
			{
				this.Direct3DDevice.ShowCursor( true );
			}
		}

				
		private void OnFrameMove( double dElapsedTime,double dAppTime )
		{
			//11/20/2004 Chris Hill  Added debugging.
			DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,1,"Starting FrameMove( Time:" + dAppTime.ToString() + " Elapsed:" + dElapsedTime.ToString() + " )" );

			//11/20/2004 Chris Hill  Added debugging.
			DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,2,"Starting m_oInput.ProccessBufferedEvents" );
			m_oInput.ProccessBufferedEvents();
			//05/24/2006 Chris Hill  Get the network events.
			m_oNetwork.ProccessBufferedEvents();

			//11/30/2004 Chris Hill  If the scene is paused we don't advance but we do render.
			//This is neccessary because the buffered key events may have changed our status
			if( this.HasFocus == true && this.Paused == false )
			{
				//11/20/2004 Chris Hill  Added debugging.
				DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,2,"Starting m_oParticleSystem.Advance" );

				m_oParticleSystem.Advance( dElapsedTime );
			}

			//11/20/2004 Chris Hill  Added debugging.
			DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,2,"Starting m_odDelegate_FrameMove" );

			m_oForms.FrameMove( dElapsedTime,dAppTime );

			//11/20/2004 Chris Hill  Added debugging.
			DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,1,"Stopping FrameMove" );
		}



		#region State Block Functions
		public StateBlock CaptureStateBlock( string sKey )
		{
			StateBlock oStateBlock = null;

			
			oStateBlock = (StateBlock)m_oLoadedStateBlocks.GetByKey( sKey );
			if( oStateBlock == null )
			{
				oStateBlock = new StateBlock( this.Direct3DDevice,Microsoft.DirectX.Direct3D.StateBlockType.All );
				m_oLoadedStateBlocks.Add( sKey,oStateBlock );
			}
			oStateBlock.Capture(); 


			return( oStateBlock );
		}
		public void ApplyStateBlock( string sKey )
		{
			StateBlock oStateBlock = null;

			
			oStateBlock = (StateBlock)m_oLoadedStateBlocks.GetByKey( sKey );
			if( oStateBlock == null )
			{
				throw new System.Exception( "StateBlock with key of <" + sKey + "> not found." );
			}
			oStateBlock.Apply(); 
		}
		#endregion

		#region DirectPlay Functions
		private void NetworkError( System.Exception oError )
		{
			m_oDirectPlayError = DSMisc.MergeErrors( oError,m_oDirectPlayError );
		}

		private void CheckForNetworkErrors()
		{
			System.Exception oErrors = null;


			//Throw any errors that may have occured.
			if( m_oDirectPlayError != null )
			{
				lock( m_oDirectPlayError )
				{
					oErrors = m_oDirectPlayError;
					m_oDirectPlayError = null;
				}
				if( oErrors != null )
				{
					throw new System.Exception( "DirectPlay Threw an error",oErrors );
				}
			}
		}
		#endregion
		
		#region Render 3D/2D Functions
		public void Set3DCameraTransformView( Vector3 vFrom,Vector3 vAt,Vector3 vUp )
		{
			Matrix matWorld;

			
			matWorld = Matrix.LookAtLH( vFrom, vAt, vUp );

			//Save these values so we can billboard our heads up display
			m_vWorldTransformView_From = vFrom;
			m_vWorldTransformView_At = vAt;

			this.Direct3DDevice.Transform.View = matWorld;
		}
		public void SetSkyBox( string sMeshKey )
		{
			m_bShowSkyBox = true;

			m_oSkyBox = DSResourceManager.GetGlobalInstance().GetMesh( sMeshKey );
			if( m_oSkyBox == null )
			{
				throw new System.Exception( "Unable to find DSMesh <" + sMeshKey + "> in internal DSMesh collection." );
			}
		}
		

		public int GetFontSize( int nFontSize )
		{
			int nHeight = 0;
			int nLogPixelsY = 0;
			System.IntPtr nDC = System.IntPtr.Zero; 
			System.Drawing.Graphics oGraphics = null;


			//Initialize all of the fonts
			oGraphics = this.Form.CreateGraphics();
			nDC = oGraphics.GetHdc();

			nLogPixelsY = GetDeviceCaps( nDC,m_cLOGPIXELSY );

			oGraphics.ReleaseHdc( nDC );

			nHeight = -nFontSize * nLogPixelsY / 72;


			return( nHeight );
		}
		#endregion

		#region Event Handler Functions
		protected void RaiseOneTimeSceneInitializationEvent()
		{
			EventArgs oArgs = new EventArgs();

			if( OneTimeSceneInitialization != null )
			{
				OneTimeSceneInitialization( this,oArgs );
			}
		}
		protected void RaiseSetDeviceStatesEvent()
		{
			EventArgs oArgs = new EventArgs();

			if( SetDeviceStates != null )
			{
				SetDeviceStates( this,oArgs );
			}
		}
		protected void RaiseFrameMoveEvent( double dElapsedTime,double dAppTime )
		{
			DSGameEngineArgs oArgs = null;

			if( FrameMove != null )
			{
				oArgs = new DSGameEngineArgs( dElapsedTime,dAppTime );
				FrameMove( this, oArgs );
			}
		}

		protected void RaiseRender2DBeforeFormsEvent( double dElapsedTime,double dAppTime )
		{
			DSGameEngineArgs oArgs = null;

			if( Render2DBeforeForms != null )
			{
				oArgs = new DSGameEngineArgs( dElapsedTime,dAppTime );
				Render2DBeforeForms( this, oArgs );
			}
		}

		protected void RaiseRender2DAfterFormsEvent( double dElapsedTime,double dAppTime )
		{
			DSGameEngineArgs oArgs = null;

			if( Render2DAfterForms != null )
			{
				oArgs = new DSGameEngineArgs( dElapsedTime,dAppTime );
				Render2DAfterForms( this, oArgs );
			}
		}

		protected void RaiseRender3DEvent( double dElapsedTime,double dAppTime )
		{
			DSGameEngineArgs oArgs = null;

			if( Render3D != null )
			{
				oArgs = new DSGameEngineArgs( dElapsedTime,dAppTime );
				Render3D( this, oArgs );
			}
		}

		protected void RaiseDisposeEvent()
		{
			EventArgs oArgs = new EventArgs();

			if( Dispose != null )
			{
				Dispose( this,oArgs );
			}
		}

		#endregion


		#region Properties
		public DSParticleSystem ParticleSystem
		{
			get
			{
				return( m_oParticleSystem );
			}
		}

		public bool MouseInExclusiveMode
		{
			get
			{
				return( m_oInput.MouseInExclusiveMode );
			}
			set
			{
				m_oInput.MouseInExclusiveMode = value;
				m_oInput.UseAbsoluteMouseCoordinates = !m_oInput.MouseInExclusiveMode;
			}
		}
		public Vector3 CameraFrom
		{
			get
			{
				return( m_vWorldTransformView_From );
			}
		}
		public Vector3 CameraAt
		{
			get
			{
				return( m_vWorldTransformView_At );
			}
		}
		public DSForms Forms
		{
			get
			{
				return( m_oForms );
			}
		}
		//08/17/2006 Chris Hill  Switched to a hardware rendered mouse so this is no longer necessary.
		/*public bool ShowMouseCursor
		{
			get
			{
				return( m_bShowMouseCursor );
			}
			set
			{
				m_bShowMouseCursor = value;
			}
		}*/
		public Vector2 MouseCursor
		{
			get
			{
				return( m_oInput.MouseCursor );
			}
			set
			{
				m_oInput.MouseCursor = value;
			}
		}
		public bool[] MouseButtonState
		{
			get
			{
				return( m_oInput.MouseButtonState );
			}
		}
		public DSGameEngineInput DirectInput
		{
			get
			{
				return( m_oInput );
			}
		}
		public DSNetworkBuffered DirectPlay
		{
			get
			{
				return( m_oNetwork );
			}
			set
			{
				m_oNetwork = value;
			}
		}
		public RenderMode RenderMode
		{
			get
			{
				return( m_oRenderMode );
			}
			set
			{
				m_oRenderMode = value;
			}
		}
		public System.Drawing.Color BackgroundClearColor
		{
			set
			{
				m_oBackgroundClearColor = value;
			}
			get
			{
				return( m_oBackgroundClearColor );
			}
		}
		//11/20/2004 Chris Hill  Added a debug mode for tracking a time problem.
		public bool DebugMode
		{
			set
			{
				//Are we turning it on when it was off?
				if( m_bDebugMode == false && value == true )
				{
					//Only make a new log file name if we don't have one already
					if( m_sDebugPath.Length == 0 )
					{
						m_sDebugPath =	DSMisc.GetDevelopmentAppPath() + 
							"DSGameEngine_DebugFile_" + 
							DateTime.Now.ToString( "yyyy-MM-dd hh" ) + "h" + 
							DateTime.Now.ToString( "mm" ) + "m" + 
							DateTime.Now.ToString( "ss" ) + "s" + 
							".OUT";

						//Then write a header
						DSMisc.WriteToDebugFile( m_sDebugPath,value,false,0,
							"\r\n\r\nStarting Debug Log File For DarkStrideEngine\r\n" +
							"--------------------------------------------" );
					}
				}
					//Are we turning it off when it was currently on?  Then clear the path.
				else if( m_bDebugMode == true && value == false )
				{
					m_sDebugPath = "";
				}

				m_bDebugMode = value;


				//12/29/2004 Chris Hill  Make sure to turn debug on only if those objects have been inited.
				//Now turn it on for all of our child objects
				if( m_oParticleSystem != null )
				{
					m_oParticleSystem.DebugPath = m_sDebugPath;
					m_oParticleSystem.DebugMode = value;
				}
				if( m_oInput != null )
				{
					m_oInput.DebugPath = m_sDebugPath;
					m_oInput.DebugMode = value;
				}
			}
			get
			{
				return( m_bDebugMode );
			}
		}
		public string DebugPath
		{
			get
			{
				return( m_sDebugPath );
			}
			set
			{
				m_sDebugPath = value;

				//We turn off then on the debug mode so that it writes a new header to the log
				if( m_bDebugMode == true )
				{
					DebugMode = false;
					DebugMode = true;
				}				
			}
		}
		//11/30/2004 Chris Hill  This is the max frame rate we will allow... roughly.  The best human eye can only handle 72 
		//frames per second, the average only 60.  Their is no sence in the CPU dying from overwork to display 350 FPS.  This
		//is also to help keep the game running smooth as graphics cause the FPS to dip and rise.
		public double MaxFPS
		{
			get
			{
				return( m_nMaxFPS );
			}
			set
			{
				m_nMaxFPS = value;
			}
		}
		public string MouseTextureKey
		{
			get
			{
				return( m_oMouseCursor_Texture.sFileName );
			}
			set
			{
				m_oMouseCursor_Texture = DSResourceManager.GetGlobalInstance().GetLoadedTexture( value );
				SetupTheMouse();
			}
		}
		public bool Paused
		{
			get
			{
				return( m_bPaused );
			}
			set
			{
				m_bPaused = value;
			}
		}


		public string FrameStats
		{
			get
			{
				string sFrameStats = 
					"FPS: " + m_dFPS.ToString("f2") + 
					"\nResolution: " + this.BackBufferSize + 
					"\nPolygons per Frame: " + m_nPolygonsRenderedPerFrame.ToString() +
					"\nRender Time: " + m_nSecondsSpentRendering.ToString("f4") + "s" +
					"\nMove Time: " + m_nSecondsSpentMoving.ToString("f4") + "s" + 
					m_sUserFrameStats;

				return( sFrameStats );
			}
		}
		public string UserFrameStats
		{
			get
			{
				return( m_sUserFrameStats );
			}
			set
			{
				m_sUserFrameStats = value;
			}
		}
		public double AppTime
		{
			get
			{
				return( m_dAppTime );
			}
		}
		public double ElapsedTime
		{
			get
			{
				return( m_dElapsedTime );
			}
		}
		public long PolygonsRenderedPerFrame
		{
			get
			{
				return( m_nPolygonsRenderedPerFrame );
			}
		}
		public long PolygonsRenderedPerSecond
		{
			get
			{
				return( m_nPolygonsRenderedForCurrentFPSCalc );
			}
		}

		public DSSoundWrapper DirectSound
		{
			get
			{
				return( m_oSound );
			}
		}
		#endregion
	}


	public class DSGameEngineInput : DSInputBuffered
	{
		#region Properties
		private DSForms m_oForms = null;
		#endregion


		public DSGameEngineInput(){}

		public override void ProccessBufferedEvents()
		{
			BufferedInputEvent oEvent = null;

			try
			{
				if( m_oForms == null )
				{
					throw new System.Exception( "GameEngineInput does not have a 'Forms' instance to process for." );
				}

				while( base.Events.Count > 0 )
				{
					oEvent = (BufferedInputEvent)base.Events[ 0 ];
					base.Events.RemoveAt( 0 );

					if( oEvent.nEvent == BufferedInputEventTypes.enumMouseWheelMove )
					{
						if( m_oForms.OnMouseWheelMove( (long)oEvent.oVariables[ 0 ] ) == false )
						{
							base.ExecuteMouseWheelMove( (long)oEvent.oVariables[ 0 ] );
						}
					}
					else if( oEvent.nEvent == BufferedInputEventTypes.enumMouseMoved )
					{
						if( m_oForms.OnMouseMove( (Vector2)oEvent.oVariables[ 0 ],(MouseState)oEvent.oVariables[ 1 ],(bool[])oEvent.oVariables[ 2 ] ) == false )
						{
							base.ExecuteMouseMoved( (Vector2)oEvent.oVariables[ 0 ],(MouseState)oEvent.oVariables[ 1 ],(bool[])oEvent.oVariables[ 2 ] );
						}
					}
					else if( oEvent.nEvent == BufferedInputEventTypes.enumMouseUp )
					{
						if( m_oForms.OnMouseUp( (Vector2)oEvent.oVariables[ 0 ],(long)oEvent.oVariables[ 1 ] ) == false )
						{
							base.ExecuteMouseUp( (Vector2)oEvent.oVariables[ 0 ],(long)oEvent.oVariables[ 1 ] );
						}
					}
					else if( oEvent.nEvent == BufferedInputEventTypes.enumMouseDown )
					{
						if( m_oForms.OnMouseDown( (Vector2)oEvent.oVariables[ 0 ],(long)oEvent.oVariables[ 1 ] ) == false )
						{
							base.ExecuteMouseDown( (Vector2)oEvent.oVariables[ 0 ],(long)oEvent.oVariables[ 1 ] );
						}
					}
					else if( oEvent.nEvent == BufferedInputEventTypes.enumKeyboardKeyDown )
					{
						if( m_oForms.OnKeyDown( (Key)oEvent.oVariables[ 0 ],(bool[])oEvent.oVariables[ 1 ] ) == false )
						{
							base.ExecuteKeyboardKeyDown( (Key)oEvent.oVariables[ 0 ],(bool[])oEvent.oVariables[ 1 ] );
						}
					}
					else if( oEvent.nEvent == BufferedInputEventTypes.enumKeyboardKeyUp )
					{
						if( m_oForms.OnKeyUp( (Key)oEvent.oVariables[ 0 ],(bool[])oEvent.oVariables[ 1 ] ) == false )
						{
							base.ExecuteKeyboardKeyUp( (Key)oEvent.oVariables[ 0 ],(bool[])oEvent.oVariables[ 1 ] );
						}
					}
				}
			}
			catch( System.Exception oEx )
			{
				DSMisc.ShowErrors( oEx );
			}
		}



		#region Properties
		public DSForms Forms
		{
			get
			{
				return( m_oForms );
			}
			set
			{
				m_oForms = value;
			}
		}
		#endregion
	}
}