// File: ParticleSystem.cs
// Author: Sonhui Schweitzer
// Date: January 25th, 2005
// Project: CS470 Applied Software Developement Project
using System;
using System.Drawing;
using Microsoft.DirectX;
using Direct3D=Microsoft.DirectX.Direct3D;

namespace BreakOut
{
	public struct Particle
	{
		public float Size;
		public float Orientation;
		public Vector2 Position;
		public Vector2 Velocity;
		public Vector2 Acceleration;
		public float Age;
	}

	public class ParticleSystem : Entity
	{
		private float m_fAge;
		private float m_fMaxAge;
		private int m_iParticleCount;

		// location of generator
		private Circle m_oBounds;

		// particle color
		private Color m_oColor;

		// array of particles
		private Particle[] m_aParticles;

		// continuous or burst
		private bool m_bContinuous;
		private Random m_oRandom;

		// generation parameters
		private Vector2 m_oInitialVelocity;
		private float m_fSpeedDeviation;
		private float m_fSpeed;

		public Vector2 InitialVelocity { get { return m_oInitialVelocity; } set { m_oInitialVelocity = value; } }

		public ParticleSystem( Random oRandom , Vector2 oLocation , Color oColor , float fMaxAge , int iParticleCount , Vector2 oInitialVelocty , bool bContinuous , float fSpeed , float fSpeedDeviation )
		{
			m_bContinuous = bContinuous;
			m_oBounds = new Circle( oLocation , 1.0f );
			m_oColor = oColor;
			m_fAge = 0.0f;
			m_fMaxAge = fMaxAge;
			m_iParticleCount = iParticleCount;
			m_aParticles = new Particle[ m_iParticleCount ];
			m_oInitialVelocity = oInitialVelocty;
			m_fSpeed = fSpeed;
			m_fSpeedDeviation = fSpeedDeviation;
			m_oRandom = oRandom;
			for ( int i = 0 ; i < m_iParticleCount ; ++i )
			{
				m_aParticles[ i ].Acceleration = Vector2.Empty;
				m_aParticles[ i ].Position = oLocation;
				m_aParticles[ i ].Velocity = m_oInitialVelocity + ( Vector2.Normalize( new Vector2( 2.0f * ( ( float )m_oRandom.NextDouble() - 0.5f ) , 2.0f * ( ( float )m_oRandom.NextDouble() - 0.5f ) ) ) * ( ( ( 2.0f * ( ( float )m_oRandom.NextDouble() - 0.5f ) ) * m_fSpeedDeviation ) + m_fSpeed ) );
				if ( m_bContinuous )
				{
					m_aParticles[ i ].Age = ( m_fMaxAge / m_iParticleCount ) * i;
				}
				else
				{
					m_aParticles[ i ].Age = 0.0f;
				}
			}
		}

		~ParticleSystem()
		{
		}

		public static Direct3D.VertexBuffer CreateVB( Direct3D.Device oDevice )
		{
			return new Direct3D.VertexBuffer( typeof( Direct3D.CustomVertex.PositionColoredTextured ) , 40 * 3 , oDevice , Direct3D.Usage.Dynamic , Direct3D.CustomVertex.PositionColoredTextured.Format , Direct3D.Pool.Default );
		}

		public virtual void Render( Direct3D.Device oDevice , Game oGame )
		{
			Direct3D.VertexBuffer oVB = oGame.GetVB( "ParticleSystem" );
			Direct3D.Texture oTexture = oGame.GetTexture( "ParticleSystem" );
			if ( ( oVB != null ) && ( oTexture != null ) )
			{
				float fSize = 0.02f;
				float fEdgeSize = 0.0f;
				GraphicsStream oStream = oVB.Lock( 0 , 0 , 0 );
				Direct3D.CustomVertex.PositionColoredTextured[] aVertices = new Direct3D.CustomVertex.PositionColoredTextured[ m_iParticleCount * 3 ];
				for ( int i = 0 ; i < m_iParticleCount ; ++i )
				{
					aVertices[ ( i * 3 ) + 0 ].X = m_aParticles[ i ].Position.X - fSize;
					aVertices[ ( i * 3 ) + 0 ].Y = m_aParticles[ i ].Position.Y + ( 3.0f * fSize );
					aVertices[ ( i * 3 ) + 0 ].Z = 1.0f; 
					aVertices[ ( i * 3 ) + 0 ].Color = new Direct3D.ColorValue( ( float )m_oColor.R / 255.0f , ( float )m_oColor.G / 255.0f , ( float )m_oColor.B / 255.0f , 1.0f - ( m_aParticles[ i ].Age / m_fMaxAge ) ).ToArgb();
					aVertices[ ( i * 3 ) + 0 ].Tu = fEdgeSize;
					aVertices[ ( i * 3 ) + 0 ].Tv = 2.0f - ( 3.0f * fEdgeSize );
					aVertices[ ( i * 3 ) + 1 ].X = m_aParticles[ i ].Position.X + ( 3.0f * fSize );
					aVertices[ ( i * 3 ) + 1 ].Y = m_aParticles[ i ].Position.Y - fSize;
					aVertices[ ( i * 3 ) + 1 ].Z = 1.0f; 
					aVertices[ ( i * 3 ) + 1 ].Color = new Direct3D.ColorValue( ( float )m_oColor.R / 255.0f , ( float )m_oColor.G / 255.0f , ( float )m_oColor.B / 255.0f , 1.0f - ( m_aParticles[ i ].Age / m_fMaxAge ) ).ToArgb();
					aVertices[ ( i * 3 ) + 1 ].Tu = 2.0f - ( 3.0f * fEdgeSize );
					aVertices[ ( i * 3 ) + 1 ].Tv = fEdgeSize;
					aVertices[ ( i * 3 ) + 2 ].X = m_aParticles[ i ].Position.X - fSize;
					aVertices[ ( i * 3 ) + 2 ].Y = m_aParticles[ i ].Position.Y - fSize;
					aVertices[ ( i * 3 ) + 2 ].Z = 1.0f; 
					aVertices[ ( i * 3 ) + 2 ].Color = new Direct3D.ColorValue( ( float )m_oColor.R / 255.0f , ( float )m_oColor.G / 255.0f , ( float )m_oColor.B / 255.0f , 1.0f - ( m_aParticles[ i ].Age / m_fMaxAge ) ).ToArgb();
					aVertices[ ( i * 3 ) + 2 ].Tu = fEdgeSize;
					aVertices[ ( i * 3 ) + 2 ].Tv = fEdgeSize;
				}
				oStream.Write( aVertices );					
				oVB.Unlock();

				oDevice.Transform.World = Matrix.Identity;

				oDevice.SetStreamSource( 0 , oVB , 0 );
				oDevice.VertexFormat = Direct3D.CustomVertex.PositionColoredTextured.Format;

				oDevice.SetTexture( 0 , oTexture );
				oDevice.RenderState.AlphaBlendEnable = true;
				oDevice.RenderState.SourceBlend = Direct3D.Blend.SourceAlpha;
				oDevice.RenderState.DestinationBlend = Direct3D.Blend.InvSourceAlpha;

				oDevice.SamplerState[0].AddressU = Direct3D.TextureAddress.Clamp;
				oDevice.SamplerState[0].AddressV = Direct3D.TextureAddress.Clamp;

				oDevice.SamplerState[ 0 ].MinFilter = Direct3D.TextureFilter.Linear;
				oDevice.SamplerState[ 0 ].MagFilter = Direct3D.TextureFilter.Linear;
				oDevice.SamplerState[ 0 ].MipFilter = Direct3D.TextureFilter.Linear;
				oDevice.TextureState[ 0 ].ColorOperation = Direct3D.TextureOperation.Modulate;
				oDevice.TextureState[ 0 ].ColorArgument1 = Direct3D.TextureArgument.TextureColor;
				oDevice.TextureState[ 0 ].ColorArgument2 = Direct3D.TextureArgument.Diffuse;
				oDevice.TextureState[ 0 ].AlphaOperation = Direct3D.TextureOperation.Modulate2X;
				oDevice.TextureState[ 0 ].AlphaArgument1 = Direct3D.TextureArgument.TextureColor;
				oDevice.TextureState[ 0 ].AlphaArgument2 = Direct3D.TextureArgument.Diffuse;
				oDevice.DrawPrimitives( Direct3D.PrimitiveType.TriangleList , 0 , m_iParticleCount );
				oDevice.RenderState.AlphaBlendEnable = false;
			}
		}

		public virtual void Collide( Game oGame , CollideArgs oArgs )
		{
		}

		public virtual float Update( Game oGame , float fDeltaTime )
		{
			m_fAge += fDeltaTime;
			if ( m_fAge > m_fMaxAge && !m_bContinuous )
			{
				oGame.RemoveParticleSystem( this );
				return 0.0f;
			}
			for ( int i = 0 ; i < m_iParticleCount ; ++i )
			{
				m_aParticles[ i ].Velocity += m_aParticles[ i ].Acceleration * fDeltaTime;
				m_aParticles[ i ].Position += m_aParticles[ i ].Velocity * fDeltaTime;
				if ( m_bContinuous )
				{
					m_aParticles[ i ].Age += fDeltaTime;
					if ( m_aParticles[ i ].Age > m_fMaxAge )
					{
						m_aParticles[ i ].Position = m_oBounds.Location;
						m_aParticles[ i ].Velocity = m_oInitialVelocity + ( Vector2.Normalize( new Vector2( 2.0f * ( ( float )m_oRandom.NextDouble() - 0.5f ) , 2.0f * ( ( float )m_oRandom.NextDouble() - 0.5f ) ) ) * ( ( ( 2.0f * ( ( float )m_oRandom.NextDouble() - 0.5f ) ) * m_fSpeedDeviation ) + m_fSpeed ) );
						m_aParticles[ i ].Age = 0.0f;
					}
				}
				else
				{
					m_aParticles[ i ].Age += fDeltaTime;
				}
			}
			return 0.0f;
		}

		public virtual Bounds GetBounds()
		{
			return m_oBounds;
		}
	}
}
