context: using System.Diagnostics.CodeAnalysis;
  using WowPacketParser.Enums;
  using WowPacketParser.Hotfix;
  using WowPacketParser.Loading;
  using WowPacketParser.Misc;
  using WowPacketParser.Parsing;
  using WowPacketParser.Store;
  using WowPacketParser.Store.Objects;
  
  namespace WowPacketParserModule.V5_4_0_17359.Parsers
  {
      [SuppressMessage("ReSharper", "UseObjectOrCollectionInitializer")]
      public static class QueryHandler
      {
          [Parser(Opcode.CMSG_QUERY_CREATURE)]
          public static void HandleCreatureQuery(Packet packet)
          {
              var entry = packet.ReadInt32("Entry");
          }
  
          [Parser(Opcode.CMSG_DB_QUERY_BULK)]
          public static void HandleDBQueryBulk(Packet packet)
          {
              packet.ReadInt32E<DB2Hash>("DB2 File");
              var count = packet.ReadBits(21);
  
              var guids = new byte[count][];
              for (var i = 0; i < count; ++i)
              {
                  guids[i] = new byte[8];
                  packet.StartBitStream(guids[i], 3, 4, 7, 2, 5, 1, 6, 0);
              }
  
              packet.ResetBitReader();
              for (var i = 0; i < count; ++i)
              {
                  packet.ReadXORBytes(guids[i], 6, 1, 2);
                  packet.ReadInt32("Entry", i);
                  packet.ReadXORBytes(guids[i], 4, 5, 7, 0, 3);
                  packet.WriteGuid("Guid", guids[i], i);
              }
          }
  
          [HasSniffData]
          [Parser(Opcode.SMSG_QUERY_CREATURE_RESPONSE)]
          public static void HandleCreatureQueryResponse(Packet packet)
          {
              var entry = packet.ReadEntry("Entry");
              Bit hasData = packet.ReadBit();
              if (!hasData)
                  return; // nothing to do
  
              CreatureTemplate creature = new CreatureTemplate
              {
                  Entry = (uint)entry.Key
              };
  
              var stringLens = new int[4][];
              for (int i = 0; i < 4; i++)
              {
                  stringLens[i] = new int[2];
                  stringLens[i][0] = (int)packet.ReadBits(11);
                  stringLens[i][1] = (int)packet.ReadBits(11);
              }
  
              uint qItemCount = packet.ReadBits(22);
              uint lenS4 = packet.ReadBits(6);
              uint lenS3 = packet.ReadBits(11);
              uint lenS5 = packet.ReadBits(11);
  
              creature.RacialLeader = packet.ReadBit("Racial Leader");
  
              packet.ResetBitReader();
  
              creature.Type = packet.ReadInt32E<CreatureType>("Type");
              creature.KillCredits = new uint?[2];
              creature.KillCredits[1] = packet.ReadUInt32("Kill Credit 1");
              creature.ModelIDs = new uint?[4];
              creature.ModelIDs[3] = packet.ReadUInt32("Display ID 3");
              creature.ModelIDs[2] = packet.ReadUInt32("Display ID 2");
  
              //TODO: move to creature_questitems
              //creature.QuestItems = new uint[qItemCount];
              for (int i = 0; i < qItemCount; ++i)
                  /*creature.QuestItems[i] = (uint)*/packet.ReadInt32<ItemId>("Quest Item", i);
  
              creature.RequiredExpansion = packet.ReadUInt32E<ClientType>("Expansion");
  
              var name = new string[8];
              var femaleName = new string[4];
              for (int i = 0; i < 4; ++i)
              {
                  if (stringLens[i][0] > 1)
                      name[i] = packet.ReadCString("Name", i);
                  if (stringLens[i][1] > 1)
                      femaleName[i] = packet.ReadCString("Female Name", i);
              }
              creature.Name = name[0];
              creature.FemaleName = femaleName[0];
  
              if (lenS5 > 1)
                  creature.TitleAlt = packet.ReadCString("TitleAlt");
  
              creature.ManaModifier = packet.ReadSingle("Modifier 2");
              creature.ModelIDs[0] = packet.ReadUInt32("Display ID 0");
  
              if (lenS4 > 1)
                  creature.IconName = packet.ReadCString("Icon Name");
  
              creature.KillCredits[0] = packet.ReadUInt32("Kill Credit 0");
              creature.ModelIDs[1] = packet.ReadUInt32("Display ID 1");
  
              if (lenS3 > 1)
                  creature.SubName = packet.ReadCString("Sub Name");
  
              creature.TypeFlags = packet.ReadUInt32E<CreatureTypeFlag>("Type Flags");
              creature.TypeFlags2 = packet.ReadUInt32("Creature Type Flags 2"); // Missing enum
  
              creature.HealthModifier = packet.ReadSingle("Modifier 1");
              creature.Family = packet.ReadInt32E<CreatureFamily>("Family");
              creature.Rank = packet.ReadInt32E<CreatureRank>("Rank");
              creature.MovementID = packet.ReadUInt32("Movement ID");
  
              packet.AddSniffData(StoreNameType.Unit, entry.Key, "QUERY_RESPONSE");
  
              Storage.CreatureTemplates.Add(creature, packet.TimeSpan);
  
              if (ClientLocale.PacketLocale != LocaleConstant.enUS)
              {
                  CreatureTemplateLocale localesCreature = new CreatureTemplateLocale
                  {
                      ID = (uint)entry.Key,
                      Name = creature.Name,
                      NameAlt = creature.FemaleName,
                      Title = creature.SubName,
                      TitleAlt = creature.TitleAlt
                  };
  
                  Storage.LocalesCreatures.Add(localesCreature, packet.TimeSpan);
              }
  
              ObjectName objectName = new ObjectName
              {
                  ObjectType = ObjectType.Unit,
                  ID = entry.Key,
                  Name = creature.Name
              };
              Storage.ObjectNames.Add(objectName, packet.TimeSpan);
          }
  
          [HasSniffData]
          [Parser(Opcode.SMSG_DB_REPLY)]
          public static void HandleDBReply(Packet packet)
          {
              var size = packet.ReadInt32("Size");
              var data = packet.ReadBytes(size);
              var db2File = new Packet(data, packet.Opcode, packet.Time, packet.Direction, packet.Number, packet.Writer, packet.FileName);
  
              var type = packet.ReadUInt32E<DB2Hash>("DB2 File");
              packet.ReadTime("Hotfix date");
              var entry = packet.ReadInt32("Entry");
              if (entry < 0)
              {
                  packet.WriteLine("Row {0} has been removed.", -entry);
                  HotfixStoreMgr.RemoveRecord(type, entry);
              }
              else
              {
                  packet.AddSniffData(StoreNameType.None, entry, type.ToString());
                  HotfixStoreMgr.AddRecord(type, entry, db2File);
                  db2File.ClosePacket(false);
              }
          }
  
          [Parser(Opcode.SMSG_REALM_QUERY_RESPONSE)]
          public static void HandleRealmQueryResponse(Packet packet)
          {
              packet.ReadByte("Unk byte");
              packet.ReadInt32("Realm Id");
  
              var bits278 = packet.ReadBits(8);
              packet.ReadBit();
              var bits22 = packet.ReadBits(8);
  
              packet.ReadWoWString("Realmname", bits22);
              packet.ReadWoWString("Realmname (without white char)", bits278);
          }
  
          [HasSniffData]
          [Parser(Opcode.SMSG_QUERY_PAGE_TEXT_RESPONSE)]
          public static void HandlePageTextResponse(Packet packet)
          {
              Bit hasData = packet.ReadBit();
              if (!hasData)
              {
                  packet.ReadUInt32("Entry");
                  return; // nothing to do
              }
  
              PageText pageText = new PageText();
  
              uint textLen = packet.ReadBits(12);
  
              packet.ResetBitReader();
              pageText.Text = packet.ReadWoWString("Page Text", textLen);
  
              uint entry = packet.ReadUInt32("Entry");
              pageText.ID = entry;
              pageText.NextPageID = packet.ReadUInt32("Next Page");
              packet.ReadUInt32("Entry");
  
              packet.AddSniffData(StoreNameType.PageText, (int)entry, "QUERY_RESPONSE");
              Storage.PageTexts.Add(pageText, packet.TimeSpan);
          }
  
          [HasSniffData]
          [Parser(Opcode.SMSG_QUERY_NPC_TEXT_RESPONSE)]
          public static void HandleNpcTextUpdate(Packet packet)
          {
              var entry = packet.ReadEntry("Entry");
              if (entry.Value) // Can be masked
                  return;
  
              int size = packet.ReadInt32("Size");
              var data = packet.ReadBytes(size);
              Bit hasData = packet.ReadBit();
              if (!hasData)
                  return; // nothing to do
  
              NpcTextMop npcText = new NpcTextMop
              {
                  ID = (uint)entry.Key
              };
  
              Packet pkt = new Packet(data, packet.Opcode, packet.Time, packet.Direction, packet.Number, packet.Writer, packet.FileName);
              npcText.Probabilities = new float[8];
              npcText.BroadcastTextId = new uint[8];
              for (int i = 0; i < 8; ++i)
                  npcText.Probabilities[i] = pkt.ReadSingle("Probability", i);
              for (int i = 0; i < 8; ++i)
                  npcText.BroadcastTextId[i] = pkt.ReadUInt32("Broadcast Text Id", i);
  
              pkt.ClosePacket(false);
  
              packet.AddSniffData(StoreNameType.NpcText, entry.Key, "QUERY_RESPONSE");
  
              Storage.NpcTextsMop.Add(npcText, packet.TimeSpan);
          }
  
          [Parser(Opcode.SMSG_QUERY_PLAYER_NAME_RESPONSE)]
          public static void HandleNameQueryResponse(Packet packet)
          {
              var guid = new byte[8];
              var guid1 = new byte[8];
              var guid2 = new byte[8];
  
              packet.StartBitStream(guid, 5, 7, 3, 0, 4, 1, 6, 2);
  
              packet.ReadXORByte(guid, 7);
              packet.ReadXORByte(guid, 4);
              packet.ReadXORByte(guid, 3);
  
              var hasData = packet.ReadByte("Byte18");
              if (hasData == 0)
              {
                  packet.ReadInt32("Int24");
                  packet.ReadByte("Race");
                  packet.ReadByte("Gender");
                  packet.ReadByte("Level");
                  packet.ReadByte("Class");
                  packet.ReadInt32("Realm Id");
              }
  
              packet.ReadXORByte(guid, 1);
              packet.ReadXORByte(guid, 5);
              packet.ReadXORByte(guid, 0);
              packet.ReadXORByte(guid, 6);
              packet.ReadXORByte(guid, 2);
  
              if (hasData == 0)
              {
                  guid2[6] = packet.ReadBit();
                  guid1[7] = packet.ReadBit();
  
                  var bits38 = packet.ReadBits(6);
  
                  packet.StartBitStream(guid2, 1, 7, 2);
  
                  guid1[4] = packet.ReadBit();
                  guid2[4] = packet.ReadBit();
                  guid2[0] = packet.ReadBit();
                  guid1[1] = packet.ReadBit();
  
                  var count = new int[5];
                  for (var i = 0; i < 5; ++i)
                      count[i] = (int)packet.ReadBits(7);
  
                  guid1[3] = packet.ReadBit();
                  guid2[3] = packet.ReadBit();
  
                  packet.StartBitStream(guid1, 5, 0);
                  guid2[5] = packet.ReadBit();
  
                  packet.ReadBit(); // fake bit
  
                  guid1[2] = packet.ReadBit();
                  guid1[6] = packet.ReadBit();
  
                  packet.ReadWoWString("Name", bits38);
  
                  packet.ReadXORByte(guid2, 4);
                  packet.ReadXORByte(guid1, 3);
                  packet.ReadXORByte(guid2, 6);
                  packet.ReadXORByte(guid1, 2);
                  packet.ReadXORByte(guid1, 4);
                  packet.ReadXORByte(guid2, 5);
                  packet.ReadXORByte(guid2, 1);
                  packet.ReadXORByte(guid2, 7);
  
                  for (var i = 0; i < 5; ++i)
                      packet.ReadWoWString("Name Declined", count[i], i);
  
                  packet.ReadXORByte(guid2, 3);
                  packet.ReadXORByte(guid1, 7);
                  packet.ReadXORByte(guid1, 1);
                  packet.ReadXORByte(guid1, 6);
                  packet.ReadXORByte(guid2, 0);
                  packet.ReadXORByte(guid1, 0);
                  packet.ReadXORByte(guid2, 2);
                  packet.ReadXORByte(guid1, 5);
  
                  packet.WriteGuid("Guid1", guid1);
                  packet.WriteGuid("Guid2", guid2);
              }
  
              packet.WriteGuid("Guid", guid);
          }
  
          [Parser(Opcode.CMSG_NAME_QUERY)]
          public static void HandleNameQuery(Packet packet)
          {
              var guid = new byte[8];
  
              packet.StartBitStream(guid, 5, 7, 0, 1);
  
              var hasRealmId2 = packet.ReadBit();
              guid[6] = packet.ReadBit();
              var hasRealmId1 = packet.ReadBit();
              packet.StartBitStream(guid, 3, 2, 4);
              packet.ParseBitStream(guid, 0, 1, 3, 4, 6, 5, 2, 7);
  
              if (hasRealmId1)
                  packet.ReadInt32("Realm Id 1");
  
              if (hasRealmId2)
                  packet.ReadInt32("Realm Id 2");
  
              packet.WriteGuid("Guid", guid);
          }
  
          [Parser(Opcode.CMSG_QUERY_NPC_TEXT)]
          public static void HandleNpcTextQuery(Packet packet)
          {
              var entry = packet.ReadInt32("Entry");
  
  
gt:             var guid = new byte[8];