// File: Ball.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;
using DirectSound=Microsoft.DirectX.DirectSound;

namespace BreakOut
{
	public class Ball : Entity
	{
		private Vector2 m_oAcceleration;
		private Circle m_oBounds;
		private Vector2 m_oVelocity;
		private ParticleSystem m_oParticleSystem;
		private Random m_oRandom;


		public Vector2 Velocity { get { return m_oVelocity; } set { m_oVelocity = value; } }
		public Vector2 Acceleration { get { return m_oAcceleration; } set { m_oAcceleration = value; } }
		public float Size
		{ 
			get { return m_oBounds.Radius; }
			set
			{
				if ( value > 0.08f )
				{
					m_oBounds.Radius = 0.08f;
				}
				else if ( value < 0.02f )
				{
					m_oBounds.Radius = 0.02f;
				}
				else
				{
					m_oBounds.Radius = value;
				}
			}
		}
		public float Speed
		{
			get { return m_oVelocity.Length(); }
			set
			{ 
				float fLength = m_oVelocity.Length();
				if ( value > 2.0f )
				{
					m_oVelocity *= ( 2.0f / fLength );
				}
				else if ( value < 0.4f )
				{
					m_oVelocity *= ( 0.4f / fLength );
				}
				else
				{
					m_oVelocity *= ( value / fLength );
				}
			}
		}

		public Ball( Game oGame , Vector2 oLocation , Vector2 oVelocity , Vector2 oAcceleration )
		{
			m_oRandom = oGame.Random;
			m_oAcceleration = oAcceleration;
			m_oVelocity = oVelocity;
			m_oBounds = new Circle( oLocation , 0.04f );
			m_oParticleSystem = new ParticleSystem( m_oRandom , m_oBounds.Location , Color.DarkBlue , 0.5f , 16 , Vector2.Empty , true , 0.06f , 0.06f );
		}

		~Ball()
		{
			m_oParticleSystem = null;
		}

		public static Direct3D.VertexBuffer CreateVB( Direct3D.Device oDevice )
		{
			Direct3D.VertexBuffer oVB = new Direct3D.VertexBuffer( typeof( Direct3D.CustomVertex.PositionColoredTextured ) , 4 , oDevice , 0 , Direct3D.CustomVertex.PositionColoredTextured.Format , Microsoft.DirectX.Direct3D.Pool.Default );
			GraphicsStream oStream = oVB.Lock( 0 , 0 , 0 );
			Direct3D.CustomVertex.PositionColoredTextured[] aVertices = new Direct3D.CustomVertex.PositionColoredTextured[ 4 ];
			
			float fEdgeSize = 0.0f;
			float fSize = 1.0f;
			aVertices[ 0 ].X = -fSize;
			aVertices[ 0 ].Y = fSize;
			aVertices[ 0 ].Z = 0.0f;
			aVertices[ 0 ].Color = new Direct3D.ColorValue( 1.0f , 1.0f , 1.0f , 1.0f ).ToArgb();
			aVertices[ 0 ].Tu = fEdgeSize;
			aVertices[ 0 ].Tv = fEdgeSize;

			aVertices[ 1 ].X = -fSize;
			aVertices[ 1 ].Y = -fSize;
			aVertices[ 1 ].Z = 0.0f;
			aVertices[ 1 ].Color = new Direct3D.ColorValue( 1.0f , 1.0f , 1.0f , 1.0f ).ToArgb();
			aVertices[ 1 ].Tu = fEdgeSize;
			aVertices[ 1 ].Tv = 1.0f - fEdgeSize;

			aVertices[ 2 ].X = fSize;
			aVertices[ 2 ].Y = fSize;
			aVertices[ 2 ].Z = 0.0f;
			aVertices[ 2 ].Color = new Direct3D.ColorValue( 1.0f , 1.0f , 1.0f , 1.0f ).ToArgb();
			aVertices[ 2 ].Tu = 1.0f - fEdgeSize;
			aVertices[ 2 ].Tv = fEdgeSize;
			
			aVertices[ 3 ].X = fSize;
			aVertices[ 3 ].Y = -fSize;
			aVertices[ 3 ].Z = 0.0f;
			aVertices[ 3 ].Color = new Direct3D.ColorValue( 1.0f , 1.0f , 1.0f , 1.0f ).ToArgb();
			aVertices[ 3 ].Tu = 1.0f - fEdgeSize;
			aVertices[ 3 ].Tv = 1.0f - fEdgeSize;
			
			oStream.Write( aVertices );
			oVB.Unlock();
			return oVB;
		}

		public virtual void Render( Direct3D.Device oDevice , Game oGame )
		{
			m_oParticleSystem.Render( oDevice , oGame );
			Direct3D.VertexBuffer oVB = oGame.GetVB( "Ball" );
			Direct3D.Texture oTexture = oGame.GetTexture( "Ball" );
			if ( ( oVB != null ) && ( oTexture != null ) )
			{
				oDevice.Transform.World = Matrix.Scaling( m_oBounds.Radius , m_oBounds.Radius , 1.0f ) * Matrix.Translation( m_oBounds.Location.X , m_oBounds.Location.Y , 0.0f );

				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 ].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.Modulate;
				oDevice.TextureState[ 0 ].AlphaArgument1 = Direct3D.TextureArgument.TextureColor;
				oDevice.TextureState[ 0 ].AlphaArgument2 = Direct3D.TextureArgument.Diffuse;

				oDevice.DrawPrimitives( Direct3D.PrimitiveType.TriangleStrip , 0 , 2 );
				oDevice.RenderState.AlphaBlendEnable = false;
			}
		}

		public virtual void Collide( Game oGame , CollideArgs oArgs )
		{
			Vector2 oNormal = new Vector2( 0.0f , 0.0f );
			bool bWall = false;
			bool bBreakable = false;
			bool bPaddle = false;
			for ( int i = 0 ; i < oArgs.Count ; ++i )
			{
				if ( oArgs.GetEntity( i ) is Wall )
				{
					bWall = true;
				}
				else if ( oArgs.GetEntity( i ) is Paddle )
				{
					bPaddle = true;
				}
				else if ( oArgs.GetEntity( i ) is Breakable )
				{
					bBreakable = true;
				}
				if ( oArgs.GetEntity( i ).GetBounds() is Line )
				{
					Line oLine = ( Line )oArgs.GetEntity( i ).GetBounds();
					if ( Vector2.Dot( oArgs.GetNormal( i ) , oLine.Normal ) <= 0.0f )
					{
						continue;
					}
				}
				oNormal += oArgs.GetNormal( i );
			}
			if ( oNormal == Vector2.Empty )
			{
				return;
			}
			oNormal.Normalize();
			if( Vector2.Dot( this.m_oVelocity , oNormal ) < 0.0f )
			{
				Vector2 oPerp = new Vector2( oNormal.Y , -oNormal.X );
				Vector2 oVParaN = oNormal * ( Vector2.Dot( m_oVelocity , oNormal ) );
				Vector2 oVPerpN = oPerp * ( Vector2.Dot( m_oVelocity , oPerp ) );
				m_oVelocity = -oVParaN + oVPerpN;
				if ( bWall )
				{
					float fDot = Vector2.Dot( Vector2.Normalize( m_oVelocity ) , new Vector2( 1.0f , 0.0f ) );
					if ( Math.Abs( fDot ) > 0.95 )
					{
						float fLength = m_oVelocity.Length();
						if ( m_oVelocity.Y < 0 )
						{
							m_oVelocity.Y -= 1.0f;
						}
						else
						{
							m_oVelocity.Y += 1.0f;
						}
						m_oVelocity.Normalize();
						m_oVelocity *= fLength;
					}
					else
					{
						float fLength = m_oVelocity.Length();
						m_oVelocity.X += ( float )( oGame.Random.NextDouble() - 0.5 ) * 0.1f;
						m_oVelocity.Y += ( float )( oGame.Random.NextDouble() - 0.5 ) * 0.1f;
						m_oVelocity.Normalize();
						m_oVelocity *= fLength;
					}
				}
				if ( bPaddle )
				{
					if ( m_oVelocity.Y < 0.0f )
					{
						m_oVelocity.Y = -m_oVelocity.Y;
					}
				}
				if ( bWall || bBreakable || bPaddle )
				{
					oGame.AddParticleSystem( new ParticleSystem( oGame.Random , m_oBounds.Location + ( -oNormal * m_oBounds.Radius ) , Color.Blue , 1.0f , 6 , oNormal * 0.1f , false , 0.0f , 0.1f ) );
					oGame.PlaySound( "Bounce" );
				}
			}
		}

		public virtual float Update( Game oGame , float fDeltaTime )
		{
			m_oVelocity += m_oAcceleration * fDeltaTime;
			m_oBounds.Location += m_oVelocity * fDeltaTime;
			( ( Circle )m_oParticleSystem.GetBounds() ).Location = m_oBounds.Location;
			m_oParticleSystem.InitialVelocity = -m_oVelocity * 0.1f;
			m_oParticleSystem.Update( oGame , fDeltaTime );
			return 0.0f;
		}

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