using System;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectPlay;
using Microsoft.DirectX.DirectInput;
using Direct3D=Microsoft.DirectX.Direct3D;

using DarkStrideToolbox;


namespace MetalMarines
{
	public class MMAttack
	{
		#region Properties
		private string m_sSourceIslandGlobalID = "";
		private long m_nSourceX = 0;
		private long m_nSourceY = 0;
		private string m_sTargetIslandGlobalID = "";
		private long m_nTargetX = 0;
		private long m_nTargetY = 0;
		private DSSortedList m_oAAFire = new DSSortedList();
		private double m_nLandedPosOffsetX = 0;
		private double m_nLandedPosOffsetY = 0;
		private long m_nDirection = 0;
		private double m_nTimeSinceAnimationChange = 0;
		private long m_nAnimationCell = 0;
		private bool m_bFiring = false;
		private double m_nPercentAlongPath = 0;
		private double m_nPercSinceLastActed = 0;
		private bool m_bLanded = false;
		private bool m_bLaunched = false;
		private string m_sGlobalID = "";
		private double m_nMMStrength = 0;
		private double m_nMMOffense = 0;
		private long m_nMMGraphicType = 0;
		private double m_nLastSent = 0;
		private int m_nColor = 0;
		
		private int m_nMMFiringSoundIndex = -1;
		private string m_sMMFiringSoundKey = "";
		private int m_nMMLaunchSoundIndex = -1;
		private int m_sMMWalkingSoundIndex = -1;

		private static string m_cATTACK_SEP = "[AS]";
		private static string m_cAAFIRE_SEP = "[AAFS]";
		private static double m_cSECONDSTOLAUNCH = 5.15;
		private static double m_cSECONDSTOLAND = 2.0;
		private static double m_cSECONDSTOATTACK = 45.0;

		private DSGameEngine m_oGameEngine = null;
		private Game m_oGame = null;
		#endregion
        

		public MMAttack()
		{
			m_sGlobalID = DSMisc.GetGUID();
		}


		public bool GetsHit( ref long nPercentHitAt )
		{
			long nHitCount = 0;
			bool bGetsHit = false;
			structAAFire oLoopAAFire;

			for( int nLoopAAFireIndex=0 ; nLoopAAFireIndex<m_oAAFire.Count ; nLoopAAFireIndex++ )
			{
				oLoopAAFire = (structAAFire)m_oAAFire.GetByIndex( nLoopAAFireIndex );
				if( oLoopAAFire.m_bHits == true )
				{
					nHitCount++;
					if( nHitCount == 5 )
					{
						bGetsHit = true;
						nPercentHitAt = oLoopAAFire.m_nPercToHitAt;
					}
				}
			}

			return( bGetsHit );
		}

		public void Advance( double nElapsedTime,Island oTargetIsland )
		{
			object oValue = null;
			bool bAttackMM = false;
			bool bTargetFound = false;
			Square oSquare = null;
			double nDamage = 0;
			double nAttackLen = 0;
			System.Drawing.Point oTarget = new System.Drawing.Point( 0,0 );
			System.Drawing.Point oTargetBuilding = new System.Drawing.Point( 0,0 );
			System.Drawing.Point[] oaPoints = null;


			//Advance our limited stay on this island
			if( m_nPercentAlongPath > 100 && m_bLaunched == false )
			{
				m_bLaunched = true;
				m_nPercentAlongPath = 0;
			}
			else if( m_nPercentAlongPath > 100 && m_bLanded == false ) 
			{
				m_bLanded = true;
				m_nPercentAlongPath = 0;
				m_nPercSinceLastActed = 0;
			}

			//Make the "I'm launching" sound if we haven't already
			if( m_nMMLaunchSoundIndex == -1 && m_bLaunched == false )
			{
				m_nMMLaunchSoundIndex = m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_MMLAUNCH );
			}

			//Am I walking across a mine field?
			oSquare = oTargetIsland.GetSquare( m_nTargetX,m_nTargetY ); 

			if( m_sMMWalkingSoundIndex != -1 &&
				m_oGameEngine.DirectSound.SoundIsStillPlaying( Game.m_cGOB_SOUND_WALKONMINES,m_sMMWalkingSoundIndex ) == false )
			{
				m_sMMWalkingSoundIndex = -1;
			}
			if( m_bLanded == true && oSquare.SquareStatus == enumSquareStatus.Built &&
				m_sMMWalkingSoundIndex == -1 && oSquare.GobBuildingKey == Game.m_cGOB_MINEFIELD_KEY )
			{
				m_sMMWalkingSoundIndex = m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_WALKONMINES );
			}

			//We want it to take roughly X seconds
			if( m_bLaunched == false )
			{
				nAttackLen = m_cSECONDSTOLAUNCH;
			}
			else if( m_bLanded == false )
			{
				nAttackLen = m_cSECONDSTOLAND;
			}
			else
			{
				nAttackLen = m_cSECONDSTOATTACK;
			}

			//If we are in debug mode we never return home
			if( m_bLanded == false || m_oGame.Debug_MMNeverReturnHome == false )
			{
				m_nPercentAlongPath += ( 100.0 / nAttackLen ) * nElapsedTime;
				m_nPercSinceLastActed += ( 100.0 / nAttackLen ) * nElapsedTime; 
			}

			//If we have landed then pick out a target and fire away!
			if( m_bLanded == true )
			{
				///////////////////////////////////////////////////////////////////////////////////////////////
				//Locate the closest enemy building
				///////////////////////////////////////////////////////////////////////////////////////////////
				//Look for MM to target
				oaPoints = Game.GetSquaresInRadius( Game.m_nLANDCOMBAT_MAXRANGE,m_nTargetX,m_nTargetY );
				foreach( System.Drawing.Point oPoint in oaPoints )
				{
					oSquare = oTargetIsland.GetSquare( oPoint.X,oPoint.Y );
					if( oSquare != null )
					{
						if( ( oSquare.SquareStatus == enumSquareStatus.Built || oSquare.SquareStatus == enumSquareStatus.Building ) &&
							(bool)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_FIRESMM_CLMN ) == true &&
							( oSquare.MMX != oPoint.X || oSquare.MMY != oPoint.Y || oSquare.MMPosOffsetX != 0 || oSquare.MMPosOffsetY != 0 ) &&
							oSquare.MMStrength >= 0 &&
							( 
							  bTargetFound == false ||
							  DSMath.Distance( oSquare.MMX,oSquare.MMY,m_nTargetX,m_nTargetY ) < DSMath.Distance( oTarget.X,oTarget.Y,m_nTargetX,m_nTargetY ) 
							)		
						  )
						{
							bAttackMM = true;
							bTargetFound = true;
							oTargetBuilding = oPoint;
							oTarget = new System.Drawing.Point( (int)oSquare.MMX,(int)oSquare.MMY );
						}
					}
				}
				//Now look for buildings to target
				if( bTargetFound == false )
				{
					oaPoints = Game.GetSquaresInRadius( Game.m_nLANDCOMBAT_MAXRANGE,m_nTargetX,m_nTargetY );
					foreach( System.Drawing.Point oPoint in oaPoints )
					{
						oSquare = oTargetIsland.GetSquare( oPoint.X,oPoint.Y );
						if( oSquare != null )
						{
							if( ( oSquare.SquareStatus == enumSquareStatus.Built || oSquare.SquareStatus == enumSquareStatus.Building ) 
								&&
								( 
								  bTargetFound == false ||
								  DSMath.Distance( oPoint.X,oPoint.Y,m_nTargetX,m_nTargetY ) < DSMath.Distance( oTarget.X,oTarget.Y,m_nTargetX,m_nTargetY ) 
								) &&
								(bool)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_CANCAMO_CLMN ) == false &&
								(bool)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_INVISIBLE_CLMN ) == false &&
								(bool)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_AIATTACKS_CLMN ) == true 
							  )
							{
								bTargetFound = true;
								oTarget = oPoint;
								oTargetBuilding = oPoint;
							}
						}
					}
				}


				//Did we find a target
				if( bTargetFound == true )
				{
					m_nPercSinceLastActed = 0;

					Game.MoveMMTowordsGoal( oTargetIsland,nElapsedTime,
											m_nTargetX,m_nTargetY,m_nLandedPosOffsetX,m_nLandedPosOffsetY,
											oTarget.X,oTarget.Y,0,0,
											ref m_nTargetX,ref m_nTargetY,ref m_nLandedPosOffsetX,ref m_nLandedPosOffsetY,ref m_nDirection );

					//Are we within range of our target?  Fire!
					m_bFiring = ( DSMath.Distance( oTarget.X,oTarget.Y,m_nTargetX,m_nTargetY ) <= Game.m_nLANDCOMBAT_MAXRANGE );

					//Start the sounds if we need to
					if( m_nMMFiringSoundIndex != -1 && m_oGameEngine.DirectSound.SoundIsStillPlaying( m_sMMFiringSoundKey,m_nMMFiringSoundIndex ) == false )
					{
						m_nMMFiringSoundIndex = -1;
						m_sMMFiringSoundKey = "";
					}
					if( m_bFiring == true && m_nMMFiringSoundIndex == -1 )
					{
						m_sMMFiringSoundKey = Game.m_cGOB_SOUND_MMFIRING[ DSMisc.GetRnd( 0,1 ) ];
						m_nMMFiringSoundIndex = m_oGameEngine.DirectSound.PlaySound( m_sMMFiringSoundKey );
					}

					//Do our damage and update everyone on our island
					oSquare = oTargetIsland.GetSquare( oTargetBuilding.X,oTargetBuilding.Y );
					if( m_bFiring == true )
					{
						nDamage = ( m_nMMOffense / Game.m_nMMDAMAGEMODIFIER ) * nElapsedTime;
						if( bAttackMM == true )
						{
							oSquare.MMStrength -= nDamage;
							if( oSquare.MMStrength <= 0 )
							{
								m_oGame.AddMMExplosion( oSquare.X,oSquare.Y,oTargetIsland.GlobalID );
								oSquare.Armed = false;
							}
						}
						else
						{
							oSquare.DamageSquare( nDamage,oTargetIsland.GlobalID,"",false );
						}
					}
						  
					//Advance animations
					if( m_nTimeSinceAnimationChange > .7 && bTargetFound == true )
					{
						m_nTimeSinceAnimationChange = 0;
						m_nAnimationCell++;
						if( m_nAnimationCell > 3 && m_bFiring == false )
						{
							m_nAnimationCell = 0;
						}
						else if( m_nAnimationCell > 4 )
						{
							m_nAnimationCell = 0;
						}
					}
					else
					{
						m_nTimeSinceAnimationChange += nElapsedTime;
					}
				}
				else
				{
					m_bFiring = false;
					m_nAnimationCell = 0;
					m_nDirection = 2;
				}

try{
				///////////////////////////////////////////////////////////////////////////////////////////////
				//Have anyone left on the island fire back.  Get all the nearby squares
				///////////////////////////////////////////////////////////////////////////////////////////////
				oaPoints = Game.GetSquaresInRadius( Game.m_nLANDCOMBAT_MAXRANGE,m_nTargetX,m_nTargetY );
				foreach( System.Drawing.Point oPoint in oaPoints )
				{
try{
					oSquare = oTargetIsland.GetSquare( oPoint.X,oPoint.Y );
					if( oSquare != null && m_nMMStrength > 0 && 
						oSquare.SquareStatus == enumSquareStatus.Built &&
						(
							oSquare.DefenseHasFiredAtMMThisRound == false ||
							(bool)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_MMOFFENSEFIRESONCEPERROUNDONLY_CLMN ) == false
						)
					  )
					{
						oSquare.DefenseHasFiredAtMMThisRound = true;

						oValue = Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_MMOFFENSE_CLMN );
						if( oSquare.Armed == true &&
							oSquare.SquareStatus == enumSquareStatus.Built && 
							oValue != null && (double)oValue > 0 )
						{
							//Now that we know they have a range, find out if we are within it
							if( (long)( DSMath.Distance( m_nTargetX,m_nTargetY,oSquare.X,oSquare.Y ) ) <= 
								(long)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_MMOFFENSERANGE_CLMN ) )
							{
								m_nMMStrength -= nElapsedTime * ( (double)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_MMOFFENSE_CLMN ) / Game.m_nMMDAMAGEMODIFIER );
								if( m_nMMStrength <= 0 )
								{
									oSquare.Game.AddMMExplosion( m_nTargetX,m_nTargetY,oTargetIsland.GlobalID );
								}
							}
						}
					}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}

				}   
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}
			}
		}

		public void AddAAFire( long nSourceX,long nSourceY,bool bHits,long nPercToHitAt,long nXDeviation,long nYDeviation )
		{
			structAAFire oAAFire;

			oAAFire.m_nSourceX = nSourceX;
			oAAFire.m_nSourceY = nSourceY;
			oAAFire.m_bHits = bHits;
			oAAFire.m_nPercToHitAt = nPercToHitAt;
			oAAFire.m_nXDeviation = nXDeviation;
			oAAFire.m_nYDeviation = nYDeviation;
			oAAFire.m_bLaunchSoundFired = false;

			m_oAAFire.Add( DSMisc.GetGUID(),oAAFire );
		}


		public string Serialize()
		{
			string sRetVal = "";
			string sAAFire = "";
			structAAFire oLoopAAFire;


			//Assemble our AAFire
			for( int nLoopAAFireIndex=0 ; nLoopAAFireIndex<m_oAAFire.Count ; nLoopAAFireIndex++ )
			{
				oLoopAAFire = (structAAFire)m_oAAFire.GetByIndex( nLoopAAFireIndex );

				sAAFire +=	oLoopAAFire.m_nSourceX.ToString() +  m_cAAFIRE_SEP + 
							oLoopAAFire.m_nSourceY.ToString() +  m_cAAFIRE_SEP + 
							oLoopAAFire.m_bHits.ToString() +  m_cAAFIRE_SEP + 
							oLoopAAFire.m_nPercToHitAt.ToString() +  m_cAAFIRE_SEP + 
							oLoopAAFire.m_nXDeviation.ToString() +  m_cAAFIRE_SEP + 
							oLoopAAFire.m_nYDeviation.ToString() +  m_cAAFIRE_SEP;
			}

			//Now write all the square information
			sRetVal =	m_sSourceIslandGlobalID.ToString() + m_cATTACK_SEP +
						m_nSourceX.ToString() + m_cATTACK_SEP + 
						m_nSourceY.ToString() + m_cATTACK_SEP +
						m_sTargetIslandGlobalID.ToString() + m_cATTACK_SEP +
						m_nTargetX.ToString() + m_cATTACK_SEP + 
						m_nTargetY.ToString() + m_cATTACK_SEP + 
						m_sGlobalID + m_cATTACK_SEP + 
						m_bLanded.ToString() + m_cATTACK_SEP + 
						m_nLandedPosOffsetX.ToString() + m_cATTACK_SEP + 
						m_nLandedPosOffsetY.ToString() + m_cATTACK_SEP + 
						m_nDirection.ToString() + m_cATTACK_SEP + 
						m_nTimeSinceAnimationChange.ToString() + m_cATTACK_SEP + 
						m_nAnimationCell.ToString() + m_cATTACK_SEP + 
						m_bFiring.ToString() + m_cATTACK_SEP + 
						m_nPercentAlongPath.ToString() + m_cATTACK_SEP + 
						m_nMMStrength.ToString() + m_cATTACK_SEP + 
						m_nMMGraphicType.ToString() + m_cATTACK_SEP + 
						m_nMMOffense.ToString() + m_cATTACK_SEP + 
						m_nPercSinceLastActed.ToString() + m_cATTACK_SEP + 
						m_nColor.ToString() + m_cATTACK_SEP +
						sAAFire + m_cATTACK_SEP;


			return( sRetVal );
		}
		public void DeSerialize( string sRecord )
		{
			structAAFire oAAFire;
			string[] saAttackParamaters = null;
			string[] saAAFires = null;

			saAttackParamaters = DSMisc.Split( sRecord,m_cATTACK_SEP );

			//Now update this island
			m_sSourceIslandGlobalID	= saAttackParamaters[ 0 ];
			m_nSourceX				= Convert.ToInt64( saAttackParamaters[ 1 ] );
			m_nSourceY				= Convert.ToInt64( saAttackParamaters[ 2 ] );
			m_sTargetIslandGlobalID	= saAttackParamaters[ 3 ];
			m_nTargetX				= Convert.ToInt64( saAttackParamaters[ 4 ] );
			m_nTargetY				= Convert.ToInt64( saAttackParamaters[ 5 ] );
			m_sGlobalID				= saAttackParamaters[ 6 ];
			m_bLanded				= Convert.ToBoolean( saAttackParamaters[ 7 ] );
			m_nLandedPosOffsetX		= Convert.ToDouble( saAttackParamaters[ 8 ] );
			m_nLandedPosOffsetY		= Convert.ToDouble( saAttackParamaters[ 9 ] );
			m_nDirection			= Convert.ToInt64( saAttackParamaters[ 10 ] );
			m_nTimeSinceAnimationChange	= Convert.ToDouble( saAttackParamaters[ 11 ] );
			m_nAnimationCell		= Convert.ToInt64( saAttackParamaters[ 12 ] );
			m_bFiring				= Convert.ToBoolean( saAttackParamaters[ 13 ] );
			m_nPercentAlongPath		= Convert.ToDouble( saAttackParamaters[ 14 ] );
			m_nMMStrength			= Convert.ToDouble( saAttackParamaters[ 15 ] );
			m_nMMGraphicType		= Convert.ToInt64( saAttackParamaters[ 16 ] );
			m_nMMOffense			= Convert.ToInt64( saAttackParamaters[ 17 ] );
			m_nPercSinceLastActed	= Convert.ToInt64( saAttackParamaters[ 18 ] );
			m_nColor				= Convert.ToInt32( saAttackParamaters[ 19 ] );

				
			saAAFires = DSMisc.Split( saAttackParamaters[ 20 ],m_cAAFIRE_SEP );

			//Add our AA fire
			for( int i=0 ; i<saAAFires.Length-1 ; i+=6 )
			{
				oAAFire.m_nSourceX		= Convert.ToInt64( saAAFires[ i ] );
				oAAFire.m_nSourceY		= Convert.ToInt64( saAAFires[ i+1 ] );
				oAAFire.m_bHits			= Convert.ToBoolean( saAAFires[ i+2 ] );
				oAAFire.m_nPercToHitAt	= Convert.ToInt64( saAAFires[ i+3 ] );
				oAAFire.m_nXDeviation	= Convert.ToInt64( saAAFires[ i+4 ] );
				oAAFire.m_nYDeviation	= Convert.ToInt64( saAAFires[ i+5 ] );
				oAAFire.m_bLaunchSoundFired = false;

				m_oAAFire.Add( DSMisc.GetGUID(),oAAFire );
			}
		}



		#region Properties
		public string SourceIslandGlobalID
		{
			get
			{
				return( m_sSourceIslandGlobalID );
			}
			set
			{
				m_sSourceIslandGlobalID = value;
			}
		}
		public long SourceX
		{
			get
			{
				return( m_nSourceX );
			}
			set
			{
				m_nSourceX = value;
			}
		}
		public long SourceY
		{
			get
			{
				return( m_nSourceY );
			}
			set
			{
				m_nSourceY = value;
			}
		}
		public string TargetIslandGlobalID
		{
			get
			{
				return( m_sTargetIslandGlobalID );
			}
			set
			{
				m_sTargetIslandGlobalID = value;
			}
		}
		public long TargetX
		{
			get
			{
				return( m_nTargetX );
			}
			set
			{
				m_nTargetX = value;
			}
		}
		public long TargetY
		{
			get
			{
				return( m_nTargetY );
			}
			set
			{
				m_nTargetY = value;
			}
		}
		public double PercentAlongPath
		{
			get
			{
				return( m_nPercentAlongPath );
			}
			set
			{
				m_nPercentAlongPath = value;
			}
		}			
		public double PercSinceLastActed
		{
			get
			{
				return( m_nPercSinceLastActed );
			}
			set
			{
				m_nPercSinceLastActed = value;
			}
		}			
		public string GlobalID
		{
			get
			{
				return( m_sGlobalID );
			}
			set
			{
				m_sGlobalID = value;
			}
		}
		public DSSortedList AAFire
		{
			get
			{
				return( m_oAAFire );
			}
			set
			{
				m_oAAFire = value;
			}
		}
		public bool Landed
		{
			get
			{
				return( m_bLanded );
			}
			set
			{
				m_bLanded = value;
			}
		}

		public bool Launched
		{
			get
			{
				return( m_bLaunched );
			}
			set
			{
				m_bLaunched = value;
			}
		}

		public double LandedPosOffsetX
		{
			get
			{
				return( m_nLandedPosOffsetX );
			}
			set
			{
				m_nLandedPosOffsetX = value;
			}
		}
		public double LandedPosOffsetY
		{
			get
			{
				return( m_nLandedPosOffsetY );
			}
			set
			{
				m_nLandedPosOffsetY = value;
			}
		}
		public long Direction
		{
			get
			{
				return( m_nDirection );
			}
			set
			{
				m_nDirection = value;
			}
		}
		public double LastAnimationChange
		{
			get
			{
				return( m_nTimeSinceAnimationChange );
			}
			set
			{
				m_nTimeSinceAnimationChange = value;
			}
		}
		public long AnimationCell
		{
			get
			{
				return( m_nAnimationCell );
			}
			set
			{
				m_nAnimationCell = value;
			}
		}
		public bool Firing
		{
			get
			{
				return( m_bFiring );
			}
			set
			{
				m_bFiring = value;
			}
		}
		public double MMStrength
		{
			get
			{
				return( m_nMMStrength );
			}
			set
			{
				m_nMMStrength = value;
			}
		}
		public double MMOffense
		{
			get
			{
				return( m_nMMOffense );
			}
			set
			{
				m_nMMOffense = value;
			}
		}
		public long MMGraphicType
		{
			get
			{
				return( m_nMMGraphicType );
			}
			set
			{
				m_nMMGraphicType = value;
			}
		}
		
		public double LastSent
		{
			get
			{
				return( m_nLastSent );
			}
			set
			{
				m_nLastSent = value;
			}
		}
		public int SoundIndex
		{
			get
			{
				return( m_nMMFiringSoundIndex );
			}
			set
			{
				m_nMMFiringSoundIndex = value;
			}
		}
		public string SoundKey
		{
			get
			{
				return( m_sMMFiringSoundKey );
			}
			set
			{
				m_sMMFiringSoundKey = value;
			}
		}

		public DSGameEngine GameEngine
		{
			get
			{
				return( m_oGameEngine );
			}
			set
			{
				m_oGameEngine = value;
			}
		}
		public Game Game
		{
			get
			{
				return( m_oGame );
			}
			set
			{
				m_oGame = value;
			}
		}
		public int Color
		{
			get
			{
				return( m_nColor );
			}
			set
			{
				m_nColor = value;
			}
		}
		#endregion
	}
}
