context: using System;
  using System.Collections.Generic;
  using WCell.Constants.NPCs;
  using WCell.Constants.World;
  using WCell.RealmServer.Content;
  using WCell.RealmServer.Entities;
  using WCell.RealmServer.Global;
  using WCell.RealmServer.Gossips;
  using WCell.RealmServer.Spawns;
  using WCell.RealmServer.Waypoints;
  using WCell.Util;
  using WCell.Util.Data;
  using WCell.Util.Graphics;
  
  namespace WCell.RealmServer.NPCs.Spawns
  {
  	/// <summary>
  	/// Spawn-information for NPCs
  	/// </summary>
  	[DataHolder]
  	public partial class NPCSpawnEntry : SpawnEntry<NPCSpawnPoolTemplate, NPCSpawnEntry, NPC, NPCSpawnPoint, NPCSpawnPool>, INPCDataHolder
  	{
  		public NPCSpawnEntry()
  		{
  
  		}
  
  		public NPCSpawnEntry(NPCId entryId, MapId map, Vector3 pos)
  		{
  			EntryId = entryId;
  			MapId = map;
  			Position = pos;
  		}
  
  		public NPCId EntryId;
  
  		[NotPersistent]
  		public NPCEntry Entry
  		{
  			get;
  			set;
  		}
  
  		#region Spawn data for NPCs
  		//TODO: Rename to something meaningful!
  		public AIMotionGenerationType MoveType;
  
  		public float RespawnRadius;
  
  		public bool IsDead;
  
  		public uint DisplayIdOverride;
  
  		public uint EquipmentId;
  
  		public NPCAddonData AddonData
  		{
  			get;
  			set;
  		}
  
  		[NotPersistent]
  		public NPCEquipmentEntry Equipment;
  		#endregion
  
  		public NPC SpawnObject(Map map)
  		{
  			var spawnling = Entry.Create(map.DifficultyIndex);
  			map.AddObjectNow(spawnling, Position);
  			return spawnling;
  		}
  
  		public override NPC SpawnObject(NPCSpawnPoint point)
  		{
  			var spawnling = Entry.Create(point);
  			point.Map.AddObjectNow(spawnling, Position);
  			return spawnling;
  		}
  
  		#region FinalizeDataHolder
  		/// <summary>
  		/// Finalize this NPCSpawnEntry
  		/// </summary>
  		/// <param name="addToPool">If set to false, will not try to add it to any pool (recommended for custom NPCSpawnEntry that share a pool)</param>
  		public void FinalizeDataHolder()
  		{
  			FinalizeDataHolder(true);
  		}
  
  		private void AddToPoolTemplate()
  		{
  			m_PoolTemplate = NPCMgr.GetOrCreateSpawnPoolTemplate(PoolId);
  			m_PoolTemplate.AddEntry(this);
  		}
  
  		/// <summary>
  		/// Finalize this NPCSpawnEntry
  		/// </summary>
  		/// <param name="addToPool">If set to false, will not try to add it to any pool (recommended for custom NPCSpawnEntry that share a pool)</param>
  		public override void FinalizeDataHolder(bool addToPool)
  		{	
  			// set Entry
  			if (Entry == null)
  			{
  				Entry = NPCMgr.GetEntry(EntryId);
  				if (Entry == null)
  				{
  					ContentMgr.OnInvalidDBData("{0} had an invalid EntryId.", this);
  					return;
  				}
  			}
  
  			// fix data inconsistencies & load addon data
  			if (EquipmentId != 0)
  			{
  				Equipment = NPCMgr.GetEquipment(EquipmentId);
  			}
  
  			// seems to be a DB issue where the id was inserted as a float
  			if (DisplayIdOverride == 0xFFFFFF)
  			{
  				DisplayIdOverride = 0;
  			}
  
  			if (AddonData != null)
  			{
  				AddonData.InitAddonData(this);
  			}
  
  			// do the default thing
  			base.FinalizeDataHolder(addToPool);
  
  			if (MapId != MapId.End)
  			{
  				// valid map
  				// add to NPCEntry
  				Entry.SpawnEntries.Add(this);
  
  				// add to list of NPCSpawnEntries
  				ArrayUtil.Set(ref NPCMgr.SpawnEntries, SpawnId, this);
  
  				if (addToPool)
  				{
  					// add to pool
  					AddToPoolTemplate();
  				}
  			}
  
              // Is this NPC associated with an event
              if (_eventId != 0)
              {
                  // The event id loaded can be negative if this
                  // entry is expected to despawn during an event
                  var eventId = (uint)Math.Abs(_eventId);
  
                  //Check if the event is valid
                  var worldEvent = WorldEventMgr.GetEvent(eventId);
                  if (worldEvent != null)
                  {
                      // Add this NPC to the list of related spawns
                      // for the given world event
                      var eventNPC = new WorldEventNPC() { _eventId = _eventId, EventId = eventId, Guid = SpawnId, Spawn = _eventId > 0 };
  
                      worldEvent.NPCSpawns.Add(eventNPC);
                  }
                  EventId = eventId;
  
              }
  
  			//if(MoveType == AIMotionGenerationType.RandomMotion)
  				//CreateRandomWaypoints();
  
  			// finished initializing, now call the hooks
  			foreach (var handler in Entry.SpawnTypeHandlers)
  			{
  				if (handler != null)
  				{
  					handler(this);
  				}
  			}
  		}
  		#endregion
  
  		#region Events
  		internal void NotifySpawned(NPC npc)
  		{
  			var evt = Spawned;
  			if (evt != null)
  			{
  				evt(npc);
  			}
  		}
  		#endregion
  
  		[NotPersistent]
  		public GossipMenu DefaultGossip;
  
  		#region Waypoints
  		[NotPersistent]
  		public readonly LinkedList<WaypointEntry> Waypoints = new LinkedList<WaypointEntry>();
  
  		/// <summary>
  		/// Whether this SpawnEntry has fixed Waypoints from DB
  		/// </summary>
  		[NotPersistent]
  		public bool HasDefaultWaypoints { get; set; }
  
  		public void RecreateRandomWaypoints()
  		{
  			Waypoints.Clear();
  			CreateRandomWaypoints();
  		}
  
  		public void CreateRandomWaypoints()
  		{
  			var terrain = TerrainMgr.GetTerrain(MapId);
  			if (terrain != null)
  			{
  				var gen = new RandomWaypointGenerator();
  				var wps = gen.GenerateWaypoints(terrain, Position, RespawnRadius);
  				AddWaypoints(wps);
  				Waypoints.Last.Value.WaitTime = (uint)Utility.Random(2000, 7000);
  			}
  		}
  		public int WaypointCount
  		{
  			get { return Waypoints.Count; }
  		}
  
  		/// <summary>
  		/// Creates a Waypoint but does not add it
  		/// </summary>
  		public WaypointEntry CreateWaypoint(Vector3 pos, float orientation)
  		{
  			var last = Waypoints.Last;
  			WaypointEntry entry;
  			if (last != null)
  			{
  				entry = new WaypointEntry
  				{
  					Id = last.Value.Id + 1,
  					SpawnEntry = this,
  				};
  			}
  			else
  			{
  				entry = new WaypointEntry
  				{
  					Id = 1,
  					SpawnEntry = this,
  				};
  			}
  
  			entry.Position = pos;
  			entry.Orientation = orientation;
  			return entry;
  		}
  
  		/// <summary>
  		/// Adds the given positions as WPs
  		/// </summary>
  		/// <param name="wps"></param>
  		public void AddWaypoints(Vector3[] wps)
  		{
  			if (wps.Length < 1)
  			{
  				throw new ArgumentException("wps are empty.");
  			}
  			var len = wps.Length;
  			Vector3 first;
  			if (Waypoints.Count > 0)
  			{
  				// adding to already existing WPs: Make sure that the orientation is correct
  				first = Waypoints.First.Value.Position;
  				var last = Waypoints.Last.Value;
  				last.Orientation = last.Position.GetAngleTowards(wps[0]);
  			}
  			else
  			{
  
gt: 				first = wps[0];