context: using System;
  using System.Collections.Generic;
  using WowPacketParser.Enums;
  using WowPacketParser.Misc;
  using WowPacketParser.Parsing;
  using WowPacketParser.Store;
  using WowPacketParser.Store.Objects;
  using CoreParsers = WowPacketParser.Parsing.Parsers;
  
  namespace WowPacketParserModule.V5_4_7_17898.Parsers
  {
      public static class UpdateHandler
      {
          [Parser(Opcode.SMSG_DESTROY_OBJECT)]
          public static void HandleDestroyObject(Packet packet)
          {
              var guid = new byte[8];
  
              guid[0] = packet.ReadBit();
              guid[2] = packet.ReadBit();
              guid[3] = packet.ReadBit();
              guid[1] = packet.ReadBit();
              guid[5] = packet.ReadBit();
              packet.ReadBit("Despawn Animation");
              guid[6] = packet.ReadBit();
              guid[7] = packet.ReadBit();
              guid[4] = packet.ReadBit();
  
              packet.ParseBitStream(guid, 2, 1, 5, 4, 3, 6, 7, 0);
  
              packet.WriteGuid("GUID", guid);
          }
  
          [Parser(Opcode.CMSG_OBJECT_UPDATE_FAILED)]
          public static void HandleObjectUpdateFailed(Packet packet)
          {
              var guid = packet.StartBitStream(4, 6, 3, 0, 7, 5, 1, 2);
              packet.ParseBitStream(guid, 4, 7, 0, 6, 5, 2, 1, 3);
  
              packet.WriteGuid("Guid", guid);
          }
  
          [HasSniffData] // in ReadCreateObjectBlock
          [Parser(Opcode.SMSG_UPDATE_OBJECT)]
          public static void HandleUpdateObject(Packet packet)
          {
              uint map = packet.ReadUInt16("Map");
              var count = packet.ReadUInt32("Count");
  
              for (var i = 0; i < count; i++)
              {
                  var type = packet.ReadByte();
                  var typeString = ((UpdateTypeCataclysm)type).ToString();
  
                  packet.AddValue("UpdateType", typeString, i);
                  switch (typeString)
                  {
                      case "Values":
                          {
                              var guid = packet.ReadPackedGuid("GUID", i);
  
                              WoWObject obj;
                              var updates = CoreParsers.UpdateHandler.ReadValuesUpdateBlock(packet, guid.GetObjectType(), i, false);
  
                              if (Storage.Objects.TryGetValue(guid, out obj))
                              {
                                  if (obj.ChangedUpdateFieldsList == null)
                                      obj.ChangedUpdateFieldsList = new List<Dictionary<int, UpdateField>>();
                                  obj.ChangedUpdateFieldsList.Add(updates);
                              }
  
                              break;
                          }
                      case "CreateObject1":
                      case "CreateObject2": // Might != CreateObject1 on Cata
                          {
                              var guid = packet.ReadPackedGuid("GUID", i);
                              ReadCreateObjectBlock(packet, guid, map, i);
                              break;
                          }
                      case "DestroyObjects":
                          {
                              CoreParsers.UpdateHandler.ReadObjectsBlock(packet, i);
                              break;
                          }
                  }
              }
          }
  
          private static void ReadCreateObjectBlock(Packet packet, WowGuid guid, uint map, object index)
          {
              var objType = packet.ReadByteE<ObjectType>("Object Type", index);
              var moves = ReadMovementUpdateBlock(packet, guid, index);
              var updates = CoreParsers.UpdateHandler.ReadValuesUpdateBlock(packet, objType, index, true);
  
              WoWObject obj;
              switch (objType)
              {
                  case ObjectType.Unit:
                      obj = new Unit();
                      break;
                  case ObjectType.GameObject:
                      obj = new GameObject();
                      break;
                  case ObjectType.Item:
                      obj = new Item();
                      break;
                  case ObjectType.Player:
                      obj = new Player();
                      break;
                  default:
                      obj = new WoWObject();
                      break;
              }
  
              obj.Type = objType;
              obj.Movement = moves;
              obj.UpdateFields = updates;
              obj.Map = map;
              obj.Area = CoreParsers.WorldStateHandler.CurrentAreaId;
              obj.PhaseMask = (uint)CoreParsers.MovementHandler.CurrentPhaseMask;
              obj.Phases = new HashSet<ushort>(CoreParsers.MovementHandler.ActivePhases);
  
              // If this is the second time we see the same object (same guid,
              // same position) update its phasemask
              if (Storage.Objects.ContainsKey(guid))
              {
                  var existObj = Storage.Objects[guid].Item1;
                  CoreParsers.UpdateHandler.ProcessExistingObject(ref existObj, obj, guid); // can't do "ref Storage.Objects[guid].Item1 directly
              }
              else
                  Storage.Objects.Add(guid, obj, packet.TimeSpan);
  
              if (guid.HasEntry() && (objType == ObjectType.Unit || objType == ObjectType.GameObject))
                  packet.AddSniffData(Utilities.ObjectTypeToStore(objType), (int)guid.GetEntry(), "SPAWN");
          }
  
          private static MovementInfo ReadMovementUpdateBlock(Packet packet, WowGuid guid, object index)
          {
              var moveInfo = new MovementInfo();
  
              var guid1 = new byte[8];
              var transportGuid = new byte[8];
              var goTransportGuid = new byte[8];
              var attackingTargetGuid = new byte[8];
              var guid5 = new byte[8];
              var guid6 = new byte[8];
              var guid7 = new byte[8];
  
              var hasAnimKit1 = false;
              var hasAnimKit2 = false;
              var hasAnimKit3 = false;
              var hasFullSpline = false;
              var hasSplineStartTime = false;
              var hasSplineVerticalAcceleration = false;
              var hasMovementFlags = false;
              var hasMoveFlagsExtra = false;
              var hasTimestamp = false;
              var hasOrientation = false;
              var hasTransportData = false;
              var hasTransportTime2 = false;
              var hasTransportTime3 = false;
              var hasGOTransportTime2 = false;
              var hasGOTransportTime3 = false;
              var hasPitch = false;
              var hasFallData = false;
              var hasFallDirection = false;
              var hasSplineElevation = false;
  
              var bit134 = false;
              var bitA8 = false;
              var bit23C = false;
              var bit298 = false;
              var bit230 = false;
              var bit284 = false;
              var bit228 = false;
              var bit258 = false;
              var bit218 = false;
              var bit220 = false;
  
              var bits168 = 0u;
              var splineCount = 0u;
              var bits138 = 0u;
              var bits98 = 0u;
              var bits25C = 0u;
              var bits26C = 0u;
              var bits288 = 0u;
              var bits418 = 0u;
              var bits2AA = 0u;
              var splineType = SplineType.Stop;
  
              var bit2A4 = packet.ReadBit(); // 676
              var hasVehicleData = packet.ReadBit("Has Vehicle Data", index); // 488
              var bit414 = packet.ReadBit(); // 1044
              var hasGameObjectRotation = packet.ReadBit("Has GameObject Rotation", index); // 512
              packet.ReadBit(); // fake 0
              var isLiving = packet.ReadBit("Is Living", index); // 368
              var hasSceneObjectData = packet.ReadBit("Has Scene Object Data", index); // 1032
              packet.ReadBit(); // fake 2
              var bit29C = packet.ReadBit(); // 668
              var bit2A8 = packet.ReadBit(); // 680
              var bit2A9 = packet.ReadBit(); // 681
              packet.ReadBit(); // fake 1
              var hasTransportPosition = packet.ReadBit("Has Transport Position", index); // 424
              var bit1DC = packet.ReadBit(); // 476
              var hasAnimKits = packet.ReadBit("Has Anim Kits", index); // 498
              var hasStationaryPosition = packet.ReadBit("Has Stationary Position", index); // 448
              var hasAttackingTarget = packet.ReadBit("Has Attacking Target", index); // 464
              packet.ReadBit(); // fake 3
              var transportFrames = packet.ReadBits("Transport Frames Count", 22, index); // 1068
              var bit32A = packet.ReadBit(); // 810
              var bit428 = packet.ReadBit(); // 1064
  
              if (bit428)
                  bits418 = packet.ReadBits(22);
  
              if (hasTransportPosition)
              {
                  packet.StartBitStream(goTransportGuid, 3, 5, 2, 1, 4);
                  hasGOTransportTime3 = packet.ReadBit();
                  hasGOTransportTime2 = packet.ReadBit();
                  packet.StartBitStream(goTransportGuid, 0, 6, 7);
              }
  
              if (isLiving)
              {
                  hasTransportData = packet.ReadBit("Has Transport Data", index);
                  if (hasTransportData)
                  {
                      transportGuid[4] = packet.ReadBit();
                      transportGuid[0] = packet.ReadBit();
                      transportGuid[5] = packet.ReadBit();
                      transportGuid[2] = packet.ReadBit();
                      transportGuid[3] = packet.ReadBit();
                      hasTransportTime2 = packet.ReadBit();
                      transportGuid[7] = packet.ReadBit();
                      transportGuid[6] = packet.ReadBit();
                      transportGuid[1] = packet.ReadBit();
                      hasTransportTime3 = packet.ReadBit();
                  }
  
                  hasPitch = !packet.ReadBit("Has Pitch", index);
                  packet.ReadBit();
                  bits168 = packet.ReadBits(19);
                  guid1[1] = packet.ReadBit();
                  hasMoveFlagsExtra = !packet.ReadBit();
                  packet.ReadBit();
                  hasSplineElevation = !packet.ReadBit("Has SplineElevation", index);
  
                  if (hasMoveFlagsExtra)
                      moveInfo.FlagsExtra = packet.ReadBitsE<MovementFlagExtra>("Extra Movement Flags", 13, index);
  
                  hasOrientation = !packet.ReadBit();
                  hasTimestamp = !packet.ReadBit();
                  hasMovementFlags = !packet.ReadBit();
                  bitA8 = !packet.ReadBit("bitA8", index);
                  guid1[2] = packet.ReadBit();
                  guid1[6] = packet.ReadBit();
                  hasFallData = packet.ReadBit();
                  guid1[5] = packet.ReadBit();
                  guid1[4] = packet.ReadBit();
                  guid1[0] = packet.ReadBit();
  
                  if (hasMovementFlags)
                      moveInfo.Flags = packet.ReadBitsE<MovementFlag>("Movement Flags", 30, index);
                  packet.ReadBit();
                  if (hasFallData)
                      hasFallDirection = packet.ReadBit();
                  bits98 = packet.ReadBits("bits98", 22, index);
  
                  guid1[7] = packet.ReadBit();
                  moveInfo.HasSplineData = packet.ReadBit();
                  guid1[3] = packet.ReadBit();
  
  
                  if (moveInfo.HasSplineData)
                  {
                      hasFullSpline = packet.ReadBit();
                      if (hasFullSpline)
                      {
                          hasSplineVerticalAcceleration = packet.ReadBit();
                          packet.ReadBitsE<SplineMode>("Spline Mode", 2, index);
                          splineCount = packet.ReadBits(20);
                          packet.ReadBitsE<SplineFlag434>("Spline flags", 25, index);
  
                          hasSplineStartTime = packet.ReadBit();
                          bit134 = packet.ReadBit();
                          if (bit134)
                          {
                              bits138 = packet.ReadBits(21);
                              packet.ReadBits(2);
                          }
                      }
                  }
              }
  
              if (bit29C)
              {
                  bit230 = packet.ReadBit();
                  bit258 = packet.ReadBit();
                  packet.ReadBit("bit20E", index);
                  packet.ReadBit("bit20F", index);
                  bit228 = packet.ReadBit();
                  packet.ReadBit("bit20C", index);
  
gt:                 bit218 = packet.ReadBit();