using System;
using System.Collections.Generic;
using Server.Mobiles;
using Server.Regions;
using Server.Misc;

using Server.Commands;

namespace Server.EventSystem
{	
	public class TownInvasion : BaseEvent
	{
		public static TimeSpan m_TIEventRate = TimeSpan.FromHours( 12.0 );
		
		#region Private Variables

		private int m_MinSpawnZ;
		private int m_MaxSpawnZ;
		private int m_LastMsg;

		private bool m_Broadcast = true;
		private bool m_FinalStage;

		private Point3D m_Top = new Point3D(4394, 1058, 30);
		private Point3D m_Bottom = new Point3D(4481, 1173, 0);
		private Map m_SpawnMap = Map.Felucca;

		private List<Mobile> m_Spawned;

		private TownMonsterType m_TownMonsterType = TownMonsterType.OrcsandRatmen;
		private TownChampionType m_TownChampionType = TownChampionType.Barracoon;
		
		private double m_ParagonChance = 0.05;
		private bool m_AlwaysMurderer = false;
		private bool m_IsRunning;
		
		private DateTime m_TimeNextScheduledEvent = DateTime.MinValue;
		private DateTime m_TimeLastEvent = DateTime.MinValue;
		
		private string m_TownInvaded = "Moonglow";
		
		#endregion

		#region Public Variables

		[CommandProperty( AccessLevel.Administrator )]
		public int MinSpawnZ { get { return m_MinSpawnZ; } set { m_MinSpawnZ = value; } }

		[CommandProperty( AccessLevel.Administrator )]
		public int MaxSpawnZ { get { return m_MaxSpawnZ; } set { m_MaxSpawnZ = value; } }
		
		[CommandProperty( AccessLevel.GameMaster )]
		public bool Broadcast { get { return m_Broadcast; } set { m_Broadcast = value; } }

		[CommandProperty( AccessLevel.Administrator )]
		public Point3D Top { get { return m_Top; } set { m_Top = value; } }

		[CommandProperty( AccessLevel.Administrator )]
		public Point3D Bottom { get { return m_Bottom; } set { m_Bottom = value; } }
		
		[CommandProperty( AccessLevel.Administrator )]
		public Map SpawnMap { get { return m_SpawnMap; } set { m_SpawnMap = value; } }

		public List<Mobile> Spawned { get { return m_Spawned; } set { m_Spawned = value; } }

		[CommandProperty( AccessLevel.GameMaster )]
		public TownMonsterType TownMonsterType { get { return m_TownMonsterType; } set { m_TownMonsterType = value; } }

		[CommandProperty( AccessLevel.GameMaster )]
		public TownChampionType TownChampionType { get { return m_TownChampionType; } set { m_TownChampionType = value; } }
		
		[CommandProperty( AccessLevel.GameMaster )]
		public double ParagonChance { get { return m_ParagonChance; } set { m_ParagonChance = value; } }
		
		[CommandProperty( AccessLevel.GameMaster )]
		public bool AlwaysMurderer { get { return m_AlwaysMurderer; } set { m_AlwaysMurderer = value; } }
		
		[CommandProperty( AccessLevel.GameMaster )]
		public bool IsRunning { get { return m_IsRunning; } set { m_IsRunning = value; } }
		
		[CommandProperty( AccessLevel.GameMaster, true )]
		public DateTime TimeNextScheduledEvent { get { return m_TimeNextScheduledEvent; } set { m_TimeNextScheduledEvent = value; } }
		
		[CommandProperty( AccessLevel.GameMaster, true )]
		public DateTime TimeLastEvent { get { return m_TimeLastEvent; } set { m_TimeLastEvent = value; } }
		
		[CommandProperty( AccessLevel.GameMaster )]
		public TimeSpan TIEventRate { get { return m_TIEventRate; } set { m_TIEventRate = value; } }
		
		[CommandProperty( AccessLevel.GameMaster )]
		public string TownInvaded { get { return m_TownInvaded; } set { m_TownInvaded = value; } }

		#endregion

		#region Property Overrides

		public override EventType EventType { get { return EventType.TownInvasion; } }
		public override string EventName { get { return "Town Invasion"; } }
		public override TimeSpan TimerFrequency { get { return TimeSpan.FromSeconds( 1.0 ); } }
		
		public override Map StartingMap { get { return Map.Felucca; } }
		public override Point3D StartingLocation { get { return new Point3D(6804, 2233, 0); } }

		#endregion

		#region Constructor

		public TownInvasion()
		{
			m_Spawned = new List<Mobile>();
		}

		#endregion

		#region Method Overrides

		protected override void OnEnable()
		{
		}

		protected override void OnDisable()
		{

		}

		public override void OnStart()
		{
			if( !this.IsRunning )
			{
				switch( Utility.Random(6) )
				{
					/*case 0:  //Britain Doesn't Get Invaded Anymore
					{
						this.Top = new Point3D(1426, 1554, 30);
						this.Bottom = new Point3D(1490, 1735, 0);
						this.MinSpawnZ = 0;
						this.MaxSpawnZ = 31;
						this.SpawnMap = Map.Felucca;
						this.TownInvaded = "Britain";
						this.TownMonsterType = TownMonsterType.OrcsandRatmen;
						this.TownChampionType = TownChampionType.Barracoon;
						
						break;
					}*/
					case 0:  //Moonglow
					{
						this.Top = new Point3D(4394, 1058, 30);
						this.Bottom = new Point3D(4481, 1173, 0);
						this.MinSpawnZ = 0;
						this.MaxSpawnZ = 31;
						this.SpawnMap = Map.Felucca;
						this.TownInvaded = "Moonglow";
						this.TownMonsterType = TownMonsterType.Abyss;
						this.TownChampionType = TownChampionType.Rikktor;
						
						break;
					}
					case 1:  //Minoc
					{
						this.Top = new Point3D(2443, 420, 15);
						this.Bottom = new Point3D(2520, 539, 0);
						this.MinSpawnZ = 0;
						this.MaxSpawnZ = 16;
						this.SpawnMap = Map.Felucca;
						this.TownInvaded = "Minoc";
						this.TownMonsterType = TownMonsterType.OreElementals;
						this.TownChampionType = TownChampionType.LordOaks;
						
						break;
					}
					case 2:  //Delucia
					{
						this.Top = new Point3D(5171, 3980, 41);
						this.Bottom = new Point3D(5300, 4040, 39);
						this.MinSpawnZ = 39;
						this.MaxSpawnZ = 42;
						this.SpawnMap = Map.Felucca;
						this.TownInvaded = "Delucia";
						this.TownMonsterType = TownMonsterType.Ophidian;
						this.TownChampionType = TownChampionType.Mephitis;
						
						break;
					}
					case 3:  //Ocllo
					{
						this.Top = new Point3D(3617, 2482, 0);
						this.Bottom = new Point3D(3712, 2630, 20);
						this.MinSpawnZ = 0;
						this.MaxSpawnZ = 21;
						this.SpawnMap = Map.Felucca;
						this.TownInvaded = "Ocllo";
						this.TownMonsterType = TownMonsterType.Elementals;
						this.TownChampionType = TownChampionType.Semidar;
						
						break;
					}
					case 4:  //Skara Brae
					{
						this.Top = new Point3D(577, 2131, -90);
						this.Bottom = new Point3D(634, 2234, -90);
						this.MinSpawnZ = 0;
						this.MaxSpawnZ = 1;
						this.SpawnMap = Map.Felucca;
						this.TownInvaded = "Skara Brae";
						this.TownMonsterType = TownMonsterType.Humanoid;
						this.TownChampionType = TownChampionType.Serado;
						
						break;
					}
					case 5:  //Yew
					{
						this.Top = new Point3D(457, 913, 30);
						this.Bottom = new Point3D(662, 1117, 0);
						this.MinSpawnZ = 0;
						this.MaxSpawnZ = 0;
						this.SpawnMap = Map.Felucca;
						this.TownInvaded = "Yew";
						this.TownMonsterType = TownMonsterType.OrcsandRatmen;
						this.TownChampionType = TownChampionType.Barracoon;
						
						break;
					}
				}
				
				foreach ( Region r in Region.Regions )
				{ 
					if ( r is GuardedRegion && r.Name == this.TownInvaded ) 
					{ 
						((GuardedRegion)r).Disabled = true; 
					} 
				}
		
				Spawn();
				this.m_TimeLastEvent = DateTime.Now;
				this.m_TimeNextScheduledEvent = DateTime.Now + TIEventRate;
				
				//World.Broadcast( 0x35, true, "{0} is being invaded!  The guards have fled! Help save it's citzens!", this.m_TownInvaded);
				string bc = this.m_TownInvaded + " is being invaded!  The guards have fled! Help save it's citizens!";
				Server.Mobiles.InvasionAnnouncer.SetAnnouncement( bc );
				this.IsRunning = true;
			}
		}

		protected override void OnStop()
		{
			Despawn();
			
			foreach ( Region r in Region.Regions )
         	{ 
            	if ( r is GuardedRegion && r.Name == this.TownInvaded  ) 
            	{ 
               		((GuardedRegion)r).Disabled = false; 
            	} 
         	}
			
			Server.Mobiles.InvasionAnnouncer.DeleteAnnouncements();
			this.IsRunning = false;  //it's not running anymore
		}

		public override void OnTick( int ticks )
		{
			int count = 0;

			for ( int i = 0; i < m_Spawned.Count; ++i )
				if ( m_Spawned[i] != null && !m_Spawned[i].Deleted && m_Spawned[i].Alive )
					++count;

			if ( !m_FinalStage ) //Monsters
			{
				if ( count == 0 ) //All monsters have been slayed
					SpawnChamp();
			}
			else //Champion
			{
				if ( count == 0 ) //Champion is dead
				{
					if ( m_Broadcast )
						Announce( 1161, "The city of {0} has been saved and the guards have returned to their posts.  The citizens of {1} thank you!", m_TownInvaded, m_TownInvaded );

					Stop();
				}
			}
		}

		public override void Serialize( GenericWriter writer )
		{
			base.Serialize( writer );

			writer.Write( (int)3 ); //version
			
			//v 3
			writer.Write( m_Spawned, true );
			
			// v 2
			writer.Write( m_IsRunning );
			
			// v 1
			writer.Write( m_ParagonChance );
			writer.Write( m_AlwaysMurderer );
			writer.Write( m_TimeNextScheduledEvent );
			writer.Write( m_TimeLastEvent );
			writer.Write( m_TIEventRate );

			// v 0
			writer.Write( m_MinSpawnZ );
			writer.Write( m_MaxSpawnZ );

			writer.Write( m_Broadcast );

			writer.Write( m_Top );
			writer.Write( m_Bottom );
			writer.Write( m_SpawnMap );

			writer.WriteEncodedInt( (int)m_TownMonsterType );
			writer.WriteEncodedInt( (int)m_TownChampionType );
		}

		public override void Deserialize( GenericReader reader )
		{
			base.Deserialize( reader );

			int version = reader.ReadInt();

			switch ( version )
			{
				case 3:
				{
					m_Spawned = reader.ReadStrongMobileList();
					goto case 2;
				}
				case 2:
				{
					m_IsRunning = reader.ReadBool();
					goto case 1;
				}
				case 1:
				{
					m_ParagonChance = reader.ReadDouble(); 
					m_AlwaysMurderer = reader.ReadBool();
					m_TimeNextScheduledEvent = reader.ReadDateTime();
					m_TimeLastEvent = reader.ReadDateTime();
					m_TIEventRate = reader.ReadTimeSpan();
					goto case 0;
				}
				case 0:
				{
					m_MinSpawnZ = reader.ReadInt();
					m_MaxSpawnZ = reader.ReadInt();

					m_Broadcast = reader.ReadBool();

					m_Top = reader.ReadPoint3D();
					m_Bottom = reader.ReadPoint3D();
					m_SpawnMap = reader.ReadMap();

					m_TownMonsterType = (TownMonsterType)reader.ReadEncodedInt();
					m_TownChampionType = (TownChampionType)reader.ReadEncodedInt();


					break;
				}
			}
			
			OnStop();
			m_IsRunning = false;
			//m_Spawned = new List<Mobile>();
		}

		#endregion

		#region Private Methods

		private void Spawn()
		{
			Despawn();

			MonsterTownSpawnEntry[] entries = null;

			switch ( m_TownMonsterType )
			{
				default:
				case TownMonsterType.Abyss: entries = MonsterTownSpawnEntry.Abyss; break;
				case TownMonsterType.Arachnid: entries = MonsterTownSpawnEntry.Arachnid; break;
				case TownMonsterType.DragonKind: entries = MonsterTownSpawnEntry.DragonKind; break;
				case TownMonsterType.Elementals: entries = MonsterTownSpawnEntry.Elementals; break;
				case TownMonsterType.Humanoid: entries = MonsterTownSpawnEntry.Humanoid; break;
				case TownMonsterType.Ophidian: entries = MonsterTownSpawnEntry.Ophidian; break;
				case TownMonsterType.OrcsandRatmen: entries = MonsterTownSpawnEntry.OrcsandRatmen; break;
				case TownMonsterType.OreElementals: entries = MonsterTownSpawnEntry.OreElementals; break;
				case TownMonsterType.Snakes: entries = MonsterTownSpawnEntry.Snakes; break;
				case TownMonsterType.Undead: entries = MonsterTownSpawnEntry.Undead; break;
			}

			for ( int i = 0; i < entries.Length; ++i )
				for ( int count = 0; count < entries[i].Amount; ++count )
					AddMonster( entries[i].Monster );
		}

		private void Despawn()
		{
			foreach ( Mobile m in m_Spawned )
				if ( m != null && !m.Deleted )
					m.Delete();

			m_Spawned.Clear();

			m_FinalStage = false;
		}

		private Point3D FindSpawnLocation()
		{
			int x, y, z;

			do
			{
				x = Utility.Random( m_Top.X, ( m_Bottom.X - m_Top.X ) );
				y = Utility.Random( m_Top.Y, ( m_Bottom.Y - m_Top.Y ) );
				z = SpawnMap.GetAverageZ( x, y );
			}
			while ( !SpawnMap.CanSpawnMobile( x, y, z ) );

			return new Point3D( x, y, z );
		}

		private void AddMonster( Type type )
		{
			object monster = Activator.CreateInstance( type );

			if ( monster != null && monster is Mobile )
			{
				Point3D location = FindSpawnLocation();

				Mobile from = (Mobile)monster;

				if ( m_FinalStage )
				{
					Announce( 1161, "{0} has come to take over {1}!  Please save it's citizens!", from.Name, m_TownInvaded );

					//TODO: Pack Goodies
				}

				from.MoveToWorld( location, SpawnMap );

				if ( from is BaseCreature )
				{
					( (BaseCreature)from ).Tamable = false;
					
					if ( !(from is BaseChampion) && !(from is Harrower) && !(from is BaseVendor) && !(from is BaseEscortable) && !(from is Clone) )
					{
						//if( Utility.RandomDouble() < m_ParagonChance )
							//( (BaseCreature)from ).IsParagon = true;
							
						if( m_AlwaysMurderer )
							( (BaseCreature)from ).Kills = 6;
					}
				}

				m_Spawned.Add( from );
			}
		}

		public void SpawnChamp()
		{
			Despawn();

			m_FinalStage = true;

			switch ( m_TownChampionType )
			{
				default:
				case TownChampionType.Barracoon: AddMonster( typeof( Barracoon ) ); break;
				case TownChampionType.Harrower: AddMonster( typeof( Harrower ) ); break;
				case TownChampionType.LordOaks: AddMonster( typeof( LordOaks ) ); break;
				case TownChampionType.Mephitis: AddMonster( typeof( Mephitis ) ); break;
				case TownChampionType.Neira: AddMonster( typeof( Neira ) ); break;
				case TownChampionType.Rikktor: AddMonster( typeof( Rikktor ) ); break;
				case TownChampionType.Semidar: AddMonster( typeof( Semidar ) ); break;
				case TownChampionType.Serado: AddMonster( typeof( Serado ) ); break;
			}
		}

		#endregion
	}
	
	public class TIWorldTimer : Timer
	{
		public static void Initialize()
		{
			new TIWorldTimer().Start();
		}
		
		public TIWorldTimer() : base( TownInvasion.m_TIEventRate, TownInvasion.m_TIEventRate )
		{
			Priority = TimerPriority.OneMinute;
		}
		
		protected override void OnTick()
		{
			BaseEvent evnt = Server.EventSystem.EventCore.FindEvent( EventType.TownInvasion );
			
			if( evnt != null )  //don't run a new one if one is running already.
			{
				evnt.Start();
			}
		}
	}
}