context: #nullable enable
  using System;
  using System.Collections.Generic;
  using WowPacketParser.DBC;
  using WowPacketParser.Enums;
  using WowPacketParser.Misc;
  using WowPacketParser.PacketStructures;
  using WowPacketParser.Parsing;
  using WowPacketParser.Proto;
  using WowPacketParser.Store;
  using WowPacketParser.Store.Objects;
  
  namespace WowPacketParserModule.V1_13_2_31446.Parsers
  {
      public static class SpellHandler
      {
          public static void ReadSpellCastLogData(Packet packet, params object[] idx)
          {
              packet.ReadSByte("Unk1_13_2", idx);
          }
  
          public static void ReadSpellTargetData(Packet packet, PacketSpellData? packetSpellData, uint spellID, params object[] idx)
          {
              packet.ResetBitReader();
  
              packet.ReadBitsE<TargetFlag>("Flags", 25, idx);
  
              var hasSrcLoc = packet.ReadBit("HasSrcLocation", idx);
              var hasDstLoc = packet.ReadBit("HasDstLocation", idx);
              var hasOrient = packet.ReadBit("HasOrientation", idx);
              var hasMapID = packet.ReadBit("hasMapID ", idx);
              var nameLength = packet.ReadBits(7);
  
              var targetUnit = packet.ReadPackedGuid128("Unit", idx);
              if (packetSpellData != null)
                  packetSpellData.TargetUnit = targetUnit;
              packet.ReadPackedGuid128("Item", idx);
  
              if (hasSrcLoc)
                  V6_0_2_19033.Parsers.SpellHandler.ReadLocation(packet, "SrcLocation");
  
              Vector3? dstLocation = null;
              if (hasDstLoc)
              {
                  dstLocation = V6_0_2_19033.Parsers.SpellHandler.ReadLocation(packet, "DstLocation");
                  if (packetSpellData != null)
                      packetSpellData.DstLocation = dstLocation;
              }
  
              if (hasOrient)
                  packet.ReadSingle("Orientation", idx);
  
              int mapID = -1;
              if (hasMapID)
                  mapID = (ushort)packet.ReadInt32("MapID", idx);
  
              if (Settings.UseDBC && dstLocation != null && mapID != -1)
              {
                  for (uint i = 0; i < 32; i++)
                  {
                      var tuple = Tuple.Create(spellID, i);
                      if (DBC.SpellEffectStores.ContainsKey(tuple))
                      {
                          var effect = DBC.SpellEffectStores[tuple];
                          if ((Targets)effect.ImplicitTarget[0] == Targets.TARGET_DEST_DB || (Targets)effect.ImplicitTarget[1] == Targets.TARGET_DEST_DB)
                          {
                              string effectHelper = $"Spell: { StoreGetters.GetName(StoreNameType.Spell, (int)spellID) } Efffect: { effect.Effect } ({ (SpellEffects)effect.Effect })";
  
                              var spellTargetPosition = new SpellTargetPosition
                              {
                                  ID = spellID,
                                  EffectIndex = (byte)i,
                                  PositionX = dstLocation.Value.X,
                                  PositionY = dstLocation.Value.Y,
                                  PositionZ = dstLocation.Value.Z,
                                  MapID = (ushort)mapID,
                                  EffectHelper = effectHelper
                              };
  
                              if (!Storage.SpellTargetPositions.ContainsKey(spellTargetPosition))
                                  Storage.SpellTargetPositions.Add(spellTargetPosition);
                          }
                      }
                  }
              }
  
              packet.ReadWoWString("Name", nameLength, idx);
          }
  
          public static PacketSpellData ReadSpellCastData(Packet packet, params object[] idx)
          {
              var packetSpellData = new PacketSpellData();
              packet.ReadPackedGuid128("CasterGUID", idx);
              packetSpellData.Caster = packet.ReadPackedGuid128("CasterUnit", idx);
  
              packetSpellData.CastGuid = packet.ReadPackedGuid128("CastID", idx);
              packet.ReadPackedGuid128("OriginalCastID", idx);
  
              var spellID = packetSpellData.Spell = packet.ReadUInt32<SpellId>("SpellID", idx);
              packet.ReadUInt32("SpellXSpellVisualID", idx);
  
              packetSpellData.Flags = packet.ReadUInt32("CastFlags", idx);
              packetSpellData.Flags2 = packet.ReadUInt32("CastFlagsEx", idx);
              packetSpellData.CastTime = packet.ReadUInt32("CastTime", idx);
  
              V6_0_2_19033.Parsers.SpellHandler.ReadMissileTrajectoryResult(packet, idx, "MissileTrajectory");
  
              packet.ReadByte("DestLocSpellCastIndex", idx);
  
              V6_0_2_19033.Parsers.SpellHandler.ReadCreatureImmunities(packet, idx, "Immunities");
  
              V6_0_2_19033.Parsers.SpellHandler.ReadSpellHealPrediction(packet, idx, "Predict");
  
              packet.ResetBitReader();
  
              var hitTargetsCount = packet.ReadBits("HitTargetsCount", 16, idx);
              var missTargetsCount = packet.ReadBits("MissTargetsCount", 16, idx);
              var missStatusCount = packet.ReadBits("MissStatusCount", 16, idx);
              var remainingPowerCount = packet.ReadBits("RemainingPowerCount", 9, idx);
  
              var hasRuneData = packet.ReadBit("HasRuneData", idx);
              var targetPointsCount = packet.ReadBits("TargetPointsCount", 16, idx);
              var hasAmmoDisplayId = packet.ReadBit("HasAmmoDisplayId", idx);
              var hasAmmoInventoryType = packet.ReadBit("HasAmmoInventoryType", idx);
  
              for (var i = 0; i < missStatusCount; ++i)
                  V6_0_2_19033.Parsers.SpellHandler.ReadSpellMissStatus(packet, idx, "MissStatus", i);
  
              ReadSpellTargetData(packet, packetSpellData, spellID, idx, "Target");
  
              for (var i = 0; i < hitTargetsCount; ++i)
                  packetSpellData.HitTargets.Add(packet.ReadPackedGuid128("HitTarget", idx, i));
  
              for (var i = 0; i < missTargetsCount; ++i)
                  packetSpellData.MissedTargets.Add(packet.ReadPackedGuid128("MissTarget", idx, i));
  
              for (var i = 0; i < remainingPowerCount; ++i)
                  V6_0_2_19033.Parsers.SpellHandler.ReadSpellPowerData(packet, idx, "RemainingPower", i);
  
              if (hasRuneData)
                  V7_0_3_22248.Parsers.SpellHandler.ReadRuneData(packet, idx, "RemainingRunes");
  
              for (var i = 0; i < targetPointsCount; ++i)
                  packetSpellData.TargetPoints.Add(V6_0_2_19033.Parsers.SpellHandler.ReadLocation(packet, idx, "TargetPoints", i));
  
              if (hasAmmoDisplayId)
                  packetSpellData.AmmoDisplayId = packet.ReadInt32("AmmoDisplayId", idx);
  
              if (hasAmmoInventoryType)
                  packetSpellData.AmmoInventoryType = (uint)packet.ReadInt32E<InventoryType>("AmmoInventoryType", idx);
  
              return packetSpellData;
          }
  
          [Parser(Opcode.SMSG_SPELL_START)]
          public static void HandleSpellStart(Packet packet)
          {
              PacketSpellStart packetSpellStart = packet.Holder.SpellStart = new();
              packetSpellStart.Data = ReadSpellCastData(packet, "Cast");
          }
  
          [Parser(Opcode.SMSG_SPELL_GO)]
          public static void HandleSpellGo(Packet packet)
          {
              PacketSpellGo packetSpellGo = packet.Holder.SpellGo = new();
              packetSpellGo.Data = ReadSpellCastData(packet, "Cast");
  
              packet.ResetBitReader();
  
              var unkBit = packet.ReadBit();
              if (unkBit)
                  packet.ReadSByte("UnkSByte");
          }
  
          [HasSniffData]
          [Parser(Opcode.SMSG_AURA_UPDATE)]
          public static void HandleAuraUpdate(Packet packet)
          {
              PacketAuraUpdate packetAuraUpdate = packet.Holder.AuraUpdate = new();
              packet.ReadBit("UpdateAll");
              int countBits = ClientVersion.AddedInVersion(ClientVersionBuild.V1_13_3_32790) ? 8 : 7;
              var count = packet.ReadBits("AurasCount", countBits);
  
              var auras = new List<Aura>();
              for (var i = 0; i < count; ++i)
              {
                  var auraEntry = new PacketAuraUpdateEntry();
                  packetAuraUpdate.Updates.Add(auraEntry);
                  var aura = new Aura();
  
                  auraEntry.Slot = packet.ReadByte("Slot", i);
  
                  packet.ResetBitReader();
                  var hasAura = packet.ReadBit("HasAura", i);
                  auraEntry.Remove = !hasAura;
                  if (hasAura)
                  {
                      packet.ReadPackedGuid128("CastID", i);
                      aura.SpellId = auraEntry.Spell = (uint)packet.ReadInt32<SpellId>("SpellID", i);
                      packet.ReadInt32("SpellXSpellVisualID", i);
                      var flags = packet.ReadByteE<AuraFlagMoP>("Flags", i);
                      aura.AuraFlags = flags;
                      auraEntry.Flags = flags.ToUniversal();
                      packet.ReadUInt32("ActiveFlags", i);
                      aura.Level = packet.ReadUInt16("CastLevel", i);
                      aura.Charges = packet.ReadByte("Applications", i);
                      packet.ReadInt32("ContentTuningID", i);
  
                      packet.ResetBitReader();
  
                      var hasCastUnit = packet.ReadBit("HasCastUnit", i);
  
gt:                     var hasDuration = packet.ReadBit("HasDuration", i);