/// <summary>
        /// Gets the information about the exact build a given character in a given game mode is running.
        /// </summary>
        /// <param name="characterName">name of the character</param>
        /// <param name="mode">given game mode</param>
        /// <returns>information about the exact build a given character in a given game mode</returns>
        public async Task <APIBuild> GetAPIBuildAsync(string characterName, GW2GameMode mode)
        {
            APIBuild APIBuild = new APIBuild()
            {
                CharacterName = characterName,
                GameMode      = mode
            };

            string modestring = Enum.GetName(typeof(GW2GameMode), mode).ToLower();

            APIClasses.Character APIData = await api.Request <APIClasses.Character>($"v2/characters/{characterName}");

            List <APIClasses.CharacterSpecialization> specializations = APIData.Specializations[modestring];

            List <string> allProfessions = await api.Request <List <string> >("v2/professions");

            List <int> allStats = await api.Request <List <int> >("v2/itemstats");

            List <APIClasses.ItemStats> allStatData = new List <APIClasses.ItemStats>();

            int statCounter  = 0;
            int queryCounter = 0;


            //TODO download this on launch instead of every time
            while (statCounter < allStats.Count)
            {
                StringBuilder statQuery = new StringBuilder("ids=");

                while (queryCounter < 100 && statCounter < allStats.Count)
                {
                    statQuery.Append($"{allStats[statCounter]},");

                    queryCounter++;
                    statCounter++;
                }

                List <APIClasses.ItemStats> statData = await api.Request <List <APIClasses.ItemStats> >("v2/itemstats", statQuery.ToString());

                allStatData.AddRange(statData);

                queryCounter = 0;
            }

            List <string> statNames = new List <string>();

            for (int x = 0; x < allStats.Count; x++)
            {
                if (allStatData[x].Attributes.Count() < 3 || allStatData[x].Attributes[0].Multiplier.Equals(0f) || allStatData[x].Name.Equals(""))
                {
                    allStats[x] = -1;
                }
                else
                {
                    statNames.Add(allStatData[x].Name);
                }
            }

            for (int x = 0; x < allStats.Count; x++)
            {
                if (allStats[x] == -1)
                {
                    allStats.RemoveAt(x);
                    allStatData.RemoveAt(x);
                    x--;
                }
            }

            APIBuild.ProfessionData = await api.Request <Profession>($"v2/professions/{APIData.Profession}");

            APIBuild.Profession = APIBuild.ProfessionData.Name;
            APIBuild.ProfessionData.RelativeId = allProfessions.IndexOf(APIBuild.ProfessionData.Name);

            List <APIBuildTrait> allTraits = new List <APIBuildTrait>();

            StringBuilder traitQuery = new StringBuilder("ids=");

            for (int x = 0; x < specializations.Count; x++)
            {
                APIBuildSpecialization spec = new APIBuildSpecialization
                {
                    Id         = specializations[x].Id,
                    RelativeId = APIBuild.ProfessionData.Specializations.ToList().IndexOf(specializations[x].Id)
                };
                for (int t = 0; t < specializations[x].Traits.Count; t++)
                {
                    APIBuildTrait trait;
                    if (specializations[x].Traits[t] != null)
                    {
                        trait = new APIBuildTrait
                        {
                            Id = (int)specializations[x].Traits[t]
                        };
                        traitQuery.Append($"{trait.Id},");
                    }
                    else
                    {
                        trait = new APIBuildTrait
                        {
                            Id       = -1,
                            Position = TraitPosition.Unselected
                        };
                    }
                    spec.Traits.Add(trait);
                    allTraits.Add(trait);
                }
                APIBuild.Specializations.Add(spec);
            }

            List <APIClasses.Trait> traitData = await api.Request <List <APIClasses.Trait> >("v2/traits", traitQuery.ToString());

            for (int x = 0; x < traitData.Count; x++)
            {
                allTraits[x].Position = (TraitPosition)traitData[x].Order;
            }

            APIClasses.CharacterSkills skills = APIData.Skills[modestring];

            //APIBuild.Skills.Heals     = new int[] { skills.heal };
            //APIBuild.Skills.Utilities = new int[] { skills.utilities[0], skills.utilities[1], skills.utilities[2] };
            //APIBuild.Skills.Elites    = new int[] { skills.elite };

            //skills


            //Water / Legend2        -> Shiro
            //Fire  / Legend1        -> Glint
            //Earth / Legend4        -> Mallyx
            //Air   / Legend3        -> Jalis
            //Deathshroud / Legend6  -> Ventari
            //null        / Legend5  -> Kalla (lol) rev

            if (APIBuild.ProfessionData.Name == "Revenant")
            {
                StringBuilder legendQuery = new StringBuilder("ids=");
                for (int l = 0; l < skills.Legends.Count; l++)
                {
                    APIBuildRevenantLegend legend = APIBuildRevenantLegend.Parse(skills.Legends[l].ToString());
                    legendQuery.Append($"{legend.Name},");
                }

                List <APIClasses.Legend> legendData = await api.Request <List <APIClasses.Legend> >("v2/legends", legendQuery.ToString());

                if (skills.Legends.Count == 1)
                {
                    //the api is doing a thing, construct the first (currently equipped) legend from the skills.heal skills.util skills.elite stuff
                    legendData.Add(new APIClasses.Legend()
                    {
                        Heal      = skills.Heal,
                        Utilities = skills.Utilities.Where(i => i.HasValue).Select(i => i.Value).ToList(),
                        Elite     = skills.Elite
                    });
                }

                for (int l = 0; l < legendData.Count; l++)
                {
                    APIBuild.Skills.Heals.Add(new APIBuildSkill()
                    {
                        Id         = legendData[l].Heal,
                        RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(legendData[l].Heal)).FirstOrDefault())
                    });

                    APIBuild.Skills.Utilities.AddRange(new APIBuildSkill[] {
                        new APIBuildSkill()
                        {
                            Id = legendData[l].Utilities[0], RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(legendData[l].Utilities[0])).FirstOrDefault())
                        },
                        new APIBuildSkill()
                        {
                            Id = legendData[l].Utilities[1], RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(legendData[l].Utilities[1])).FirstOrDefault())
                        },
                        new APIBuildSkill()
                        {
                            Id = legendData[l].Utilities[2], RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(legendData[l].Utilities[2])).FirstOrDefault())
                        }
                    });

                    APIBuild.Skills.Elites.Add(new APIBuildSkill()
                    {
                        Id         = legendData[l].Elite,
                        RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(legendData[l].Elite)).FirstOrDefault())
                    });
                }
            }
            else
            {
                APIBuild.Skills.Heals.Add(new APIBuildSkill()
                {
                    Id         = skills.Heal,
                    RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(skills.Heal)).FirstOrDefault())
                });

                APIBuild.Skills.Utilities.AddRange(new APIBuildSkill[] {
                    new APIBuildSkill()
                    {
                        Id = (int)skills.Utilities[0], RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(skills.Utilities[0])).FirstOrDefault())
                    },
                    new APIBuildSkill()
                    {
                        Id = (int)skills.Utilities[1], RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(skills.Utilities[1])).FirstOrDefault())
                    },
                    new APIBuildSkill()
                    {
                        Id = (int)skills.Utilities[2], RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(skills.Utilities[2])).FirstOrDefault())
                    }
                });

                APIBuild.Skills.Elites.Add(new APIBuildSkill()
                {
                    Id         = skills.Elite,
                    RelativeId = APIBuild.ProfessionData.Skills.IndexOf(APIBuild.ProfessionData.Skills.Where(s => s.Id.Equals(skills.Elite)).FirstOrDefault())
                });
            }

            if (mode == GW2GameMode.PvP)
            {
                APIClasses.CharacterEquipmentPvP apiEquipmentPvP = APIData.EquipmentPvP;
                APIBuildPvPEquipment             equipment       = new APIBuildPvPEquipment
                {
                    Amulet = (apiEquipmentPvP.Amulet == null) ? 0 : (int)apiEquipmentPvP.Amulet,
                    Rune   = (apiEquipmentPvP.Rune == null) ? 0 : (int)apiEquipmentPvP.Rune
                };

                for (int x = 0; x < apiEquipmentPvP.Sigils.Count; x++)
                {
                    if (apiEquipmentPvP.Sigils[x] != null)
                    {
                        equipment.Sigils[x] = (int)apiEquipmentPvP.Sigils[x];
                    }
                    else
                    {
                        equipment.Sigils[x] = 0;
                    }
                }

                List <APIClasses.CharacterEquipment> apiEquipment = APIData.Equipment;

                var weaponA1 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponA1"))
                               .FirstOrDefault();
                var weaponA2 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponA2"))
                               .FirstOrDefault();
                var weaponB1 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponB1"))
                               .FirstOrDefault();
                var weaponB2 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponB2"))
                               .FirstOrDefault();

                var weapons = new List <APIClasses.CharacterEquipment>()
                {
                    weaponA1, weaponA2, weaponB1, weaponB2
                };

                if (APIBuild.ProfessionData.Name == "Elementalist" || APIBuild.ProfessionData.Name == "Engineer")
                {
                    //remove the second weapon set if it exists because we only have one set
                    weapons[2] = null;
                    weapons[3] = null;
                }

                StringBuilder weaponQuery = new StringBuilder("ids=");

                for (int x = 0; x < weapons.Count; x++)
                {
                    if (weapons[x] != null)
                    {
                        weaponQuery.Append($"{weapons[x].Id},");
                    }
                }

                List <APIClasses.Item> weaponData = await api.Request <List <APIClasses.Item> >("v2/items", weaponQuery.ToString());

                for (int x = 0; x < weapons.Count; x++)
                {
                    if (weapons[x] != null)
                    {
                        APIBuildWeapon weapon = new APIBuildWeapon();
                        var            item   = weaponData.Where(s => s.Id.Equals(weapons[x].Id)).FirstOrDefault();

                        weapon.Id   = item.Id;
                        weapon.Type = (WeaponType)Enum.Parse(typeof(WeaponType), item.Details.Type.ToString().Substring(0, 1).ToUpper() + item.Details.Type.ToString().ToLower().Substring(1));

                        //var weaponInfo    = APIBuild.Profession.weapons[weapon.Type.ToString()];
                        int wIndex = 0;
                        foreach (var w in APIBuild.ProfessionData.Weapons)
                        {
                            if (w.Key == weapon.Type.ToString())
                            {
                                weapon.RelativeId = wIndex;
                            }
                            wIndex++;
                        }

                        equipment.Weapons.Add(weapon);
                    }
                    else
                    {
                        equipment.Weapons.Add(null);
                    }
                }

                APIBuild.Equipment = equipment;
            }
            else
            {
                APIBuildPvEEquipment equipment = new APIBuildPvEEquipment();

                List <APIClasses.CharacterEquipment> apiEquipment = APIData.Equipment;

                var weaponA1 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponA1"))
                               .FirstOrDefault();
                var weaponA2 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponA2"))
                               .FirstOrDefault();
                var weaponB1 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponB1"))
                               .FirstOrDefault();
                var weaponB2 = apiEquipment
                               .Where(s => s.Slot.Equals("WeaponB2"))
                               .FirstOrDefault();

                var weapons = new List <APIClasses.CharacterEquipment>()
                {
                    weaponA1, weaponA2, weaponB1, weaponB2
                };

                StringBuilder weaponQuery = new StringBuilder("ids=");

                for (int x = 0; x < weapons.Count; x++)
                {
                    if (weapons[x] != null)
                    {
                        weaponQuery.Append($"{weapons[x].Id},");
                    }
                }

                List <APIClasses.Item> weaponData = await api.Request <List <APIClasses.Item> >("v2/items", weaponQuery.ToString());

                for (int x = 0; x < weapons.Count; x++)
                {
                    if (weapons[x] != null)
                    {
                        APIBuildWeapon weapon = new APIBuildWeapon();
                        var            item   = weaponData.Where(s => s.Id.Equals(weapons[x].Id)).FirstOrDefault();

                        weapon.Id   = item.Id;
                        weapon.Type = (WeaponType)Enum.Parse(typeof(WeaponType), item.Details.Type.ToString().Substring(0, 1).ToUpper() + item.Details.Type.ToString().ToLower().Substring(1));

                        //var weaponInfo    = APIBuild.Profession.weapons[weapon.Type.ToString()];
                        int wIndex = 0;
                        foreach (var w in APIBuild.ProfessionData.Weapons)
                        {
                            if (w.Key == weapon.Type.ToString())
                            {
                                weapon.RelativeId = wIndex;
                            }
                            wIndex++;
                        }

                        equipment.Weapons.Add(weapon);
                    }
                    else
                    {
                        equipment.Weapons.Add(null);
                    }
                }

                Dictionary <int, string> itemsToQuery = new Dictionary <int, string>();

                List <string> NonGearTypes = new List <string> {
                    "Pick", "Axe", "Sickle", "HelmAquatic", "WeaponAquaticA", "WeaponAquaticB"
                };
                List <string> Armor = new List <string> {
                    "Helm", "Shoulders", "Coat", "Gloves", "Leggings", "Boots"
                };
                List <string> Weapons = new List <string> {
                    "WeaponA1", "WeaponA2", "WeaponB1", "WeaponB2"
                };
                List <string> Trinkets = new List <string> {
                    "Backpack", "Accessory1", "Accessory2", "Amulet", "Ring1", "Ring2"
                };

                for (int x = 0; x < apiEquipment.Count; x++)
                {
                    if (!NonGearTypes.Contains(apiEquipment[x].Slot))
                    {
                        APIBuildItem item = new APIBuildItem
                        {
                            Id   = apiEquipment[x].Id,
                            Slot = apiEquipment[x].Slot
                        };

                        APIClasses.CharacterEquipment tryFindItem = APIData.Equipment
                                                                    .Where(s => s.Slot.Equals(apiEquipment[x].Slot))
                                                                    .FirstOrDefault();

                        if (tryFindItem != null && tryFindItem.Stats != null)
                        {
                            string statId = tryFindItem.Stats.Id.ToString();
                            APIClasses.ItemStats statData = allStatData.FirstOrDefault(s => s.Id.ToString().Equals(tryFindItem.Stats.Id.ToString()));
                            item.AttributeType = new AttributeType
                            {
                                Id   = tryFindItem.Stats.Id,
                                Name = statData.Name
                            };

                            item.AttributeType.RelativeId = allStats.IndexOf(item.AttributeType.Id);
                        }
                        else
                        {
                            itemsToQuery.Add(x, item.Id.ToString());
                        }

                        if (apiEquipment[x].Upgrades != null)
                        {
                            item.Upgrades = apiEquipment[x].Upgrades.ToArray();
                        }

                        int indexOf;

                        if ((indexOf = Armor.IndexOf(item.Slot)) != -1)
                        {
                            equipment.Armor[indexOf] = item;
                        }
                        else if ((indexOf = Trinkets.IndexOf(item.Slot)) != -1)
                        {
                            equipment.Trinkets[indexOf] = item;
                        }
                        else
                        {
                            APIBuildWeapon weapon = equipment.Weapons[Weapons.IndexOf(item.Slot)];
                            if (weapon != null)
                            {
                                weapon.Slot          = item.Slot;
                                weapon.AttributeType = item.AttributeType;
                                weapon.Upgrades      = item.Upgrades;
                            }
                        }
                    }
                }

                string itemQuery = $"ids={string.Join(",", itemsToQuery.Values)}";

                List <APIClasses.Item> loadedItems = new List <APIClasses.Item>();
                if (itemsToQuery.Keys.Count > 0)
                {
                    loadedItems = await api.Request <List <APIClasses.Item> >("v2/items", itemQuery);
                }

                foreach (KeyValuePair <int, string> pair in itemsToQuery)
                {
                    APIBuildItem[] items = equipment.AllItems.Where(i => i?.Id.ToString() == pair.Value).ToArray();
                    foreach (APIBuildItem item in items)
                    {
                        var itemData = loadedItems.Where(s => s.Id.Equals(item.Id)).FirstOrDefault();
                        if (itemData.Details.InfixUpgrade != null)
                        {
                            item.AttributeType = new AttributeType
                            {
                                Id = itemData.Details.InfixUpgrade.Id
                            };
                            item.AttributeType.RelativeId = allStats.IndexOf(item.AttributeType.Id);
                        }
                        else
                        {
                            item.AttributeType = new AttributeType
                            {
                                Id = 0
                            };
                            item.AttributeType.RelativeId = 0;
                        }
                    }
                }

                foreach (APIBuildItem i in equipment.Armor)
                {
                    if (i != null && i.Upgrades.Length > 0)
                    {
                        equipment.Runes.Add(i.Upgrades[0]);
                    }
                }

                foreach (APIBuildItem i in equipment.Weapons)
                {
                    if (i != null && i.Upgrades.Length > 0)
                    {
                        equipment.Sigils.AddRange(i.Upgrades);
                    }
                }

                if (APIBuild.ProfessionData.Name == "Ranger")
                {
                    //pet
                    for (int p = 0; p < skills.Pets["terrestrial"].Count; p++)
                    {
                        APIBuild.Pets.Add(new APIBuildPet()
                        {
                            Id = skills.Pets["terrestrial"][p]
                        });
                    }
                }

                APIBuild.Equipment = equipment;
            }

            return(APIBuild);
        }
        /// <summary>
        /// Get the build code for the build
        /// </summary>
        /// <returns>Hardstuck Builds code</returns>
        public string GetBuildCode()
        {
            List <int> relativeIds = new List <int>
            {
                (int)GameMode,
                ProfessionData.RelativeId
            };

            foreach (APIBuildSpecialization s in Specializations)
            {
                relativeIds.Add(s.RelativeId);
                foreach (APIBuildTrait t in s.Traits)
                {
                    relativeIds.Add((int)t.Position);
                }
            }

            foreach (APIBuildSkill s in Skills.Heals)
            {
                relativeIds.Add(s.RelativeId);
            }

            foreach (APIBuildSkill s in Skills.Utilities)
            {
                relativeIds.Add(s.RelativeId);
            }

            foreach (APIBuildSkill s in Skills.Elites)
            {
                relativeIds.Add(s.RelativeId);
            }

            if (Equipment.GetType() == typeof(APIBuildPvEEquipment))
            {
                APIBuildPvEEquipment PvEEquipment = Equipment as APIBuildPvEEquipment;
                AttributeType        curStat      = null;
                int curStatCounter = 0;

                foreach (APIBuildWeapon w in PvEEquipment.Weapons)
                {
                    if (w != null)
                    {
                        relativeIds.Add(w.RelativeId);
                    }
                }

                foreach (APIBuildItem i in PvEEquipment.AllItems)
                {
                    if (i != null)
                    {
                        if (curStat == null)
                        {
                            curStat = i.AttributeType;
                            relativeIds.Add(i.AttributeType.Id);
                        }

                        if (i.AttributeType.Name != curStat.Name)
                        {
                            if (curStatCounter > 0)
                            {
                                relativeIds.Add(curStatCounter);
                            }

                            curStatCounter = 0;
                            relativeIds.Add(i.AttributeType.Id);
                            curStat = i.AttributeType;
                        }
                        curStatCounter++;
                    }
                }

                if (curStatCounter > 0)
                {
                    relativeIds.Add(curStatCounter);
                }

                int curRune        = -1;
                int curRuneCounter = 0;

                foreach (int r in PvEEquipment.Runes)
                {
                    if (curRune == -1)
                    {
                        curRune = r;
                        relativeIds.Add(r);
                    }

                    if (r != curRune)
                    {
                        if (curRuneCounter > 0)
                        {
                            relativeIds.Add(curRuneCounter);
                        }

                        curRuneCounter = 0;
                        relativeIds.Add(r);
                        curRune = r;
                    }
                    curRuneCounter++;
                }

                if (curRuneCounter > 0)
                {
                    relativeIds.Add(curRuneCounter);
                }

                relativeIds.AddRange(PvEEquipment.Sigils);
            }
            else
            {
                APIBuildPvPEquipment PvPEquipment = Equipment as APIBuildPvPEquipment;

                foreach (APIBuildWeapon w in PvPEquipment.Weapons)
                {
                    if (w != null)
                    {
                        relativeIds.Add(w.RelativeId);
                    }
                }

                relativeIds.Add(PvPEquipment.Amulet);
                relativeIds.Add(PvPEquipment.Rune);
                for (int x = 0; x < PvPEquipment.Sigils.Length; x++)
                {
                    relativeIds.Add(PvPEquipment.Sigils[x]);
                }
            }

            if (ProfessionData.Name == "Ranger")
            {
                for (int x = 0; x < Pets.Count; x++)
                {
                    if (Pets[x] != null)
                    {
                        relativeIds.Add(Pets[x].Id);
                    }
                }
            }

            return(Letterize(relativeIds.ToArray()));
        }