context: using System;
  using System.Collections.Generic;
  using System.Diagnostics.CodeAnalysis;
  using WowPacketParser.Enums;
  using WowPacketParser.Enums.Version;
  using WowPacketParser.Misc;
  using WowPacketParser.Store;
  using WowPacketParser.Store.Objects;
  
  namespace WowPacketParser.Parsing.Parsers
  {
      [SuppressMessage("ReSharper", "UseObjectOrCollectionInitializer")]
      public static class QuestHandler
      {
          private static void ReadExtraQuestInfo510(Packet packet)
          {
              packet.ReadUInt32("Choice Item Count");
              for (var i = 0; i < 6; i++)
              {
                  packet.ReadUInt32<ItemId>("Choice Item Id", i);
                  packet.ReadUInt32("Choice Item Count", i);
                  packet.ReadUInt32("Choice Item Display Id", i);
              }
  
              packet.ReadUInt32("Reward Item Count");
  
              for (var i = 0; i < 4; i++)
                  packet.ReadUInt32<ItemId>("Reward Item Id", i);
              for (var i = 0; i < 4; i++)
                  packet.ReadUInt32("Reward Item Count", i);
              for (var i = 0; i < 4; i++)
                  packet.ReadUInt32("Reward Item Display Id", i);
  
              packet.ReadUInt32("Money");
              packet.ReadUInt32("XP");
              packet.ReadUInt32("Title Id");
              packet.ReadUInt32("Bonus Talents");
              packet.ReadUInt32("Reward Reputation Mask");
  
              for (var i = 0; i < 5; i++)
                  packet.ReadUInt32("Reputation Faction", i);
              for (var i = 0; i < 5; i++)
                  packet.ReadUInt32("Reputation Value Id", i);
              for (var i = 0; i < 5; i++)
                  packet.ReadInt32("Reputation Value", i);
  
              packet.ReadInt32<SpellId>("Spell Id");
              packet.ReadInt32<SpellId>("Spell Cast Id");
  
              for (var i = 0; i < 4; i++)
                  packet.ReadUInt32("Currency Id", i);
              for (var i = 0; i < 4; i++)
                  packet.ReadUInt32("Currency Count", i);
  
              packet.ReadUInt32("Reward SkillId");
              packet.ReadUInt32("Reward Skill Points");
          }
  
          private static void ReadExtraQuestInfo(Packet packet, bool readFlags = true)
          {
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V5_1_0_16309))
              {
                  ReadExtraQuestInfo510(packet);
                  return;
              }
  
              var choiceCount = packet.ReadUInt32("Choice Item Count");
              var effectiveChoiceCount = ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164) ? 6 : choiceCount;
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164))
              {
                  for (var i = 0; i < effectiveChoiceCount; i++)
                      packet.ReadUInt32<ItemId>("Choice Item Id", i);
                  for (var i = 0; i < effectiveChoiceCount; i++)
                      packet.ReadUInt32("Choice Item Count", i);
                  for (var i = 0; i < effectiveChoiceCount; i++)
                      packet.ReadUInt32("Choice Item Display Id", i);
  
                  packet.ReadUInt32("Reward Item Count");
                  const int effectiveRewardCount = 4;
  
                  for (var i = 0; i < effectiveRewardCount; i++)
                      packet.ReadUInt32<ItemId>("Reward Item Id", i);
                  for (var i = 0; i < effectiveRewardCount; i++)
                      packet.ReadUInt32("Reward Item Count", i);
                  for (var i = 0; i < effectiveRewardCount; i++)
                      packet.ReadUInt32("Reward Item Display Id", i);
              }
              else
              {
                  for (var i = 0; i < choiceCount; i++)
                  {
                      packet.ReadUInt32<ItemId>("Choice Item Id", i);
                      packet.ReadUInt32("Choice Item Count", i);
                      packet.ReadUInt32("Choice Item Display Id", i);
                  }
  
                  var rewardCount = packet.ReadUInt32("Reward Item Count");
                  for (var i = 0; i < rewardCount; i++)
                  {
                      packet.ReadUInt32<ItemId>("Reward Item Id", i);
                      packet.ReadUInt32("Reward Item Count", i);
                      packet.ReadUInt32("Reward Item Display Id", i);
                  }
              }
  
              packet.ReadUInt32("Money");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_2_2_10482))
                  packet.ReadUInt32("XP");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164))
              {
                  packet.ReadUInt32("Title Id");
                  packet.ReadUInt32("Unknown UInt32 1");
                  packet.ReadSingle("Unknown float");
                  packet.ReadUInt32("Bonus Talents");
                  packet.ReadUInt32("Unknown UInt32 2");
                  packet.ReadUInt32("Reward Reputation Mask");
              }
              else
              {
                  packet.ReadUInt32("Honor Points");
  
                  if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                      packet.ReadSingle("Honor Multiplier");
  
                  if (readFlags)
                          packet.ReadUInt32E<QuestFlags>("Quest Flags");
  
                  packet.ReadInt32<SpellId>("Spell Id");
                  packet.ReadInt32<SpellId>("Spell Cast Id");
                  packet.ReadUInt32("Title Id");
  
                  if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_0_2_9056))
                      packet.ReadUInt32("Bonus Talents");
  
                  if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                  {
                      packet.ReadUInt32("Arena Points");
                      packet.ReadUInt32("Unk UInt32");
                  }
              }
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
              {
                  for (var i = 0; i < 5; i++)
                      packet.ReadUInt32("Reputation Faction", i);
  
                  for (var i = 0; i < 5; i++)
                      packet.ReadUInt32("Reputation Value Id", i);
  
                  for (var i = 0; i < 5; i++)
                      packet.ReadInt32("Reputation Value", i);
              }
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164))
              {
                  packet.ReadInt32<SpellId>("Spell Id");
                  packet.ReadInt32<SpellId>("Spell Cast Id");
  
                  for (var i = 0; i < 4; i++)
                      packet.ReadUInt32("Currency Id", i);
                  for (var i = 0; i < 4; i++)
                      packet.ReadUInt32("Currency Count", i);
  
                  packet.ReadUInt32("Reward SkillId");
                  packet.ReadUInt32("Reward Skill Points");
              }
          }
  
          [Parser(Opcode.CMSG_QUERY_QUEST_INFO)]
          [Parser(Opcode.CMSG_PUSH_QUEST_TO_PARTY)]
          public static void HandleQuestQuery(Packet packet)
          {
              packet.ReadInt32("Entry");
          }
  
          [HasSniffData]
          [Parser(Opcode.SMSG_QUERY_QUEST_INFO_RESPONSE, ClientVersionBuild.Zero, ClientVersionBuild.V5_0_5_16048)]
          public static void HandleQuestQueryResponse(Packet packet)
          {
              var id = packet.ReadEntry("Quest ID");
              if (id.Value) // entry is masked
                  return;
  
              QuestTemplate quest = new QuestTemplate
              {
                  ID = (uint)id.Key
              };
  
              quest.QuestType = packet.ReadInt32E<QuestType>("QuestType");
              quest.QuestLevel = packet.ReadInt32("QuestLevel");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                  quest.MinLevel = packet.ReadInt32("QuestMinLevel");
  
              quest.QuestSortID = packet.ReadInt32E<QuestSort>("QuestSortID");
  
              quest.QuestInfoID = packet.ReadInt32E<QuestInfo>("QuestInfoID");
  
              quest.SuggestedGroupNum = packet.ReadUInt32("SuggestedGroupNum");
  
              quest.RequiredFactionID = new uint?[2];
              quest.RequiredFactionValue = new int?[2];
              for (int i = 0; i < 2; i++)
              {
                  quest.RequiredFactionID[i] = packet.ReadUInt32("RequiredFactionID", i);
                  quest.RequiredFactionValue[i] = packet.ReadInt32("RequiredFactionValue", i);
              }
  
              quest.NextQuestID = packet.ReadInt32<QuestId>("NextQuestID");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                  quest.RewardXPDifficulty = packet.ReadUInt32("RewardXPDifficulty");
  
              quest.RewardMoney = packet.ReadInt32("RewardMoney");
              quest.RewardBonusMoney = packet.ReadUInt32("RewardBonusMoney");
              quest.RewardDisplaySpell = (uint)packet.ReadInt32<SpellId>("RewardDisplaySpell");
              quest.RewardSpell = packet.ReadInt32<SpellId>("RewardSpell");
              quest.RewardHonor = packet.ReadInt32("RewardHonor");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                  quest.RewardKillHonor = packet.ReadSingle("RewardKillHonor");
  
              quest.StartItem = packet.ReadUInt32<ItemId>("StartItem");
              quest.Flags = packet.ReadUInt32E<QuestFlags>("Flags");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164))
                  quest.MinimapTargetMark = packet.ReadUInt32("MinimapTargetMark"); // missing enum. 1- Skull, 16 - Unknown, but exists
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V2_4_0_8089))
                  quest.RewardTitle = packet.ReadUInt32("RewardTitle");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_0_2_9056))
              {
                  quest.RequiredPlayerKills = packet.ReadUInt32("RequiredPlayerKills");
                  quest.RewardTalents = packet.ReadUInt32("RewardTalents");
              }
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                  quest.RewardArenaPoints = packet.ReadUInt32("RewardArenaPoints");
  
              // TODO: Find when was this added/removed and what is it
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958) && (ClientVersion.RemovedInVersion(ClientVersionBuild.V4_0_1_13164)))
                  packet.ReadInt32("Unknown Int32");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164))
              {
                  quest.RewardSkillLineID = packet.ReadUInt32("RewardSkillLineID");
                  quest.RewardNumSkillUps = packet.ReadUInt32("RewardNumSkillUps");
                  quest.RewardReputationMask = packet.ReadUInt32("RewardReputationMask");
                  quest.QuestGiverPortrait = packet.ReadUInt32("QuestGiverPortrait");
                  quest.QuestTurnInPortrait = packet.ReadUInt32("QuestTurnInPortrait");
              }
  
              quest.RewardItem = new uint?[4];
              quest.RewardAmount = new uint?[4];
              for (int i = 0; i < 4; i++)
              {
                  quest.RewardItem[i] = (uint) packet.ReadInt32<ItemId>("RewardItems", i);
                  quest.RewardAmount[i] = packet.ReadUInt32("RewardAmount", i);
              }
  
              quest.RewardChoiceItemID = new uint?[6];
              quest.RewardChoiceItemQuantity = new uint?[6];
              for (int i = 0; i < 6; i++)
              {
                  quest.RewardChoiceItemID[i] = (uint) packet.ReadInt32<ItemId>("RewardChoiceItemID", i);
                  quest.RewardChoiceItemQuantity[i] = packet.ReadUInt32("RewardChoiceItemQuantity", i);
              }
  
              const int repCount = 5;
              quest.RewardFactionID = new uint?[repCount];
              quest.RewardFactionValue = new int?[repCount];
              quest.RewardFactionOverride = new int?[repCount];
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
              {
                  for (int i = 0; i < repCount; i++)
                      quest.RewardFactionID[i] = packet.ReadUInt32("RewardFactionID", i);
  
                  for (int i = 0; i < repCount; i++)
                      quest.RewardFactionValue[i] = packet.ReadInt32("RewardFactionValue", i);
  
                  for (int i = 0; i < repCount; i++)
                      quest.RewardFactionOverride[i] = (int)packet.ReadUInt32("RewardFactionOverride", i);
              }
  
              quest.POIContinent = packet.ReadUInt32("POIContinent");
              quest.POIx = packet.ReadSingle("POIx");
              quest.POIy = packet.ReadSingle("POIy");
              quest.POIPriority = packet.ReadUInt32("POIPriority");
              quest.LogTitle = packet.ReadCString("LogTitle");
              quest.LogDescription = packet.ReadCString("LogDescription");
              quest.QuestDescription = packet.ReadCString("QuestDescription");
              quest.AreaDescription = packet.ReadCString("AreaDescription");
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                  quest.QuestCompletionLog = packet.ReadCString("QuestCompletionLog");
  
              var reqId = new KeyValuePair<int, bool>[4];
              quest.RequiredNpcOrGo = new int?[4];
              quest.RequiredNpcOrGoCount = new uint?[4];
              quest.RequiredItemID = new uint?[4];
              quest.RequiredItemCount = new uint?[4];
              int reqItemFieldCount = ClientVersion.AddedInVersion(ClientVersionBuild.V3_0_8_9464) ? 6 : 4;
              quest.RequiredItemID = new uint?[reqItemFieldCount];
              quest.RequiredItemCount = new uint?[reqItemFieldCount];
  
              for (int i = 0; i < 4; i++)
              {
                  reqId[i] = packet.ReadEntry();
                  bool isGo = reqId[i].Value;
                  quest.RequiredNpcOrGo[i] = reqId[i].Key * (isGo ? -1 : 1);
  
                  packet.AddValue("Required", (isGo ? "GO" : "NPC") +
                      " ID: " + StoreGetters.GetName(isGo ? StoreNameType.GameObject : StoreNameType.Unit, reqId[i].Key), i);
  
                  quest.RequiredNpcOrGoCount[i] = packet.ReadUInt32("RequiredCount", i);
  
                  if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_0_2_9056))
                      quest.RequiredItemID[i] = (uint) packet.ReadInt32<ItemId>("RequiredItemID", i);
  
                  if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_3_0_10958))
                      quest.RequiredItemCount[i] = packet.ReadUInt32("RequiredItemCount", i);
  
                  if (ClientVersion.RemovedInVersion(ClientVersionBuild.V3_0_8_9464))
                  {
                      quest.RequiredItemID[i] = (uint) packet.ReadInt32<ItemId>("RequiredItemID", i);
                      quest.RequiredItemCount[i] = packet.ReadUInt32("RequiredItemCount", i);
                  }
              }
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_0_8_9464))
              {
                  for (int i = 0; i < reqItemFieldCount; i++)
                  {
                      quest.RequiredItemID[i] = (uint) packet.ReadInt32<ItemId>("RequiredItemID", i);
                      quest.RequiredItemCount[i] = packet.ReadUInt32("RequiredItemCount", i);
                  }
              }
  
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164))
                  quest.RequiredSpell = packet.ReadUInt32<SpellId>("RequiredSpell");
  
              quest.ObjectiveText = new string[4];
              for (int i = 0; i < 4; i++)
                  quest.ObjectiveText[i] = packet.ReadCString("Objective Text", i);
  
              quest.RewardCurrencyID = new uint?[4];
              quest.RewardCurrencyCount = new uint?[4];
              quest.RequiredCurrencyID = new uint?[4];
              quest.RequiredCurrencyCount = new uint?[4];
              if (ClientVersion.AddedInVersion(ClientVersionBuild.V4_0_1_13164))
              {
                  for (int i = 0; i < 4; ++i)
                  {
                      quest.RewardCurrencyID[i] = packet.ReadUInt32("Reward Currency ID", i);
                      quest.RewardCurrencyCount[i] = packet.ReadUInt32("Reward Currency Count", i);
                  }
  
                  for (int i = 0; i < 4; ++i)
                  {
                      quest.RequiredCurrencyID[i] = packet.ReadUInt32("Required Currency ID", i);
                      quest.RequiredCurrencyCount[i] = packet.ReadUInt32("Required Currency Count", i);
                  }
  
                  quest.QuestGiverTextWindow = packet.ReadCString("QuestGiver Text Window");
                  quest.QuestGiverTargetName = packet.ReadCString("QuestGiver Target Name");
                  quest.QuestTurnTextWindow = packet.ReadCString("QuestTurn Text Window");
                  quest.QuestTurnTargetName = packet.ReadCString("QuestTurn Target Name");
  
                  quest.SoundAccept = packet.ReadUInt32("Sound Accept");
                  quest.SoundTurnIn = packet.ReadUInt32("Sound TurnIn");
              }
  
              packet.AddSniffData(StoreNameType.Quest, id.Key, "QUERY_RESPONSE");
  
              Storage.QuestTemplates.Add(quest, packet.TimeSpan);
          }
  
          [HasSniffData]
          [Parser(Opcode.SMSG_QUERY_QUEST_INFO_RESPONSE, ClientVersionBuild.V5_1_0_16309)]
          public static void HandleQuestQueryResponse510(Packet packet)
          {
              var id = packet.ReadEntry("Quest ID");
              if (id.Value) // entry is masked
                  return;
  
              QuestTemplate quest = new QuestTemplate
              {
                  ID = (uint)id.Key
              };
  
              quest.QuestType = packet.ReadInt32E<QuestType>("QuestType");
              quest.QuestLevel = packet.ReadInt32("QuestLevel");
  
              quest.QuestPackageID = packet.ReadUInt32("QuestPackageID");
              quest.MinLevel = packet.ReadInt32("QuestMinLevel");
              quest.QuestSortID = packet.ReadInt32E<QuestSort>("QuestSortID");
              quest.QuestInfoID = packet.ReadInt32E<QuestInfo>("QuestInfoID");
              quest.SuggestedGroupNum = packet.ReadUInt32("SuggestedGroupNum");
              quest.RewardNextQuest = (uint)packet.ReadInt32<QuestId>("RewardNextQuest");
              quest.RewardXPDifficulty = packet.ReadUInt32("RewardXPDifficulty");
              quest.RewardMoney = packet.ReadInt32("RewardMoney");
              quest.RewardBonusMoney = packet.ReadUInt32("RewardBonusMoney");
              quest.RewardDisplaySpell = (uint)packet.ReadInt32<SpellId>("RewardDisplaySpell");
              quest.RewardSpell = packet.ReadInt32<SpellId>("RewardSpell");
              quest.RewardHonor = packet.ReadInt32("Reward Honor");
              quest.RewardKillHonor = packet.ReadSingle("RewardKillHonor");
              quest.StartItem = packet.ReadUInt32<ItemId>("StartItem");
              quest.Flags = packet.ReadUInt32E<QuestFlags>("Flags");
              quest.FlagsEx = packet.ReadUInt32E<QuestFlags2>("FlagsEx");
              quest.MinimapTargetMark = packet.ReadUInt32("MinimapTargetMark"); // missing enum. 1- Skull, 16 - Unknown, but exists
              quest.RewardTitle = packet.ReadUInt32("RewardTitle");
              quest.RequiredPlayerKills = packet.ReadUInt32("RequiredPlayerKills");
              quest.RewardSkillLineID = packet.ReadUInt32("RewardSkillLineID");
              quest.RewardNumSkillUps = packet.ReadUInt32("RewardNumSkillUps");
              quest.RewardReputationMask = packet.ReadUInt32("RewRepMask");
              quest.QuestGiverPortrait = packet.ReadUInt32("QuestGiverPortrait");
              quest.QuestTurnInPortrait = packet.ReadUInt32("QuestTurnInPortrait");
  
              quest.RewardItem = new uint?[4];
              quest.RewardAmount = new uint?[4];
              for (int i = 0; i < 4; i++)
              {
                  quest.RewardItem[i] = (uint)packet.ReadInt32<ItemId>("Reward Item ID", i);
                  quest.RewardAmount[i] = packet.ReadUInt32("Reward Item Count", i);
              }
  
              quest.RewardChoiceItemID = new uint?[6];
              quest.RewardChoiceItemQuantity = new uint?[6];
              for (int i = 0; i < 6; i++)
              {
                  quest.RewardChoiceItemID[i] = (uint)packet.ReadInt32<ItemId>("Reward Choice Item ID", i);
                  quest.RewardChoiceItemQuantity[i] = packet.ReadUInt32("Reward Choice Item Count", i);
              }
  
              const int repCount = 5;
              quest.RewardFactionID = new uint?[repCount];
              quest.RewardFactionValue = new int?[repCount];
              quest.RewardFactionOverride = new int?[repCount];
              for (int i = 0; i < repCount; i++)
                  quest.RewardFactionID[i] = packet.ReadUInt32("RewardFactionID", i);
  
              for (int i = 0; i < repCount; i++)
                  quest.RewardFactionValue[i] = packet.ReadInt32("RewardFactionValue", i);
  
              for (int i = 0; i < repCount; i++)
                  quest.RewardFactionOverride[i] = packet.ReadInt32("RewardFactionOverride", i);
  
              quest.RewardCurrencyID = new uint?[4];
              quest.RewardCurrencyCount = new uint?[4];
              for (int i = 0; i < 4; i++)
              {
                  quest.RewardCurrencyID[i] = packet.ReadUInt32("Reward Currency ID", i);
                  quest.RewardCurrencyCount[i] = packet.ReadUInt32("Reward Currency Count", i);
              }
  
              quest.POIContinent = packet.ReadUInt32("POIContinent");
              quest.POIx = packet.ReadSingle("POIx");
              quest.POIy = packet.ReadSingle("POIy");
              quest.POIPriority = packet.ReadUInt32("POIPriority");
  
              quest.LogTitle = packet.ReadCString("LogTitle");
              quest.LogDescription = packet.ReadCString("LogDescription");
              quest.QuestDescription = packet.ReadCString("QuestDescription");
              quest.AreaDescription = packet.ReadCString("AreaDescription");
              quest.QuestCompletionLog = packet.ReadCString("QuestCompletionLog");
              quest.QuestGiverTextWindow = packet.ReadCString("QuestGiver Text Window");
              quest.QuestGiverTargetName = packet.ReadCString("QuestGiver Target Name");
              quest.QuestTurnTextWindow = packet.ReadCString("QuestTurn Text Window");
              quest.QuestTurnTargetName = packet.ReadCString("QuestTurn Target Name");
  
  
gt:             quest.SoundAccept = packet.ReadUInt32("Sound Accept");