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;


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 double m_dFramesSinceLastFPSCalc = 0.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_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

			private DSParticleSystem m_oParticleSystem = null;
			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();
			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 FrameMove;
			public event EventHandler Render;
			public event EventHandler OneTimeSceneInitialization;
			public event EventHandler Dispose;		
			public event EventHandler RenderAfterForms;
			#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 ) );
		}

		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()
		{
			long nCurrentTime = 0;
			long nRenderStart = 0;
			long nRenderStop = 0;
			long nMoveStop = 0;


			RaiseOneTimeSceneInitializationEvent();
			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
				nCurrentTime = DSMisc.GetQueryPerformanceCounter();

				if( m_nLastFPSCalcTime == 0 )
				{
					m_dElapsedTimeSinceLastFPSCalc = (double)( nCurrentTime - m_nAppStartTime )/ (double)m_nTicksPerSecond;
				}
				else
				{
					m_dElapsedTimeSinceLastFPSCalc = (double)( nCurrentTime - m_nLastFPSCalcTime )/ (double)m_nTicksPerSecond;
				}
				m_dAppTime = ( (double)( nCurrentTime - m_nAppStartTime ) ) / (double)m_nTicksPerSecond;
				m_dElapsedTime = m_dAppTime - m_dLastFrameTime;
				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_nLastFPSCalcTime = nCurrentTime;
					m_dFPS = m_dFramesSinceLastFPSCalc / m_dElapsedTimeSinceLastFPSCalc;
					m_dFramesSinceLastFPSCalc = 0.0f;
				}


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

					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();
				}
				else 
				{
					DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,0,"TooFast Render" );
				}

				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();	
				this.SpriteEngine.Begin( Microsoft.DirectX.Direct3D.SpriteFlags.AlphaBlend );				

				if( m_oRenderMode == RenderMode.ThreeD )
				{
					//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 );

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

//Chris
/*				//Render Particle System
				if( m_oRenderMode == RenderMode.ThreeD )
				{						
					m_oParticleSystem.Render3D();
				}
				else if( m_oRenderMode == RenderMode.TwoD )
				{
					m_oParticleSystem.Render2D( WorldViewUpperLeft );
				}*/

				RaiseRenderEvent( m_dElapsedTime,m_dAppTime );
				
				m_dFramesSinceLastFPSCalc++;

				//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.
				RaiseRenderAfterFormsEvent( m_dElapsedTime,m_dAppTime );

				//Render the mouse cursor
				RenderTheMouse();

				this.SpriteEngine.End();
				this.Direct3DDevice.EndScene();

				//11/20/2004 Chris Hill  Added debugging.
				DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,0,"Starting Direct3DDevice.Present" );

				// Show the frame on the primary surface.
				this.Direct3DDevice.Present();
			}
		}
		private void RenderTheMouse()
		{
			string sTemporaryMouseCursorTextureKey = "";
			string sFinalMouseTextureKey = "";
			Viewport oViewPort;
			System.Drawing.Rectangle oCursorLocation;
			LoadedTexture oTexture = null;


			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
				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() );
			}
		}
		
		
		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 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." );
			}
		}
		
		#endregion

		#region Event Handler Functions
		protected void RaiseFrameMoveEvent( double dElapsedTime,double dAppTime )
		{
			DSGameEngineArgs oArgs = null;

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

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

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

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

			if( RenderAfterForms != null )
			{
				oArgs = new DSGameEngineArgs( dElapsedTime,dAppTime );
				RenderAfterForms( this, oArgs );
			}
		}
		protected void RaiseOneTimeSceneInitializationEvent()
		{
			EventArgs oArgs = new EventArgs();

			if( OneTimeSceneInitialization != null )
			{
				OneTimeSceneInitialization( 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 );
			}
		}
		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 );
			}
		}
		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;

			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 ] );
					}
				}
			}
		}



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