context: using Server;
  using System;
  using System.Collections.Generic;
  using Server.Mobiles;
  using Server.Items;
  using System.Linq;
  using Server.Engines.Points;
  using Server.Regions;
  using System.Text.RegularExpressions;
  
  namespace Server.Engines.VoidPool
  {
  	public enum VoidType
  	{
  		Abyss,
  		Repond, 
  		Undead,
  		Reptile,
  		Elemental
  	}
  
  	public class VoidPoolController : Item
  	{
  		public static VoidPoolController InstanceTram { get; set; }
  		public static VoidPoolController InstanceFel { get; set; }
  
          private readonly int RestartSpan = 15;
  		private readonly int StartMessage = 5;
  		private readonly int PoolStartHits = 15;
  		private readonly int StartPointVariance = 8;
  		
  		private readonly Point3D StartPoint1 = new Point3D(5592, 2012, 0);
  		private readonly Point3D StartPoint2 = new Point3D(5466, 2007, 0);
  		
  		private readonly Point3D EndPoint = new Point3D(5500, 1998, 5);
  		private readonly Rectangle2D PoolWalls = new Rectangle2D(5495, 1993, 10, 10);
  		
  		private bool _Active;
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public bool Active 
  		{ 
  			get { return _Active; } 
  			set 
  			{
  				if(!value)
  				{
  					if(Timer != null)
  					{
  						Timer.Stop();
  						Timer = null;
  					}
  
                      if (Region != null)
                      {
                          Region.Unregister();
                          Region = null;
                      }
  				}
  				else
  				{
                      if (Region == null)
                      {
                          Region = new VoidPoolRegion(this, this.Map);
                          Region.Register();
                      }
  
  					if(Timer == null)
  					{
  						Timer = Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnTick);
  						Timer.Start();
  						
  						NextStart = DateTime.UtcNow + TimeSpan.FromMinutes(RestartSpan);
  
                          if(Region != null)
  						    Region.SendRegionMessage(1152526, RestartSpan.ToString()); // The battle for the Void Pool will begin in ~1_VALUE~ minutes.
  					}
  				}
  				
  				_Active = value; 
  			} 
  		}
  		
          [CommandProperty(AccessLevel.GameMaster)]
  		public int Wave { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public int Stage 
          { 
              get 
              {
                  if (Wave < 2)
                      return 0;
                  //TODO: Make this like EA?
                  return Math.Max(1, Wave / 5); 
              } 
          }
  		
  		public List<WaveInfo> Waves { get; set; }
          public List<WayPoint> WaypointsA { get; set; }
          public List<WayPoint> WaypointsB { get; set; }
          public int WaypointACount { get; set; }
          public int WaypointBCount { get; set; }
  
  		public VoidPoolRegion Region { get; set; }
  		public Timer Timer { get; set; }
  		public Dictionary<Mobile, long> CurrentScore { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public bool OnGoing { get; private set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public VoidType VoidType { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public DateTime NextStart { get; private set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public DateTime NextWave { get; private set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public int PoolHits { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int RespawnMin { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int RespawnMax { get; set; }
  
          public VoidPoolController(Map map)
              : base(3803)
  		{
              Name = "Void Pool Controller";
  			Visible = false;
  			Movable = false;
  
              PoolHits = PoolStartHits;
  			
  			if(map == Map.Trammel)
  			{
  				if(InstanceTram != null)
  					Delete();
  				else
  					InstanceTram = this;
  			}
  			else if(map == Map.Felucca)
  			{
  				if(InstanceFel != null)
  					Delete();
  				else
  					InstanceFel = this;
  			}
  			else
  				Delete();
  
              WaypointsA = new List<WayPoint>();
              WaypointsB = new List<WayPoint>();
  
              Region = new VoidPoolRegion(this, map);
              Region.Register();
  
              RespawnMin = 60;
              RespawnMax = 90;
  
              ClearSpawners();
  			Active = true;
  		}
  
          public override void OnDoubleClick(Mobile from)
          {
              if (from.AccessLevel >= AccessLevel.GameMaster)
                  from.SendGump(new Server.Gumps.PropertiesGump(from, this));
          }
  		
  		private void OnTick()
  		{
  			if(!OnGoing && DateTime.UtcNow > NextStart && Region != null && Region.GetPlayerCount() > 0)
  			{
  				NextStart = DateTime.MaxValue;
  				OnGoing = true;
  				VoidType = (VoidType)Utility.Random(5);
                  PoolHits = PoolStartHits;
  				Wave = 0;
  
                  if (CurrentScore != null)
                      CurrentScore.Clear();
  
                  if (Waves != null)
                  {
                      Waves.Clear();
                      Waves.TrimExcess();
                  }
  
  				CurrentScore = new Dictionary<Mobile, long>();
  				Waves = new List<WaveInfo>();
  				
  				Region.SendRegionMessage(1152527); // The battle for the Void Pool is beginning now!
  
                  if (WaypointACount != WaypointsA.Count || WaypointBCount != WaypointsB.Count)
                      Generate.AddWaypoints();
  
  				SpawnWave();
  			}
  			else if(OnGoing)
  			{
  				if(DateTime.UtcNow > NextWave)
  				{
  					SpawnWave();
  				}
  				
  				IPooledEnumerable eable = this.Map.GetMobilesInBounds(PoolWalls);
  				foreach(Mobile m in eable)
  				{
                      if (!OnGoing)
                          break;
  
  					if(m is BaseCreature && !((BaseCreature)m).Controlled && !((BaseCreature)m).Summoned && Utility.RandomDouble() > 0.25)
  						OnVoidWallDamaged(m);
  				}
  				eable.Free();
  			}
  		}
  		
  		public void SpawnWave()
  		{
  			Wave++;
  			
  			Region.SendRegionMessage(1152528, Wave.ToString());
  
              int toSpawn = (int)Math.Ceiling(Math.Max(5, Math.Sqrt(Wave) * 2) * 1.5);
  			List<BaseCreature> creatures = new List<BaseCreature>();
  
  			for(int i = 0; i < toSpawn; i++)
  			{
  				Point3D start = i % 2 == 0 ? StartPoint1 : StartPoint2;
  				
  				for(int j = 0; j < 25; j++)
  				{
  					int x = start.X + Utility.RandomMinMax(start.X - (StartPointVariance / 2), start.X + (StartPointVariance / 2));
  					int y = start.Y + Utility.RandomMinMax(start.Y - (StartPointVariance / 2), start.Y + (StartPointVariance / 2));
  					int z = this.Map.GetAverageZ(x, y);
  					
  					if(this.Map.CanSpawnMobile(x, y, z))
  					{
  						start = new Point3D(x, y, z);
  						break;
  					}
  				}
  			
  				int ran = Utility.RandomMinMax(0, Stage < 10 ? 12 : Stage < 15 ? 14 : 15);
  				Type t;
  				
  				switch(ran)
  				{
  					default:
  					case 0: case 1: case 3: case 4: t = SpawnTable[(int)VoidType][0]; break;
  					case 5: case 6: case 7: case 8: t = SpawnTable[(int)VoidType][1]; break;
  					case 9: case 10: case 11: t = SpawnTable[(int)VoidType][2]; break;
  					case 12: case 13: t = SpawnTable[(int)VoidType][3]; break;
  					case 14: case 15: t = SpawnTable[(int)VoidType][4]; break;
  				}
  				
  				BaseCreature bc = Activator.CreateInstance(t,  Wave, true) as BaseCreature;
  				
  				if(bc != null)
  				{
                      bc.NoLootOnDeath = true;
  					Timer.DelayCall(TimeSpan.FromSeconds((double)i * .75), () =>
  					{
                          if (OnGoing)
                          {
                              bc.MoveToWorld(start, this.Map);
                              bc.Home = EndPoint;
                              bc.RangeHome = 1;
  
                              creatures.Add(bc);
  
                              bc.CurrentWayPoint = GetNearestWaypoint(bc);
                          }
                          else
                              bc.Delete();
  					});
  				}
  			}
  			
  			Waves.Add(new WaveInfo(Wave, creatures));
  			NextWave = GetNextWaveTime();
  		}
  
          public WayPoint GetNearestWaypoint(Mobile m, int range = 15)
          {
              IPooledEnumerable eable = this.Map.GetItemsInRange(m.Location, range);
  
              int closestRange = 15;
              WayPoint closest = null;
  
              foreach (Item item in eable)
              {
                  int dist = 0;
                  if (item is WayPoint)
                  {
                      dist = (int)m.GetDistanceToSqrt(item);
  
                      if (dist < closestRange || closest == null)
                      {
                          closest = item as WayPoint;
                          closestRange = dist;
                      }
                  }
              }
  
              return closest;
          }
  
          public Item GetNearestVoidPoolWall(Mobile m)
          {
              IPooledEnumerable eable = this.Map.GetItemsInRange(m.Location, 5);
  
              int closestRange = 5;
              Item closest = null;
  
              foreach (Item item in eable)
              {
                  int dist = 0;
  
gt:                 if (item.Name == "Void Pool")