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

namespace BreakOut
{
	public class Paddle : Entity
	{
		// the collision bounds of the Paddle
		private Line m_oBounds;
		// 0.0 to 1.0 value indicating the center of the Paddle along the path
		private float m_fCurrentPathParameter;
		// the list of points along the path (in path space not world space)
		private Vector2[] m_oPath;
		// the total length of the vectors composing the path
		private float m_fPathLength;
		// the world space point denoting the start of the path
		private Vector2 m_oPathStartPoint;
		// sensitivity of the mouse motion in correlation to Paddle motion
		// the greater the sensitivity the slower the Paddle, the less the sensitivity the faster the Paddle
		private float m_fSensitivity;

		private Ball m_oBall;
		private float m_fPaddleWidth;
		private Vector2 m_oSpecialPoint;
		private float m_fSpecialPointDistance;
		private bool m_bSampled;
		private int m_iState;
		public const int Pistol = 0;
		public const int MachineGun = 1;
		public const int Shotgun = 2;
		public const int Rifle = 3;
		public const int None = 4;
		private int m_iShotgunProjectileCount = 6;
		private float m_fTimeSinceLastFire;
		private float m_fGunAge;
		private float m_fGunMaxAge;

		public Ball Ball
		{
			get
			{
				return m_oBall;
			}
			set
			{
				m_oBall = value;
				UpdateBallLocation();
				m_oBall.Velocity = m_oBounds.Normal * m_oBall.Velocity.Length();
				m_iState = Pistol;
				m_bSampled = true;
			}
		}

		public float Size
		{
			get
			{
				return m_fPaddleWidth;
			}
			set
			{
				if ( value > 0.9f )
				{
					m_fPaddleWidth = 0.9f;
				}
				else if ( value < 0.2f )
				{
					m_fPaddleWidth = 0.2f;
				}
				else
				{
					m_fPaddleWidth = value;
				}
			}
		}

		public Vector2 SpecialPoint
		{
			get { return m_oSpecialPoint; }
		}

		public int GunType
		{
			set { m_iState = value; m_fGunAge = 0.0f; }
		}

		public Paddle( float fCurrentPathParameter , Vector2 oPathStartPoint , Vector2[] oPath , float fPaddleWidth , float fSpecialPointDistance )
		{
			// save the starting path point
			m_fCurrentPathParameter = fCurrentPathParameter;
			m_oPath = oPath;
			m_oPathStartPoint = oPathStartPoint;
			// calculate the total path length
			m_fPathLength = 0.0f;
			foreach ( Vector2 oVector in oPath )
			{
				m_fPathLength += oVector.Length();
			}
			m_oBounds = new Line( Vector2.Empty , Vector2.Empty );
			m_fPaddleWidth = fPaddleWidth;
			m_fSpecialPointDistance = fSpecialPointDistance;
			UpdateBoundsLocation();
			m_fSensitivity = 640.0f;
			m_oBall = null;
			m_iState = Pistol;
			m_fTimeSinceLastFire = 0.0f;
			m_fGunAge= 0.0f;
			m_fGunMaxAge= 10.0f;
		}

		~Paddle()
		{
		}

		public static Direct3D.VertexBuffer CreateVB( Direct3D.Device oDevice )
		{
			Direct3D.VertexBuffer oVB = new Direct3D.VertexBuffer( typeof( Direct3D.CustomVertex.PositionColoredTextured ) , 8 , oDevice , 0 , Direct3D.CustomVertex.PositionColoredTextured.Format , Microsoft.DirectX.Direct3D.Pool.Default );
			return oVB;
		}

		public virtual void Render( Direct3D.Device oDevice , Game oGame )
		{
			if ( m_oBall != null )
			{
				m_oBall.Render( oDevice , oGame );
			}
			Direct3D.VertexBuffer oVB = oGame.GetVB( "Paddle" );
			Direct3D.Texture oTexture = oGame.GetTexture( "Paddle" );
			if ( oVB != null )
			{
				Direct3D.ColorValue oColor = new Direct3D.ColorValue( 1.0f , 1.0f , 1.0f , 1.0f );
				float fEdgeSize = 0.02f;
				Vector2 oWallVector = Vector2.Subtract( m_oBounds.EndPoint , m_oBounds.StartPoint );
				Vector2 oPerp = -Vector2.Normalize( new Vector2( oWallVector.Y , -oWallVector.X ) );
				oPerp *= 0.1f;
				Vector2 oPara = new Vector2( oPerp.Y , -oPerp.X );
				GraphicsStream oStream = oVB.Lock( 0 , 0 , 0 );
				Direct3D.CustomVertex.PositionColoredTextured[] aVertices = new Direct3D.CustomVertex.PositionColoredTextured[ 8 ];
				Vector2 oVector = m_oBounds.StartPoint;
				aVertices[ 0 ].X = oVector.X;
				aVertices[ 0 ].Y = oVector.Y;
				aVertices[ 0 ].Z = 0.0f;
				aVertices[ 0 ].Color = oColor.ToArgb();
				aVertices[ 0 ].Tu = fEdgeSize;
				aVertices[ 0 ].Tv = fEdgeSize;

				oVector = m_oBounds.StartPoint + oPerp;
				aVertices[ 1 ].X = oVector.X;
				aVertices[ 1 ].Y = oVector.Y;
				aVertices[ 1 ].Z = 0.0f;
				aVertices[ 1 ].Color = oColor.ToArgb();
				aVertices[ 1 ].Tu = fEdgeSize;
				aVertices[ 1 ].Tv = 1.0f - fEdgeSize;

				oVector = m_oBounds.StartPoint + oPara;
				aVertices[ 2 ].X = oVector.X;
				aVertices[ 2 ].Y = oVector.Y;
				aVertices[ 2 ].Z = 0.0f;
				aVertices[ 2 ].Color = oColor.ToArgb();
				aVertices[ 2 ].Tu = 0.25f;
				aVertices[ 2 ].Tv = fEdgeSize;

				oVector = m_oBounds.StartPoint + oPara + oPerp;
				aVertices[ 3 ].X = oVector.X;
				aVertices[ 3 ].Y = oVector.Y;
				aVertices[ 3 ].Z = 0.0f;
				aVertices[ 3 ].Color = oColor.ToArgb();
				aVertices[ 3 ].Tu = 0.25f;
				aVertices[ 3 ].Tv = 1.0f - fEdgeSize;

				oVector = m_oBounds.EndPoint + ( -oPara );
				aVertices[ 4 ].X = oVector.X;
				aVertices[ 4 ].Y = oVector.Y;
				aVertices[ 4 ].Z = 0.0f;
				aVertices[ 4 ].Color = oColor.ToArgb();
				aVertices[ 4 ].Tu = 0.75f;
				aVertices[ 4 ].Tv = fEdgeSize;

				oVector = m_oBounds.EndPoint + ( -oPara ) + oPerp;
				aVertices[ 5 ].X = oVector.X;
				aVertices[ 5 ].Y = oVector.Y;
				aVertices[ 5 ].Z = 0.0f;
				aVertices[ 5 ].Color = oColor.ToArgb();
				aVertices[ 5 ].Tu = 0.75f;
				aVertices[ 5 ].Tv = 1.0f - fEdgeSize;
			
				oVector = m_oBounds.EndPoint;
				aVertices[ 6 ].X = oVector.X;
				aVertices[ 6 ].Y = oVector.Y;
				aVertices[ 6 ].Z = 0.0f;
				aVertices[ 6 ].Color = oColor.ToArgb();
				aVertices[ 6 ].Tu = 1.0f - fEdgeSize;
				aVertices[ 6 ].Tv = fEdgeSize;
			
				oVector = m_oBounds.EndPoint + oPerp;
				aVertices[ 7 ].X = oVector.X;
				aVertices[ 7 ].Y = oVector.Y;
				aVertices[ 7 ].Z = 0.0f;
				aVertices[ 7 ].Color = oColor.ToArgb();
				aVertices[ 7 ].Tu = 1.0f - fEdgeSize;
				aVertices[ 7 ].Tv = 1.0f - 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 ].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 , 6 );
				oDevice.RenderState.AlphaBlendEnable = false;
			}
		}

		// Each Entity must be able to react to collisions with other entities
		// May interact with the Game world in the process.
		// Given a list of colliding entities and the normal of the collision
		public virtual void Collide( Game oGame , CollideArgs oArgs )
		{
		}

		// Each entity must update its self based on its own set of dynamics
		// Takes a delta time and processes <= deltatime returning unprocessed time
		public virtual float Update( Game oGame , float fDeltaTime )
		{
			m_fTimeSinceLastFire += fDeltaTime;
			m_fGunAge += fDeltaTime;
			if ( m_fGunAge > m_fGunMaxAge )
			{
				m_iState = Pistol;
			}
			if ( m_oBall != null )
			{
				m_oBall.Update( oGame , fDeltaTime );
				UpdateBallLocation();
			}
			return 0.0f;
		}

		// Each entity must have a bounds
		public virtual Bounds GetBounds()
		{
			return m_oBounds;
		}

		private void UpdateBoundsLocation()
		{
			// calc the distance down the path from the start point to the current Paddle center
			float fPathPoint = m_fCurrentPathParameter * m_fPathLength;
			// traverse the path summing up the segment lengths as we go
			float fSum = 0.0f;
			Vector2 oLastPoint = m_oPathStartPoint;
			foreach ( Vector2 oVector in m_oPath )
			{
				float fNewSum = fSum + oVector.Length();
				// when we pass the current Paddle point on the path it means the Paddle center is partway down the current segment
				if ( fNewSum >= fPathPoint )
				{
					// middle of current vector
					// find the actual point and set the Paddle's bound's center to that point
					Vector2 oCenter = ( oLastPoint + ( ( ( fPathPoint - fSum ) / ( fNewSum - fSum ) ) * oVector ) );
					Vector2 oHalfPaddle = Vector2.Normalize( oVector ) * ( m_fPaddleWidth / 2.0f );
					m_oBounds.StartPoint = oCenter + ( -oHalfPaddle );
					m_oBounds.EndPoint = oCenter + ( oHalfPaddle );
					m_oSpecialPoint = new Vector2( m_oBounds.Center.X , m_oBounds.Center.Y - m_fSpecialPointDistance );
					break;
				}
				oLastPoint += oVector;
				fSum = fNewSum;
			}
			UpdateBallLocation();
		}

		private void UpdateBallLocation()
		{
			if( m_oBall != null )
			{
				Vector2 oCenter = m_oBounds.Center;
				( ( Circle )m_oBall.GetBounds() ).Location = new Vector2( oCenter.X , oCenter.Y + ( ( Circle )m_oBall.GetBounds() ).Radius );
			}
		}

		public void MouseMoveEvent( int iDeltaX , int iDeltaY )
		{
			// update the new path parameter
			m_fCurrentPathParameter -= ( ( float )iDeltaX ) / m_fSensitivity;
			// do the range check and resolve it if need be
			if( m_fCurrentPathParameter < 0.0f )
			{
				m_fCurrentPathParameter = 0.0f;
			}
			if( m_fCurrentPathParameter > 1.0f )
			{
				m_fCurrentPathParameter = 1.0f;
			}
			UpdateBoundsLocation();
		}

		public void MouseLeftDownEvent( Game oGame )
		{
			if ( !m_bSampled )
			{
				m_bSampled = true;
				if ( m_oBall != null )
				{
					oGame.AddBall( m_oBall );
					m_oBall = null;
				}
			}
			if ( m_oBall == null )
			{
				switch ( m_iState )
				{
					case MachineGun:
						if ( m_fTimeSinceLastFire > 0.15f )
						{
							oGame.PlaySound( "Fire" );
							oGame.AddProjectile( new Projectile( oGame.Random , new Vector2( m_oBounds.Center.X , m_oBounds.Center.Y ) , Vector2.Multiply( Vector2.Normalize( new Vector2( ( ( float )( 2.0 * oGame.Random.NextDouble() ) - 1.0f ) * 0.1f , 1.0f ) ) , 2.0f ) ) );
							m_fTimeSinceLastFire = 0.0f;
						}
						break;
					case Pistol:
						if ( m_fTimeSinceLastFire > 0.8f )
						{
							oGame.PlaySound( "Fire" );
							oGame.AddProjectile( new Projectile( oGame.Random , new Vector2( m_oBounds.Center.X , m_oBounds.Center.Y ) , Vector2.Multiply( Vector2.Normalize( new Vector2( ( ( float )( 2.0 * oGame.Random.NextDouble() ) - 1.0f ) * 0.05f , 1.0f ) ) , 2.0f ) ) );
							m_fTimeSinceLastFire = 0.0f;
						}
						break;
					case Shotgun:
						if ( m_fTimeSinceLastFire > 1.0f )
						{
							oGame.PlaySound( "Fire" );
							for ( int i = 0 ; i < m_iShotgunProjectileCount ; ++i )
							{
								oGame.AddProjectile( new Projectile( oGame.Random , new Vector2( m_oBounds.Center.X , m_oBounds.Center.Y ) , Vector2.Multiply( Vector2.Normalize( new Vector2( ( ( float )( 2.0 * oGame.Random.NextDouble() ) - 1.0f ) * 0.2f , 1.0f ) ) , 2.0f ) ) );
							}
							m_fTimeSinceLastFire = 0.0f;
						}
						break;
					case Rifle:
						if ( m_fTimeSinceLastFire > 0.7f )
						{
							oGame.PlaySound( "Fire" );
							oGame.AddProjectile( new Projectile( oGame.Random , new Vector2( m_oBounds.Center.X , m_oBounds.Center.Y ) , new Vector2( 0.0f , 3.0f ) ) );
							m_fTimeSinceLastFire = 0.0f;
						}
						break;
					case None:
						break;
				}
			}
		}

		public void MouseLeftUpEvent( Game oGame )
		{
			if ( m_bSampled )
			{
				m_bSampled = false;
			}
		}
	}
}
