using System;
using System.Drawing;
using System.Collections;

using DarkStrideToolbox;


namespace MetalMarines
{
	public enum enumAILevels
	{
		Noob,
		Neophite,
		Average,
		Hard,
		Obscene,
		MaxIndex
	}


	public class AI
	{
		#region Properties
		private string m_sName = "";
		private enumAILevels m_nAILevel = enumAILevels.Noob;
		private string m_sManagerGlobalID = "";
		private string m_sGlobalID = "";
		private Game m_oGame = null;
		private MetalMarines m_oApp = null;
		private bool m_nHavePlacedBases = false;
		private ArrayList m_naBuildingGobsThatCanBePurchased = null;
		private double m_nTotalElapsedTime = 0;

		private DateTime m_dtLastPurchaseCheck = DateTime.MinValue;
		private DateTime m_dtLastMMAttackCheck = DateTime.MinValue;
		private DateTime m_dtLastMissleAttackCheck = DateTime.MinValue;
		private DateTime m_dtLastAntimatterAttackCheck = DateTime.MinValue;
		#endregion


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


		public void Advance( double nElapsedTime,Island oAIsIsland )
		{
			m_nTotalElapsedTime += nElapsedTime;

			if( m_nAILevel == enumAILevels.Noob )
			{
				AIProcessing( 1,nElapsedTime,oAIsIsland );
			}
			else if( m_nAILevel == enumAILevels.Neophite )
			{
				AIProcessing( 2,nElapsedTime,oAIsIsland );
			}
			else if( m_nAILevel == enumAILevels.Average )
			{
				AIProcessing( 3,nElapsedTime,oAIsIsland );
			}
			else if( m_nAILevel == enumAILevels.Hard )
			{
				AIProcessing( 4,nElapsedTime,oAIsIsland );
			}
			else if( m_nAILevel == enumAILevels.Obscene )
			{
				AIProcessing( 5,nElapsedTime,oAIsIsland );
			}			
		}
		public void AIProcessing( int nLevel,double nElapsedTime,Island oAIsIsland )
		{
			bool bSuccess = false;
			string sFailureMessage = "";
			double nFactor = 0;
			long nBasesToPlace = 0;
			long nGobBuildingKey = 0;
			long nX = 0, nY = 0;
			long nPriceToBuy = 0;
			int nTemp = 0;
			DSSortedList oMissles = null;
			DSSortedList oSquaresToUse = null;
			ArrayList naGobsToGet = null;
			MissleAttack oLoopAttack = null;
			MMAttack oMMAttack = null;
			Island oTargetIsland = null;
			Square oTargetSquare = null;
			Square oSquare = null;
			TimeSpan oSpan = TimeSpan.MinValue;


			//Give the AI a fuel bonus too
			if( nLevel >= 4 )
			{
				nFactor = nLevel / 4.0;
				oAIsIsland.Credits += oAIsIsland.CreditIncomePerSecond * nElapsedTime * nFactor; 
				nFactor = nLevel / 3.0;
				oAIsIsland.Fuel += oAIsIsland.FuelIncomePerSecond * nElapsedTime * nFactor; 
			}


			//////////////////////////////////////////////////////////////////////////////////////////////////////
			//Do we have bases left to place?  If so do so.
			//////////////////////////////////////////////////////////////////////////////////////////////////////
			if( m_nHavePlacedBases == false )
			{
				m_nHavePlacedBases = true;

				//Count how many bases already exist
				nBasesToPlace = oAIsIsland.MaxBasesToPlace - 
								oAIsIsland.GetNumberOfBasesOnIsland();

				for( int i=0 ; i<200 && nBasesToPlace>0 ; i++ )
				{					
					nX = DSMisc.GetRnd( 1,oAIsIsland.Width-2 );
					nY = DSMisc.GetRnd( 1,oAIsIsland.Height-2 );
					oSquare = oAIsIsland.GetSquare( nX,nY );
					if( oSquare.SquareStatus == enumSquareStatus.Empty &&
						(bool)Game.GetTrnStat( oSquare.SquareType,Game.m_cGOB_TRNSTATS_CANBEBUILTON_CLMN ) == true )
					{
						nBasesToPlace--;
						oSquare.SetSquare( Game.m_cGOB_BASE_KEY );
					}
				}

				oAIsIsland.BasesPlaced = true;
				m_oApp.SendIslandUpdate( oAIsIsland );
			}


			//////////////////////////////////////////////////////////////////////////////////////////////////////
			//Check for purchases every 20 seconds
			//////////////////////////////////////////////////////////////////////////////////////////////////////
try
{

			oSpan = DateTime.Now - m_dtLastPurchaseCheck;
			if( oSpan.TotalSeconds > ( (int)enumAILevels.MaxIndex - nLevel ) * 10 && nLevel > 1 )
			{
				m_dtLastPurchaseCheck = DateTime.Now;

				//Find our building list
				if( m_naBuildingGobsThatCanBePurchased == null )
				{
					m_naBuildingGobsThatCanBePurchased = GetListOfPurchaseAbleBuildings();
				}

				//For now randomly purchase
				for( int nTries=0 ; nTries<nLevel ; nTries++ )
				{
					nTemp = DSMisc.GetRnd( 0,m_naBuildingGobsThatCanBePurchased.Count-1 );
					nGobBuildingKey = (long)m_naBuildingGobsThatCanBePurchased[ nTemp ];
					nPriceToBuy = Game.GetPriceOfBuilding( nGobBuildingKey );

					//Can we aford this building?
					if( nPriceToBuy < oAIsIsland.Credits )
					{
						//Find a random place to put it
						bSuccess = false;
						for( int i=0 ; i<20 && bSuccess == false ; i++ )
						{
							oTargetSquare = PickRandomSquare( oAIsIsland );
							if( oTargetSquare.SquareStatus == enumSquareStatus.Empty &&
								(bool)Game.GetTrnStat( oTargetSquare.SquareType,Game.m_cGOB_TRNSTATS_CANBEBUILTON_CLMN ) == true )
							{
								oTargetSquare.SetSquare( nGobBuildingKey );
								oAIsIsland.Credits -= nPriceToBuy;
								bSuccess = true;
							}
						}							
					}
				}
			}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}


			//////////////////////////////////////////////////////////////////////////////////////////////////////
			//Check for MM launch every 20 seconds
			//////////////////////////////////////////////////////////////////////////////////////////////////////
try
{
			oSpan = DateTime.Now - m_dtLastMMAttackCheck;
			if( nLevel > 0 &&
				oSpan.TotalSeconds > 10 + ( (int)enumAILevels.MaxIndex - nLevel - 1 ) * 10 && 
				m_nTotalElapsedTime > 30 + ( (int)enumAILevels.MaxIndex - nLevel - 1 ) * 30 )
			{
				m_dtLastMMAttackCheck = DateTime.Now;

				//Pick a target
				oTargetIsland = FindIslandToAttack( oAIsIsland,( nLevel >= 4 ) );

				if( oTargetIsland != null )
				{
					//Find squares that are armed
					naGobsToGet = GetGobsThatCanDo( Game.m_cGOB_BLDSTATS_FIRESMM_CLMN );
					oSquaresToUse = GetMySquares( oAIsIsland,naGobsToGet );

					for( int nSquareIndex=0 ; nSquareIndex<oSquaresToUse.Count && nSquareIndex<=nLevel ; nSquareIndex++ )
					{
						oSquare = (Square)oSquaresToUse.GetByIndex( nSquareIndex );

						//Pick a target for this missle
						oTargetSquare = FindSquareToAttack( oAIsIsland,oTargetIsland );

						//Launch the attack
						oMMAttack = m_oGame.AttackWithMetalMarine( oAIsIsland.GlobalID,oSquare.X,oSquare.Y,
																oTargetIsland.GlobalID,oTargetSquare.X,oTargetSquare.Y,
																oSquare.MMStrength,
																(double)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_MMOFFENSE_CLMN ),
																out bSuccess,out sFailureMessage );
					
						if( bSuccess == true )
						{
							oSquare.FireWeapon();

							//Send an update
							m_oApp.SendMMAttackUpdate( oMMAttack );
							break;
						}
					}
				}
			}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}



			//////////////////////////////////////////////////////////////////////////////////////////////////////
			//Check for missle launch every 1 seconds
			//////////////////////////////////////////////////////////////////////////////////////////////////////
try
{			
			oSpan = DateTime.Now - m_dtLastMissleAttackCheck;
			if( oSpan.TotalSeconds > 1 + ( (int)enumAILevels.MaxIndex - nLevel - 1 ) * 2 && 
				m_nTotalElapsedTime > 30 + ( (int)enumAILevels.MaxIndex - nLevel - 1 ) * 20 )
			{
				m_dtLastMissleAttackCheck = DateTime.Now;

				//Pick a target
				oTargetIsland = FindIslandToAttack( oAIsIsland,( nLevel >= 4 ) );

				if( oTargetIsland != null )
				{
					//Find squares that are armed
					naGobsToGet = GetGobsThatCanDo( Game.m_cGOB_BLDSTATS_FIRESMISSLES_CLMN );
					oSquaresToUse = GetMySquares( oAIsIsland,naGobsToGet );

					for( int nSquareIndex=0 ; nSquareIndex<oSquaresToUse.Count && nSquareIndex<=nLevel ; nSquareIndex++ )
					{
						oSquare = (Square)oSquaresToUse.GetByIndex( nSquareIndex );

						//Pick a target for this missle
						oTargetSquare = FindSquareToAttack( oAIsIsland,oTargetIsland );

						//Now attack
						oMissles = m_oGame.AddMissleAttacks( oAIsIsland.GlobalID,oSquare.X,oSquare.Y,
										oTargetIsland.GlobalID,oTargetSquare.X,oTargetSquare.Y,
										(long)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_NUMSHOTS_CLMN ),
										true,false,
										(long)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_EXPLOSIONRADIUS ),
										out bSuccess,out sFailureMessage );

						oSquare.FireWeapon();

						//Send an update
						for( int nLoopMissleIndex=0 ; nLoopMissleIndex<oMissles.Count ; nLoopMissleIndex++ )
						{
							oLoopAttack = (MissleAttack)oMissles.GetByIndex( nLoopMissleIndex );
							m_oApp.SendMMMsg( enumNetMsg.MissleAttack,oLoopAttack.Serialize() );
							m_oGame.MissleAttacks.Add( oLoopAttack.GlobalID,oLoopAttack );
						}
					}
				}
			}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}


			//////////////////////////////////////////////////////////////////////////////////////////////////////
			//Check for anti-mater missile launch every 15 seconds
			//////////////////////////////////////////////////////////////////////////////////////////////////////
try
{			
			oSpan = DateTime.Now - m_dtLastAntimatterAttackCheck;
			if( ( oSpan.TotalSeconds > 90 && nLevel == 3 ) ||
				( oSpan.TotalSeconds > 10 && nLevel == 4 ) ||
				nLevel > 4 )
			{
				m_dtLastAntimatterAttackCheck = DateTime.Now;

				//Pick a target
				oTargetIsland = FindIslandToAttack( oAIsIsland,( nLevel >= 4 ) );

				if( oTargetIsland != null )
				{
					//Find squares that are armed
					naGobsToGet = GetGobsThatCanDo( Game.m_cGOB_BLDSTATS_FIRESANTIMATTER );
					oSquaresToUse = GetMySquares( oAIsIsland,naGobsToGet );

					for( int nSquareIndex=0 ; nSquareIndex<oSquaresToUse.Count ; nSquareIndex++ )
					{
						oSquare = (Square)oSquaresToUse.GetByIndex( nSquareIndex );

						//Pick a target for this missle
						oTargetSquare = FindSquareToAttack( oAIsIsland,oTargetIsland );

						//Now attack
						oMissles = m_oGame.AddMissleAttacks( oAIsIsland.GlobalID,oSquare.X,oSquare.Y,
										oTargetIsland.GlobalID,oTargetSquare.X,oTargetSquare.Y,
										1,false,true,
										(long)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_EXPLOSIONRADIUS ),
										out bSuccess,out sFailureMessage );

						oSquare.FireWeapon();

						//Send an update
						for( int nLoopMissleIndex=0 ; nLoopMissleIndex<oMissles.Count ; nLoopMissleIndex++ )
						{
							oLoopAttack = (MissleAttack)oMissles.GetByIndex( nLoopMissleIndex );
							m_oApp.SendMMMsg( enumNetMsg.MissleAttack,oLoopAttack.Serialize() );
							m_oGame.MissleAttacks.Add( oLoopAttack.GlobalID,oLoopAttack );
						}

						//We found one so exit
						break;
					}
				}
			}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}
		}


		private Island FindIslandToAttack( Island oAIsIsland,bool bHumanIslandOnly )
		{
			Island oLoopIsland = null;
			int nIslandIndex = 0;
			long nRandomIslandIndex = 0;
			long nLoopCount = 0;
			bool bTempHumanIslandOnly = false;


			if( bHumanIslandOnly == true )
			{
				bTempHumanIslandOnly = ( GetHumanIslandCount() > 0 );
			}
			else
			{
				bTempHumanIslandOnly = false;
			}

			nRandomIslandIndex = DSMisc.GetRnd( 0,m_oGame.Islands.Count-1-1 );
			//for( int i=0 ; i<=m_oGame.Islands.Count ; i++ )
			while( nRandomIslandIndex >= 0 && nLoopCount < 3 )
			{
				oLoopIsland = (Island)m_oGame.Islands.GetByIndex( nIslandIndex );
				//If this is our island, then incrament
				if( oLoopIsland.GlobalID != oAIsIsland.GlobalID && 
					oLoopIsland.ImDead == false && oLoopIsland.BasesPlaced == true &&
					(
						bTempHumanIslandOnly == false ||
						oLoopIsland.AIOwnerGlobalID.Length == 0
					)
				  )
				{
					nRandomIslandIndex--;
				}
				else
				{
					oLoopIsland = null;
				}

				nIslandIndex++;
				if( nIslandIndex >= m_oGame.Islands.Count )
				{
					nIslandIndex = 0;
					nLoopCount++;
				}
			}


			return( oLoopIsland );
		}
		private int GetHumanIslandCount()
		{
			Island oLoopIsland = null;
			int nHumanIslandCount = 0;


			for( int i=0 ; i<m_oGame.Islands.Count ; i++ )
			{
				oLoopIsland = (Island)m_oGame.Islands.GetByIndex( i );
				//If this island is human
				if( oLoopIsland.AIOwnerGlobalID.Length == 0 )
				{
					nHumanIslandCount++;
				}
			}


			return( nHumanIslandCount );
		}


		private Square FindSquareToAttack( Island oAIIsland,Island oTargetIsland )
		{
			Square oTargetSquare = null;
			long nDist = 0;
			long nTargetDist = 0;
			long nTargetX = 0;
			long nTargetY = 0;
			long nArrowX = 0;
			long nArrowY = 0;
			Square oSquare = null;


try
{
			//0=UpperRight, 1=UpperLeft, 2=LowerLeft, 3=LowerRight
			if( oTargetIsland.Direction == 0 )
			{
				nArrowX = oTargetIsland.Width;
				nArrowY = 0;
			}
			else if( oTargetIsland.Direction == 1 )
			{
				nArrowX = 0;
				nArrowY = 0;
			}
			else if( oTargetIsland.Direction == 2 )
			{
				nArrowX = 0;
				nArrowY = oTargetIsland.Height;
			}
			else if( oTargetIsland.Direction == 3 )
			{
				nArrowX = oTargetIsland.Width;
				nArrowY = oTargetIsland.Height;
			}


			//Start by finding the closest visible target
			for( int nX=0 ; nX<oTargetIsland.Width ; nX++ )
			{
				for( int nY=0 ; nY<oTargetIsland.Height ; nY++ )
				{
try
{
					oSquare = oTargetIsland.GetSquareICanSee( nX,nY,oAIIsland );

					if( oSquare.SquareStatus == enumSquareStatus.Built &&
						(bool)Game.GetBldStat( oSquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_ISARROW_CLMN ) == false )
					{
						//Pick a target for this missle
						nDist = Game.GetDistance( nArrowX,nArrowY,oSquare.X,oSquare.Y );

						if( nDist < nTargetDist || oTargetSquare == null )
						{
							nTargetX = nX;
							nTargetY = nY;
							nTargetDist = nDist;
							oTargetSquare = oSquare;
						}
					}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}
				}
			}

			//Did we find anything?  If not pick random
			if( oTargetSquare == null )
			{
				oTargetSquare = PickRandomSquare( oTargetIsland );
			}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}

			return( oTargetSquare );
		}
		private Square PickRandomSquare( Island oTargetIsland )
		{
			long nTargetX = 0;
			long nTargetY = 0;
			Square oRetVal = null;

			nTargetX = DSMisc.GetRnd( 1,oTargetIsland.Width-1 );
			nTargetY = DSMisc.GetRnd( 1,oTargetIsland.Height-1 );
			oRetVal = oTargetIsland.GetSquare( nTargetX,nTargetY );

			return( oRetVal );
		}
		private DSSortedList GetMySquares( Island oAIsIsland,ArrayList naGobKeyToCheck )
		{
			DSSortedList oRetVal = new DSSortedList();
			Square oSquare = null;


			//Now randomly attack if we have any open things to attack with
			for( int nX=0 ; nX<oAIsIsland.Width ; nX++ )
			{
				for( int nY=0 ; nY<oAIsIsland.Height ; nY++ )
				{
					oSquare = oAIsIsland.GetSquare( nX,nY );

					if( oSquare.Armed == true &&
						oSquare.SquareStatus == enumSquareStatus.Built &&
						naGobKeyToCheck.Contains( oSquare.GobBuildingKey ) == true )
					{
						oRetVal.Add( oSquare.X.ToString() + "," + oSquare.Y.ToString(),oSquare );
					}
				}
			}


			return( oRetVal );
		}

		private ArrayList GetGobsThatCanDo( string sBooleanColToCheck )
		{
			long nGobBuildingIndex = -1;
			object oValue = null;
			DSGobTable oTable = null;
			ArrayList oRetVal = new ArrayList();


			//Find any tables that fit this
			oTable = Game.GobFile.GetTable( Game.m_cGOB_BLDSTATS_TABLE );
			for( int nRow=0 ; nRow<oTable.RowCount ; nRow++ )
			{
				oValue = oTable.GetData( sBooleanColToCheck,nRow );
				if( oValue != null && oValue.GetType().ToString() != "System.DBNull" )
				{
					if( (bool)oValue == true )
					{
						nGobBuildingIndex = (long)oTable.GetData( Game.m_cGOB_BLDSTATS_BLDKEY_CLMN,nRow );
						oRetVal.Add( nGobBuildingIndex );
					}
				}
			}


			return( oRetVal );
		}

		private ArrayList GetListOfPurchaseAbleBuildings()
		{
			long nGobBuildingKey = 0;
			bool bCanBeBuilt = false;
			bool bIsAntiMatter = false;
			DSGobTable oTable = null;
			ArrayList naPurchasableGobs = new ArrayList();


			//For now randomly purchase
			oTable = Game.GobFile.GetTable( Game.m_cGOB_BLDSTATS_TABLE );
			for( int nRow=0 ; nRow<oTable.RowCount ; nRow++ )
			{
				bCanBeBuilt = (bool)oTable.GetData( Game.m_cGOB_BLDSTATS_CANBEBUILT_CLMN,nRow );
				bIsAntiMatter = (bool)oTable.GetData( Game.m_cGOB_BLDSTATS_FIRESANTIMATTER,nRow );
				if( bCanBeBuilt == true && bIsAntiMatter == false )
				{
					nGobBuildingKey = (long)oTable.GetData( Game.m_cGOB_BLDSTATS_BLDKEY_CLMN,nRow );
					naPurchasableGobs.Add( nGobBuildingKey );
				}
			}


			return( naPurchasableGobs );
		}



		#region Properties
		public string Name
		{
			get
			{
				return( m_sName );
			}
			set
			{
				m_sName = value;
			}
		}
		public enumAILevels AILevel
		{
			get
			{
				return( m_nAILevel );
			}
			set
			{
				m_nAILevel = value;
			}
		}
		public string ManagerGlobalID
		{
			get
			{
				return( m_sManagerGlobalID );
			}
			set
			{
				m_sManagerGlobalID = value;
			}
		}
		public string GlobalID
		{
			get
			{
				return( m_sGlobalID );
			}
			set
			{
				m_sGlobalID = value;
			}
		}
		public Game Game
		{
			get
			{
				return( m_oGame );
			}
			set
			{
				m_oGame = value;
			}
		}
		public MetalMarines App
		{
			get
			{
				return( m_oApp );
			}
			set
			{
				m_oApp = value;
			}
		}
		#endregion
	}
}
