context: using System.Collections.Generic;
  using NLog;
  using WCell.Constants;
  using WCell.Constants.NPCs;
  using WCell.Constants.Pets;
  using WCell.Constants.Spells;
  using WCell.Constants.Talents;
  using WCell.Core.Network;
  using WCell.RealmServer.Entities;
  using WCell.RealmServer.Network;
  using WCell.RealmServer.NPCs.Pets;
  using WCell.RealmServer.Spells;
  
  namespace WCell.RealmServer.Handlers
  {
      public static class PetHandler
      {
          private static readonly Logger log = LogManager.GetCurrentClassLogger();
  
          #region IN
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_NAME_QUERY)]
          public static void HandleNameQuery(IRealmClient client, RealmPacketIn packet)
          {
              var petNumber = packet.ReadUInt32();
              var petEntityId = packet.ReadEntityId();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(petEntityId) as NPC;
  
              if (pet != null)
              {
                  SendName(chr, pet.PetNumber, pet.Name, pet.PetNameTimestamp);
              }
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_REQUEST_PET_INFO)]
          public static void HandleInfoRequest(IRealmClient client, RealmPacketIn packet)
          {
              // seems useless?
              // log.Warn("Client {0} sent CMSG_REQUEST_PET_INFO", client);
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_ACTION)]
          public static void HandleAction(IRealmClient client, RealmPacketIn packet)
          {
              var entityId = packet.ReadEntityId();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(entityId) as NPC;
  
              if (pet != null && pet == chr.ActivePet && pet.IsAlive)
              {
                  var record = pet.PetRecord;
                  if (record != null)
                  {
                      var data = (PetActionEntry)packet.ReadUInt32();
  
                      switch (data.Type)
                      {
                          case PetActionType.SetMode:
                              // mode
                              pet.SetPetAttackMode(data.AttackMode);
                              break;
                          case PetActionType.SetAction:
                              // action
                              var targetAction = data.Action;
                              pet.SetPetAction(targetAction);
                              break;
                          default:
                              // spell cast
                              var target = chr.Map.GetObject(packet.ReadEntityId());
                              pet.CastPetSpell(data.SpellId, target);
                              break;
                      }
                  }
              }
          }
  
          /// <summary>
          ///
          /// </summary>
          /// <param name="client"></param>
          /// <param name="packet"></param>
          [PacketHandler(RealmServerOpCode.CMSG_PET_SET_ACTION)]
          public static void HandleSetAction(IRealmClient client, RealmPacketIn packet)
          {
              var entityId = packet.ReadEntityId();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(entityId) as NPC;
  
              if (pet == null || pet.PermanentPetRecord == null || (pet != chr.ActivePet && !chr.GodMode))
              {
                  return;
              }
  
              while (packet.Length - packet.Position >= 8)	// a button has 8 bytes in the packet
              {
                  ReadButton(pet, packet);
              }
          }
  
          private static void ReadButton(NPC pet, RealmPacketIn packet)
          {
              var index = packet.ReadUInt32();
              var newAction = (PetActionEntry)packet.ReadUInt32();
  
              var record = pet.PermanentPetRecord;
  
              if (index > record.ActionButtons.Length)
              {
                  return;
              }
  
              // TODO: Interpret spell settings (auto-cast etc)
              record.ActionButtons[index] = newAction;
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_RENAME)]
          public static void HandleRename(IRealmClient client, RealmPacketIn packet)
          {
              var entityId = packet.ReadEntityId();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(entityId) as NPC;
  
              if (pet != null)
              {
                  if (pet == chr.ActivePet || chr.GodMode)
                  {
                      var newName = packet.ReadCString();
                      var reason = pet.TrySetPetName(chr, newName);
                      if (reason != PetNameInvalidReason.Ok)
                      {
                          SendNameInvalid(chr, reason, newName);
                      }
                  }
              }
  #if DEBUG
  			else
  			{
  				chr.SendSystemMessage("You sent CMSG_PET_RENAME for a pet that does not exist in the same Map.");
  			}
  #endif
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_ABANDON)]
          public static void HandleAbandon(IRealmClient client, RealmPacketIn packet)
          {
              var petId = packet.ReadEntityId();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(petId) as NPC;
  
              if (pet != null && pet.IsAlive && pet.IsInContext)
              {
                  if (pet == chr.ActivePet || chr.GodMode)
                  {
                      chr.AbandonActivePet();
                  }
              }
  #if DEBUG
  			else
  			{
  				chr.SendSystemMessage("You sent CMSG_PET_ABANDON for a pet that does not exist in the same Map.");
  			}
  #endif
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_SPELL_AUTOCAST)]
          public static void HandleAutocast(IRealmClient client, RealmPacketIn packet)
          {
              var petId = packet.ReadEntityId();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(petId) as NPC;
  
              if (pet != null)
              {
                  if (pet == chr.ActivePet || chr.GodMode)
                  {
                      // TODO: Autocast
                  }
              }
  #if DEBUG
  			else
  			{
  				chr.SendSystemMessage("You sent CMSG_PET_SPELL_AUTOCAST for a pet that does not exist in the same Map.");
  			}
  #endif
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_CAST_SPELL)]
          public static void HandlePetCastSpell(IRealmClient client, RealmPacketIn packet)
          {
              var petId = packet.ReadEntityId();
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(petId) as NPC;
  
              if (pet != null)
              {
                  //TODO: Add (pet as Vehicle).GetDriver() == chr;
                  if (pet == chr.ActivePet || chr.Vehicle == pet || chr.Charm == pet || chr.GodMode)
                  {
                      var castCount = packet.ReadByte();
                      var spellId = packet.ReadUInt32();
                      var unkFlags = packet.ReadByte();
  
                      //TODO: Fix this
                      //var spell = pet.Spells[spellId];
                      var spell = SpellHandler.Get(spellId);
                      if (spell != null)
                      {
                          var cast = pet.SpellCast;
                          cast.Start(spell, packet, castCount, unkFlags);
                      }
                  }
              }
  #if DEBUG
  			else
  			{
  				chr.SendSystemMessage("You sent CMSG_PET_CAST_SPELL for a pet that does not exist in the same Map.");
  			}
  #endif
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_CANCEL_AURA)]
          public static void HandleCancelAura(IRealmClient client, RealmPacketIn packet)
          {
              var petId = packet.ReadEntityId();
              var spellId = (SpellId)packet.ReadUInt32();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(petId) as NPC;
  
              if (pet != null)
              {
                  if (pet.Master == chr)
                  {
                      var aura = pet.Auras[spellId, true];
                      if (aura != null && aura.CanBeRemoved)
                      {
                          aura.TryRemove(true);
                      }
                  }
              }
          }
  
          [PacketHandler(RealmServerOpCode.CMSG_PET_STOP_ATTACK)]
          public static void HandleStopAttack(IRealmClient client, RealmPacketIn packet)
          {
              var petId = packet.ReadEntityId();
  
              var chr = client.ActiveCharacter;
              var pet = chr.Map.GetObject(petId) as NPC;
  
              if (pet != null && pet.IsAlive)
              {
                  if (pet == chr.ActivePet)
                  {
                      pet.Brain.EnterDefaultState();
                  }
              }
  #if DEBUG
  			else
  			{
  				chr.SendSystemMessage("You sent CMSG_PET_STOP_ATTACK for a pet that does not exist in the same Map.");
  			}
  #endif
          }
  
          #endregion IN
  
          public static void SendTameFailure(IPacketReceiver receiver, TameFailReason reason)
          {
              using (var packet = new RealmPacketOut(RealmServerOpCode.SMSG_PET_TAME_FAILURE, 1))
              {
                  packet.Write((byte)reason);
                  receiver.Send(packet);
              }
          }
  
          #region Spells
  
          public static void SendPetGUIDs(Character chr)
          {
              if (chr.ActivePet == null)
              {
                  using (var packet = new RealmPacketOut(RealmServerOpCode.SMSG_PET_GUIDS, 12))
                  {
                      packet.Write(0); // list count
                      chr.Send(packet);
                  }
              }
              else
              {
                  using (var packet = new RealmPacketOut(RealmServerOpCode.SMSG_PET_GUIDS, 12))
                  {
                      packet.Write(1); // list count
                      packet.Write(chr.ActivePet.EntityId);
                      chr.Send(packet);
                  }
              }
          }
  
          /// <summary>
          /// Sends any kind of extra command-bar to control other entities, such as NPCs, vehicles etc
          /// </summary>
          /// <param name="owner"></param>
          //public static void SendSpells(Character owner, NPC npc, uint duration,
          //    PetAttackMode attackMode, PetAction action, PetFlags flags,
          //    PetActionEntry[] petActions,
          //    PetSpell[] spells)
  
          public static void SendSpells(Character owner, NPC pet, PetAction currentAction)
          {
              // TODO: Cooldowns
              var record = pet.PetRecord;
              var mode = pet.Entry.Type == CreatureType.NonCombatPet ? PetAttackMode.Passive : PetAttackMode.Defensive;
              var flags = PetFlags.None;
  
gt:             uint[] actions = null;