using System;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;


using System.Windows.Forms;
namespace DarkStrideToolbox
{
	public class DSParticleSystem
	{
		#region Properties
		private Device m_oDirect3DDevice = null;
		private DSSortedList m_oParticleManagers = new DSSortedList();

		//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 = "";
		#endregion


		public DSParticleSystem( Device oDevice )
		{
			m_oDirect3DDevice = oDevice;
		}

		public void RestoreDeviceObjects( Device oDevice )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.RestoreDeviceObjects";

			try
			{
				for( int i=m_oParticleManagers.Count-1 ; i>=0 ; i--)
				{
					DSParticleManager oManager = (DSParticleManager)m_oParticleManagers.GetByIndex( i );
					oManager.RestoreDeviceObjects( oDevice );
				}

				m_oDirect3DDevice = oDevice;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public void Advance( double dSecsPerFrame )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.Advance";
			DSParticleManager oManager = null;

			try
			{
				for( int i=m_oParticleManagers.Count-1 ; i>=0 ; i--)
				{
					oManager = (DSParticleManager)m_oParticleManagers.GetByIndex( i );
					oManager.Advance( dSecsPerFrame );

					if( oManager.RunDown == true && oManager.ParticleCount == 0 )
					{
						m_oParticleManagers.RemoveAt( i );
					}
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		public void Render3D()
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.Render3D";

			try
			{
				m_oDirect3DDevice.RenderState.ZBufferWriteEnable = false;
				m_oDirect3DDevice.RenderState.AlphaBlendEnable = true;
				m_oDirect3DDevice.RenderState.SourceBlend = Blend.One;
				m_oDirect3DDevice.RenderState.DestinationBlend = Blend.One;


				/*// Set the render states for using point sprites
				m_oDirect3DDevice.RenderState.PointSpriteEnable = true;
				m_oDirect3DDevice.RenderState.PointScaleEnable = true ;
				m_oDirect3DDevice.RenderState.PointSize = 0.08f;
				m_oDirect3DDevice.RenderState.PointSizeMin = 0.00f;
				m_oDirect3DDevice.RenderState.PointScaleA = 0.00f;
				m_oDirect3DDevice.RenderState.PointScaleB = 0.00f;
				m_oDirect3DDevice.RenderState.PointScaleC = 1.00f;*/

				

				m_oDirect3DDevice.RenderState.Lighting = false;

				for( int i=m_oParticleManagers.Count-1 ; i>=0 ; i--)
				{
					DSParticleManager oManager = (DSParticleManager)m_oParticleManagers.GetByIndex( i );
					oManager.Render3D( m_oDirect3DDevice );
				}

				m_oDirect3DDevice.RenderState.Lighting = true;

				// Reset render states
				m_oDirect3DDevice.RenderState.PointSpriteEnable = false;
				m_oDirect3DDevice.RenderState.PointScaleEnable = false;


				m_oDirect3DDevice.RenderState.ZBufferWriteEnable = true;
				m_oDirect3DDevice.RenderState.AlphaBlendEnable = false;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public void Render2D( Vector2 oViewUpperLeft )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.Render2D";

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

				m_oDirect3DDevice.RenderState.Lighting = false;

				for( int i=m_oParticleManagers.Count-1 ; i>=0 ; i--)
				{
					DSParticleManager oManager = (DSParticleManager)m_oParticleManagers.GetByIndex( i );
					oManager.Render2D( m_oDirect3DDevice,oViewUpperLeft  );
				}

				m_oDirect3DDevice.RenderState.Lighting = true;

				// Reset render states
				m_oDirect3DDevice.RenderState.PointSpriteEnable = false;
				m_oDirect3DDevice.RenderState.PointScaleEnable = false;

				m_oDirect3DDevice.RenderState.ZBufferWriteEnable = true;
				m_oDirect3DDevice.RenderState.AlphaBlendEnable = false;


				//11/20/2004 Chris Hill  Added debugging.
				DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,2,
					"Stoping ParticleSystem.Render2D" );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		public DSParticleManager GetParticleManager( string sGUID )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.GetParticleManager";
			DSParticleManager oManager = null;
			bool bFound = false;

			try
			{
				for( int i=0 ; i<m_oParticleManagers.Count ; i++ )
				{
					oManager = (DSParticleManager)m_oParticleManagers.GetByIndex(i);
					if( oManager.GUID.Equals( sGUID ) == true )
					{
						bFound = true;
						break;
					}
				}

				if( bFound == false )
				{
					oManager = null;
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oManager );
		}


		public DSParticleManager CreateParticleSystem()
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.CreateParticleSystem";
			DSParticleManager oNewManager = null;

			try
			{
				oNewManager = new DSParticleManager( m_oDirect3DDevice,512,2048 );
				m_oParticleManagers.Add( oNewManager.GUID,oNewManager );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oNewManager );
		}
		public DSParticleManager CreateParticleSystem( System.Drawing.Color clrEmitColor,System.Drawing.Color clrFadeColor, 
														float fEmitVel,int NumParticlesToEmit, Vector3 vEmitterPosition,
														Vector3 vEmitterDirection )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.CreateParticleSystem";
			DSParticleManager oNewManager = null;

			try
			{
				oNewManager = new DSParticleManager( m_oDirect3DDevice,512,2048,clrEmitColor,clrFadeColor,fEmitVel, NumParticlesToEmit,  vEmitterPosition, vEmitterDirection );
				oNewManager.ParticleSystem = this;
				m_oParticleManagers.Add( oNewManager.GUID,oNewManager );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oNewManager );
		}
		public DSParticleManager CreateParticleSystem( System.Drawing.Color clrEmitColor,System.Drawing.Color clrFadeColor, 
														float fEmitVel,int NumParticlesToEmit, Vector3 vEmitterPosition,
														Vector3 vEmitterDirection,string sTexture )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.CreateParticleSystem";
			DSParticleManager oNewManager = null;

			try
			{
				oNewManager = new DSParticleManager( m_oDirect3DDevice,512,2048,clrEmitColor,clrFadeColor,fEmitVel, NumParticlesToEmit,  vEmitterPosition, vEmitterDirection );
				oNewManager.ParticleSystem = this;
				oNewManager.ParticleTexture = sTexture;
				m_oParticleManagers.Add( oNewManager.GUID,oNewManager );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oNewManager );
		}

		public DSParticleManager CreateParticleSystem( DSParticleManager oParticleManager )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.CreateParticleSystem";

			try
			{
				oParticleManager.ParticleSystem = this;
				oParticleManager.RestoreDeviceObjects( m_oDirect3DDevice );
				m_oParticleManagers.Add( oParticleManager.GUID,oParticleManager );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oParticleManager );
		}


		public virtual void Dispose()
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleSystem.Dispose";

			try
			{
				m_oDirect3DDevice = null;

				m_oParticleManagers = null;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}



		#region Properties
		public Device Device
		{
			get
			{
				return( m_oDirect3DDevice );
			}
			set
			{
				m_oDirect3DDevice = value;
			}
		}
		//11/20/2004 Chris Hill  Added a debug mode for tracking a time problem.
		public bool DebugMode
		{
			set
			{
				DSParticleManager oManager = null;


				//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() + 
							"DSParticleSystem_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 DSParticleSystem\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;


				//Now turn it on for all of our child objects
				for( int i=0 ; i<m_oParticleManagers.Count ; i++ )
				{
					oManager = (DSParticleManager)m_oParticleManagers.GetByIndex( i );
					oManager.DebugPath = m_sDebugPath;
					oManager.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;
				}				
			}
		}
		#endregion
	}


	public class DSParticleManager
	{
		#region Properties
		//Movement Properties
		private Vector3 m_vGravity = new Vector3( 0,0,0 );
		private Vector3 m_vEmitterPosition = new Vector3( 0,0,0 );
		private Vector3 m_vEmitterDirection = new Vector3( 0,0,0 );
		private double m_nEmitterRangeInRad = Math.PI;
		//Particle Properties
		private double m_dParticleLifeTimeInSeconds = 0;
		//General Properties
		private string m_sGUID = "";
		private double m_dCurrentTime = 0.0f;
		//Appearance Properties
		private float m_fParticleSize = 0.8f;
		private System.Drawing.Color m_clrEmitColor;
		private System.Drawing.Color m_clrFadeColor;

		private bool m_bEmitParticles = true;
		private bool m_bRunDown = false;
		//11/13/2003 Chris Hill  If its a one shot PM then after its first shot at emitting it switches to a run down PM.
		private bool m_bOneShot = false;

		private float m_fEmitVel = 0;
		private double m_nNumParticlesToEmit = 0;
		private double m_nNumPartialParticlesEmmitedLastClick = 0;

		//11/30/2004 Chris Hill  This is the absolute max number of particles to emit.  The previous variable is how many to
		//emit per second.  After this many particles is admitted the PM goes into run-down mode automatically.  If this is a
		//zero then their is no limit to the number of particles emmited.
		private long m_nMaxNumParticlesToEmit = 0;
		private long m_nNumParticlesEmmited = 0;
	
		public struct PointVertex
		{
			public Vector3 v;
			public int color;
			public static readonly VertexFormats Format =  VertexFormats.Position | VertexFormats.Diffuse;
		};

		private int baseParticle = 0;
		private int flush = 0;
		private int discard = 0;

		private int particles = 0;
		private int m_nParticlesLimit = 2048;
		private System.Collections.ArrayList particlesList = new System.Collections.ArrayList();
		private System.Collections.ArrayList freeParticles = new System.Collections.ArrayList();

		private System.Random rand = new System.Random();

		// Geometry
		private VertexBuffer vertexBuffer = null;

		//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 = "";

		private DSParticleSystem m_oParticleSystem = null;

		private string m_sParticleTexture = "";
		#endregion


		public DSParticleManager()
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.DSParticleManager";

			try
			{
				flush			= 512;
				discard			= 2048;
				baseParticle	= discard;

				particles		= 0;

				m_sGUID = DSMisc.GetGUID( "ParticleManager" );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public DSParticleManager( Device oDevice,int numFlush, int numDiscard )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.DSParticleManager";

			try
			{
				baseParticle	= numDiscard;
				flush			= numFlush;
				discard			= numDiscard;

				particles		= 0;

				m_sGUID = DSMisc.GetGUID( "ParticleManager" );

				RestoreDeviceObjects( oDevice );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public DSParticleManager( Device oDevice,int numFlush, int numDiscard,System.Drawing.Color clrEmitColor,
									System.Drawing.Color clrFadeColor, float fEmitVel,
									double NumParticlesToEmit, Vector3 vEmitterPosition,Vector3 vEmitterDirection)
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.DSParticleManager";

			try
			{
				baseParticle	= numDiscard;
				flush			= numFlush;
				discard			= numDiscard;

				particles		= 0;


				m_clrEmitColor = clrEmitColor;
				m_clrFadeColor = clrFadeColor;
				m_fEmitVel = fEmitVel;
				m_nNumParticlesToEmit = NumParticlesToEmit;
				m_vEmitterPosition = vEmitterPosition;
				m_vEmitterDirection = vEmitterDirection;
				m_vEmitterDirection.Normalize();

				m_sGUID = DSMisc.GetGUID( "ParticleManager" );

				RestoreDeviceObjects( oDevice );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		public virtual void RestoreDeviceObjects( Device oDevice )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.RestoreDeviceObjects";

			try
			{
				// Create a vertex buffer for the Particle system.  The size of this buffer
				// does not relate to the number of particles that exist.  Rather, the
				// buffer is used as a communication channel with the device.. we fill in 
				// a bit, and tell the device to draw.  While the device is drawing, we
				// fill in the next bit using NOOVERWRITE.  We continue doing this until 
				// we run out of vertex buffer space, and are forced to DISCARD the buffer
				// and start over at the beginning.
				oDevice.RenderState.SourceBlend = Blend.One;
				oDevice.RenderState.DestinationBlend = Blend.One;

				vertexBuffer = new VertexBuffer(typeof(PointVertex), discard, oDevice,  Usage.Dynamic | Usage.WriteOnly | Usage.Points, PointVertex.Format, Pool.Default);
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		public virtual void Advance( double dElapsedTime )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.Advance";
			DSParticle oParticle = null;
			double nTempParticlesToEmit = 0;
			int particlesEmit = 0;

			try
			{
				//Advance & Kill Particles
				m_dCurrentTime += dElapsedTime;
				for (int ii = particlesList.Count-1; ii >= 0; ii--)
				{
					oParticle = (DSParticle)particlesList[ii];

					//Advance the particle
					AdvanceParticle( dElapsedTime,oParticle );

					// Kill old particles
					if( m_dCurrentTime - oParticle.m_dCreationTime > m_dParticleLifeTimeInSeconds 
						&& m_dParticleLifeTimeInSeconds > 0 )
					{
						KillParticle( oParticle );

						// Kill Particle
						freeParticles.Add( oParticle );
						particlesList.RemoveAt(ii);
						particles--;
					}
					else
					{
						particlesList[ii] = oParticle;
					}
				}

				#region Emit new particles
				//m_dtLastParticleEmitted
				//First of all how many particles a second do we emit?
				nTempParticlesToEmit = m_nNumParticlesToEmit * dElapsedTime + m_nNumPartialParticlesEmmitedLastClick;
				//Make sure we don't emit fractions of a particle.  Save them up for next time
				m_nNumPartialParticlesEmmitedLastClick = nTempParticlesToEmit - Math.Floor( nTempParticlesToEmit );
				//Finally we have our emittion count
				particlesEmit = particles + (int)Math.Floor( nTempParticlesToEmit );

				if( m_bRunDown == false && m_bEmitParticles == true )
				{
					//11/30/2004 Chris Hill  If we have exceeded our particle emission amount then bail.
					while(
							particles < m_nParticlesLimit && particles < particlesEmit &&
							( m_nNumParticlesEmmited <= m_nMaxNumParticlesToEmit || m_nMaxNumParticlesToEmit == 0 )
						 )
					{
						//11/30/2004 Chris Hill  Keep track of how many particles we emit.
						m_nNumParticlesEmmited++;
						if( m_nNumParticlesEmmited > m_nMaxNumParticlesToEmit && m_nMaxNumParticlesToEmit > 0 )
						{
							this.RunDown = true;
						}

						if (freeParticles.Count > 0)
						{
							oParticle = (DSParticle)freeParticles[0];
							freeParticles.RemoveAt(0);
						}
						else
						{
							oParticle = new DSParticle();
						}

						// Emit new Particle
						float fRand1 = ((float)rand.Next(int.MaxValue)/(float)int.MaxValue) * (float)Math.PI * 2.0f;
						float fRand2 = ((float)rand.Next(int.MaxValue)/(float)int.MaxValue) * (float)Math.PI * 0.25f;

						oParticle.m_vPosition = m_vEmitterPosition ;


						#region Get Starting Velocity
						//Chris  
						//Get Random Vector
						Vector3 vRandom = new Vector3(	DSMisc.GetRnd( -10,10 ),
							DSMisc.GetRnd( -10,10 ),
							DSMisc.GetRnd( -10,10 ) );
						vRandom.Normalize();

						//Initialize our variables
						float k = 1;

						//Compute d.e
						float a = vRandom.X*m_vEmitterDirection.X + vRandom.Y*m_vEmitterDirection.Y + vRandom.Z*m_vEmitterDirection.Z;

						//Compute r
						oParticle.m_vVelocity.X = (vRandom.X - m_vEmitterDirection.X*a)*k + m_vEmitterDirection.X;
						oParticle.m_vVelocity.Y = (vRandom.Y - m_vEmitterDirection.Y*a)*k + m_vEmitterDirection.Y;
						oParticle.m_vVelocity.Z = (vRandom.Z - m_vEmitterDirection.Z*a)*k + m_vEmitterDirection.Z;
						oParticle.m_vVelocity.Normalize();
						oParticle.m_vVelocity.Multiply( m_fEmitVel );
						#endregion


						oParticle.diffuseColor		= m_clrEmitColor;
						oParticle.fadeColor			= m_clrFadeColor;
						oParticle.m_dFadeProgression   = 1.0f;
						oParticle.m_dCreationTime   = m_dCurrentTime;

						CreateNewParticle( oParticle );

						particlesList.Add( oParticle );
						particles++;
					}
				}
				#endregion

				if( OneShot == true )
				{
					RunDown = true;
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public virtual void AdvanceParticle( double dElapsedTime,DSParticle oParticle )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.AdvanceParticle";

			try
			{
				//Chris
				//Figure this out... the particles aren't acting right
				//oParticle.m_dFadeProgression -= fElapsedTime * .025f;//0.15f; //*.25
				oParticle.m_dFadeProgression -= ( dElapsedTime / m_dParticleLifeTimeInSeconds );
				if( oParticle.m_dFadeProgression < 0.0f )
				{
					oParticle.m_dFadeProgression = 0.0f;
				}
					
				oParticle.m_vPosition += oParticle.m_vVelocity * (float)dElapsedTime;
				oParticle.m_vVelocity += m_vGravity * (float)dElapsedTime;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public virtual void KillParticle( DSParticle oParticle )
		{
		}

		public virtual void CreateNewParticle( DSParticle oParticle )
		{
		}


		public virtual void Render3D( Device dev )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.Render";
			PointVertex[] vertices = null;
			int numParticlesToRender = 0;
			int count = 0;
			Vector3 vPos;
			Vector3 vVel;
			float fLengthSq;
			System.Drawing.Color diffuse;

			try
			{
				// Set up the vertex buffer to be rendered
				dev.SetStreamSource(0, vertexBuffer, 0);
				dev.VertexFormat = PointVertex.Format;

				// Set the render states for using point sprites
				dev.RenderState.PointSpriteEnable = true;
				dev.RenderState.PointScaleEnable = true ;
				dev.RenderState.PointSize = m_fParticleSize;
				dev.RenderState.PointSizeMin = 0.00f;
				dev.RenderState.PointScaleA = 0.00f;
				dev.RenderState.PointScaleB = 0.00f;
				dev.RenderState.PointScaleC = 1.00f;


				// Lock the vertex buffer.  We fill the vertex buffer in small
				// chunks, using LockFlags.NoOverWrite.  When we are done filling
				// each chunk, we call DrawPrim, and lock the next chunk.  When
				// we run out of space in the vertex buffer, we start over at
				// the beginning, using LockFlags.Discard.
				baseParticle += flush;

				if (baseParticle >= discard)
					baseParticle = 0;

				vertices = (PointVertex[])vertexBuffer.Lock(baseParticle * DXHelp.GetTypeSize(typeof(PointVertex)), typeof(PointVertex), (baseParticle != 0) ? LockFlags.NoOverwrite : LockFlags.Discard, flush);
				foreach( DSParticle p in particlesList )
				{
					vPos = p.m_vPosition;
					vVel = p.m_vVelocity;
					fLengthSq = vVel.LengthSq();

					diffuse = ColorOperator.Lerp(p.fadeColor, p.diffuseColor, (float)p.m_dFadeProgression);

					vertices[count].v     = vPos;
					vertices[count].color = diffuse.ToArgb();
					count++;

					if( ++numParticlesToRender == flush )
					{
						// Done filling this chunk of the vertex buffer.  Lets unlock and
						// draw this portion so we can begin filling the next chunk.
						vertexBuffer.Unlock();
						dev.DrawPrimitives(PrimitiveType.PointList, baseParticle, numParticlesToRender);

						// Lock the next chunk of the vertex buffer.  If we are at the 
						// end of the vertex buffer, LockFlags.Discard the vertex buffer and start
						// at the beginning.  Otherwise, specify LockFlags.NoOverWrite, so we can
						// continue filling the VB while the previous chunk is drawing.
						baseParticle += flush;

						if( baseParticle >= discard )
						{
							baseParticle = 0;
						}

						vertices = (PointVertex[])vertexBuffer.Lock(baseParticle * DXHelp.GetTypeSize(typeof(PointVertex)), typeof(PointVertex), (baseParticle != 0) ? LockFlags.NoOverwrite : LockFlags.Discard, flush);
						count = 0;

						numParticlesToRender = 0;
					}
				}

				// Unlock the vertex buffer
				vertexBuffer.Unlock();
				// Render any remaining particles
				if (numParticlesToRender > 0)
					dev.DrawPrimitives(PrimitiveType.PointList, baseParticle, numParticlesToRender);

				// Reset render states
				dev.RenderState.PointSpriteEnable = false;
				dev.RenderState.PointScaleEnable = false;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public virtual void Render2D( Device dev,Vector2 oViewUpperLeft )
		{
			const string sRoutineName = "DarkStrideToolbox.ParticleManager.Render";
			PointVertex[] vertices = null;
			int numParticlesToRender = 0;
			int count = 0;
			Vector3 vPos;
			Vector3 vVel;
			float fLengthSq;
			System.Drawing.Color diffuse;
			Texture oTexture = null;
			System.Reflection.Assembly oAssembly = null;
			System.IO.Stream oStream = null;

			try
			{
				//11/20/2004 Chris Hill  Added debugging.
				DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,3,"Starting ParticleManager.Render" );


				if( m_sParticleTexture != null && m_sParticleTexture.Length > 0 )
				{
					oTexture = DSResourceManager.GetGlobalInstance().GetTexture( m_sParticleTexture );
				}
				else
				{
					oAssembly = System.Reflection.Assembly.GetExecutingAssembly();
					oStream = oAssembly.GetManifestResourceStream( "DarkStrideToolbox.Graphics.Particle.bmp" );
					oTexture = TextureLoader.FromStream( dev, oStream );
				}
				dev.SetTexture(0, oTexture);

				// Set up the vertex buffer to be rendered
				dev.SetStreamSource(0, vertexBuffer, 0);
				dev.VertexFormat = PointVertex.Format;

				dev.RenderState.ZBufferWriteEnable = false;
				dev.RenderState.AlphaBlendEnable = true;
				dev.RenderState.SourceBlend = Blend.One;
				dev.RenderState.DestinationBlend = Blend.One;

				//Set the render states for using point sprites
				dev.RenderState.PointSpriteEnable = true;
				dev.RenderState.PointScaleEnable = false;
				dev.RenderState.PointSize = m_fParticleSize;
				dev.RenderState.PointSizeMin = 0.00f;
				dev.RenderState.PointScaleA = 0.00f;
				dev.RenderState.PointScaleB = 0.00f;
				dev.RenderState.PointScaleC = 1.00f;


				// Lock the vertex buffer.  We fill the vertex buffer in small
				// chunks, using LockFlags.NoOverWrite.  When we are done filling
				// each chunk, we call DrawPrim, and lock the next chunk.  When
				// we run out of space in the vertex buffer, we start over at
				// the beginning, using LockFlags.Discard.
				baseParticle += flush;

				if (baseParticle >= discard)
					baseParticle = 0;

				vertices = (PointVertex[])vertexBuffer.Lock(baseParticle * DXHelp.GetTypeSize(typeof(PointVertex)), typeof(PointVertex), (baseParticle != 0) ? LockFlags.NoOverwrite : LockFlags.Discard, flush);
				foreach( DSParticle p in particlesList )
				{
					//11/19/2004 Chris Hill  This code was assuming the viewport was 500x500!!
					vPos = new Vector3( ( -( oViewUpperLeft.X + ( dev.Viewport.Width  / 2.0f ) ) + p.m_vPosition.X ) / ( dev.Viewport.Width  / 2.0f ), 
						(  ( oViewUpperLeft.Y + ( dev.Viewport.Height / 2.0f ) ) - p.m_vPosition.Y ) / ( dev.Viewport.Height / 2.0f ),
						0 );

					vVel = p.m_vVelocity;
					fLengthSq = vVel.LengthSq();

					diffuse = ColorOperator.Lerp(p.fadeColor, p.diffuseColor, (float)p.m_dFadeProgression);

					vertices[count].v     = vPos;
					vertices[count].color = diffuse.ToArgb();
					count++;

					if( ++numParticlesToRender == flush )
					{
						//11/20/2004 Chris Hill  Added debugging.
						DSMisc.WriteToDebugFile( m_sDebugPath,m_bDebugMode,4,"Flush the buffer" );


						// Done filling this chunk of the vertex buffer.  Lets unlock and
						// draw this portion so we can begin filling the next chunk.
						vertexBuffer.Unlock();
						dev.DrawPrimitives(PrimitiveType.PointList, baseParticle, numParticlesToRender);

						// Lock the next chunk of the vertex buffer.  If we are at the 
						// end of the vertex buffer, LockFlags.Discard the vertex buffer and start
						// at the beginning.  Otherwise, specify LockFlags.NoOverWrite, so we can
						// continue filling the VB while the previous chunk is drawing.
						baseParticle += flush;

						if( baseParticle >= discard )
						{
							baseParticle = 0;
						}

						vertices = (PointVertex[])vertexBuffer.Lock(baseParticle * DXHelp.GetTypeSize(typeof(PointVertex)), typeof(PointVertex), (baseParticle != 0) ? LockFlags.NoOverwrite : LockFlags.Discard, flush);
						count = 0;

						numParticlesToRender = 0;
					}
				}

				// Unlock the vertex buffer
				vertexBuffer.Unlock();
				// Render any remaining particles
				if (numParticlesToRender > 0)
					dev.DrawPrimitives(PrimitiveType.PointList, baseParticle, numParticlesToRender);


				// Reset render states
				dev.RenderState.PointSpriteEnable = false;
				dev.RenderState.PointScaleEnable = false;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}



		#region Properties
		public long ParticleCount
		{
			get
			{
				return( particlesList.Count );
			}
		}
		//Movement Properties
		public Vector3 Gravity
		{
			get
			{
				return( m_vGravity );
			}
			set
			{
				m_vGravity = value;
			}
		}

		public Vector3 EmitterPosition
		{
			get
			{
				return( m_vEmitterPosition );
			}
			set
			{
				m_vEmitterPosition = value;
			}
		}
		public Vector3 EmitterDirection
		{
			get
			{
				return( m_vEmitterDirection );
			}
			set
			{
				m_vEmitterDirection = value;
			}
		}

		public double EmitterRangeInRad
		{
			get
			{
				return( m_nEmitterRangeInRad );
			}
			set
			{
				m_nEmitterRangeInRad = value;
			}
		}
		//Particle Properties
		public double ParticleLifeTimeInSeconds
		{
			get
			{
				return( m_dParticleLifeTimeInSeconds );
			}
			set
			{
				m_dParticleLifeTimeInSeconds = value;
			}
		}

		//General Properties
		public virtual string GUID
		{
			get
			{
				return( m_sGUID );
			}
			set
			{
				m_sGUID = value;
			}
		}
		//Apperance Properties
		public float ParticleSize
		{
			get
			{
				return( m_fParticleSize );
			}
			set
			{
				m_fParticleSize = value;
			}
		}
		public System.Drawing.Color EmitColor
		{
			get
			{
				return( m_clrEmitColor );
			}
			set
			{
				m_clrEmitColor = value;
			}
		}
		public System.Drawing.Color FadeColor
		{
			get
			{
				return( m_clrFadeColor );
			}
			set
			{
				m_clrFadeColor = value;
			}
		}

		public bool RunDown
		{
			get
			{
				return( m_bRunDown );
			}
			set
			{
				m_bRunDown = value;
			}
		}
		public bool OneShot
		{
			get
			{
				return( m_bOneShot );
			}
			set
			{
				m_bOneShot = value;
			}
		}
		public bool EmitParticles
		{
			get
			{
				return( m_bEmitParticles );
			}
			set
			{
				m_bEmitParticles = value;
			}
		}
		public float EmitVelocity
		{
			get
			{
				return( m_fEmitVel );
			}
			set
			{
				m_fEmitVel = value;
			}
		}

		//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() + 
							"DSParticleMgr_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 DSParticleManager\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;
			}
			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;
				}				
			}
		}
		public double CurrentTime
		{
			get
			{
				return( m_dCurrentTime );
			}
		}
		public double NumParticlesToEmit
		{
			set
			{
				m_nNumParticlesToEmit = value;
			}
			get
			{
				return( m_nNumParticlesToEmit );
			}
		}
		public DSParticleSystem ParticleSystem
		{
			get
			{
				return( m_oParticleSystem );
			}
			set
			{
				m_oParticleSystem = value;
			}
		}
		//11/30/2004 Chris Hill  This is the number of particles to emit before we shut down.
		public long MaxNumParticlesToEmit
		{
			get
			{
				return( m_nMaxNumParticlesToEmit );
			}
			set
			{
				m_nMaxNumParticlesToEmit = value;
			}
		}
		//11/30/2004 Chris Hill  This is the max number of particles we can have in existance at any one time.
		public int ParticlesLimit
		{
			get
			{
				return( m_nParticlesLimit );
			}
			set
			{
				m_nParticlesLimit = value;
			}
		}
		public string ParticleTexture
		{
			get
			{
				return( m_sParticleTexture );
			}
			set
			{
				m_sParticleTexture = value;
			}
		}
		#endregion
	}


	public class DSParticle
	{
		#region Properties
		public Vector3 m_vPosition;       // Current position
		public Vector3 m_vVelocity;       // Current velocity
		public double m_dCreationTime;     // Time of creation
		
		public System.Drawing.Color diffuseColor; // Initial diffuse color
		public System.Drawing.Color fadeColor;    // Faded diffuse color
		public double m_dFadeProgression;      // Fade progression
		#endregion
	};
}