local DGV = DugisGuideViewer
if not DGV then return end
local L = DugisLocals

local GA = DGV:RegisterModule("GearAdvisor")
GA.essential = true
local _
local StatLogic = LibStub("LibStatLogic-1.2-Dugi")
local TipHooker = LibStub("LibTipHooker-1.1")
local select, GetAuctionItemSubClasses, GetExpertise, GetCombatRatingBonus, GetHitModifier, GetSpellHitModifier, GetMeleeHaste, GetRangedHaste, UnitSpellHaste =
	select, GetAuctionItemSubClasses, GetExpertise, GetCombatRatingBonus, GetHitModifier, GetSpellHitModifier, GetMeleeHaste, GetRangedHaste, UnitSpellHaste
local GetCritChance, GetSpellCritChance, GetRangedCritChance, GetQuestLogChoiceInfo, GetQuestLogRewardInfo, GetQuestLogItemLink, GetQuestItemLink, GetQuestItemInfo, GetNumBankSlots =
	GetCritChance, GetSpellCritChance, GetRangedCritChance, GetQuestLogChoiceInfo, GetQuestLogRewardInfo, GetQuestLogItemLink, GetQuestItemLink, GetQuestItemInfo, GetNumBankSlots
local GetContainerItemLink, BANK_CONTAINER_INVENTORY_OFFSET, EQUIPPED_FIRST, EQUIPPED_LAST, NUM_BANKGENERIC_SLOTS, BACKPACK_CONTAINER =
	GetContainerItemLink, BANK_CONTAINER_INVENTORY_OFFSET,EQUIPPED_FIRST, EQUIPPED_LAST, NUM_BANKGENERIC_SLOTS, BACKPACK_CONTAINER
local UnitClass, GetInventoryItemLink, GetItemInfo, UnitLevel, GetInventoryItemID, GetCreateTable, QueueInvocation, strformat, RegisterReaction, RegisterFunctionReaction, RegisterMemberFunctionReaction, RegisterStopwatchReaction, TryGetCacheReaction, ListContains, PackStrings =
		UnitClass, GetInventoryItemLink, GetItemInfo, UnitLevel, GetInventoryItemID, DGV.GetCreateTable, DGV.QueueInvocation, string.format, DGV.RegisterReaction, DGV.RegisterFunctionReaction, DGV.RegisterMemberFunctionReaction, DGV.RegisterStopwatchReaction, DGV.TryGetCacheReaction, DGV.ListContains, DGV.PackStrings
local BeginAutoroutine, InterruptAutoroutine, YieldAutoroutine, tPool, DoOutOfCombat, GetRunningAutoroutine =
	DGV.BeginAutoroutine, DGV.InterruptAutoroutine, DGV.YieldAutoroutine, DGV.tPool, DGV.DoOutOfCombat, DGV.GetRunningAutoroutine


DugisGuideViewer.defaultLevelingSpec = {
    ["DEATHKNIGHT"] = {["index"] = 1, ["orderIndex"] = 1},
    ["MONK"] = {["index"] = 3, ["orderIndex"] = 2},
    ["WARRIOR"] = {["index"] = 1, ["orderIndex"] = 3},
    ["PALADIN"] = {["index"] = 3, ["orderIndex"] = 4},
    ["DRUID"] = {["index"] = 2, ["orderIndex"] = 5},
    ["ROGUE"] = {["index"] = 1, ["orderIndex"] = 6},
    ["SHAMAN"] = {["index"] = 2, ["orderIndex"] = 7},
    ["HUNTER"] = {["index"] = 1, ["orderIndex"] = 8},
    ["MAGE"] = {["index"] = 1, ["orderIndex"] = 9},
    ["PRIEST"] = {["index"] = 3, ["orderIndex"] = 10},
    ["WARLOCK"] = {["index"] = 2, ["orderIndex"] = 11},
    ["DEMONHUNTER"] = {["index"] = 1, ["orderIndex"] = 12},
}

local weightIdentifier2weightLabelMap = {
    ["AGI"] = "Agility"
    ,["AP"] = "Attack Power"
    ,["ARMOR"] = "Armor"
--    ,["BONUS_ARMOR"] = "Bonus Armor"
    ,["AVOIDANCE_RATING"] = "Avoidance"
    ,["DPS"] = "DPS"
    ,["DPS|MAIN"] = "DPS - Main"
    ,["DPS|OFF"] = "DPS - Off"
    ,["INT"] = "Intellect"
    ,["LEECH_RATING"] = "Leech"
    ,["MASTERY_RATING"] = "Mastery"
    ,["MELEE_CRIT_RATING"] = "Critical Rating"
    ,["MELEE_HASTE_RATING"] = "Haste Rating"
--    ,["MULTISTRIKE_RATING"] = "Multistrike"
    ,["RANGED_CRIT_RATING"] = "Critical Rating"
    ,["RANGED_HASTE_RATING"] = "Haste Rating"
    ,["SPELL_CRIT_RATING"] = "Critical Rating"
    ,["SPELL_HASTE_RATING"] = "Haste Rating"
    ,["SPELL_POWER"] = "Spell Power"
    ,["SPELL_DMG"] = "Spell Power"
--    ,["SPI"] = "Spirit"
    ,["STA"] = "Stamina"
    ,["STR"] = "Strength"
    ,["VERSATILITY_RATING"] = "Versatility"
    ,["XP_BONUS"] = "XP Bonus"
}

function GA:Initialize()

    if Storyline_NPCFrameRewardsItem then
        local oldOnClickScript = Storyline_NPCFrameRewardsItem:GetScript("OnClick")
        Storyline_NPCFrameRewardsItem:SetScript("OnClick", function()
            oldOnClickScript(Storyline_NPCFrameRewardsItem)
            GA.EvaluateRewards()
        end)

        Storyline_NPCFrameRewards.Content:SetScript("OnShow", function()
            GA.EvaluateRewards()
        end)
    end

		local LE_LE_ITEM_CLASS_WEAPON, LE_LE_ITEM_CLASS_ARMOR = LE_LE_ITEM_CLASS_WEAPON, LE_LE_ITEM_CLASS_ARMOR
		local LE_ITEM_ARMOR_COSMETIC, LE_ITEM_ARMOR_SHIELD = LE_ITEM_ARMOR_COSMETIC, LE_ITEM_ARMOR_SHIELD
		local LE_ITEM_WEAPON_FISHINGPOLE = LE_ITEM_WEAPON_FISHINGPOLE
		local LE_ITEM_WEAPON_BOWS, LE_ITEM_WEAPON_GUNS, LE_ITEM_WEAPON_CROSSBOW = LE_ITEM_WEAPON_BOWS, LE_ITEM_WEAPON_GUNS, LE_ITEM_WEAPON_CROSSBOW
		local LE_ITEM_WEAPON_POLEARM, LE_ITEM_WEAPON_STAFF = LE_ITEM_WEAPON_POLEARM, LE_ITEM_WEAPON_STAFF
		local LE_ITEM_WEAPON_AXE2H, LE_ITEM_WEAPON_MACE2H, LE_ITEM_WEAPON_SWORD2H = LE_ITEM_WEAPON_AXE2H, LE_ITEM_WEAPON_MACE2H, LE_ITEM_WEAPON_SWORD2H

		--LE_ITEM_WEAPON_AXE1H=0,
		--LE_ITEM_WEAPON_AXE2H=1,
		--LE_ITEM_WEAPON_BOWS=2
		--LE_ITEM_WEAPON_GUNS=3
		--LE_ITEM_WEAPON_MACE1H=4
		--LE_ITEM_WEAPON_MACE2H=5,
		--LE_ITEM_WEAPON_POLEARM=6,
		--LE_ITEM_WEAPON_SWORD1H=7
		--LE_ITEM_WEAPON_SWORD2H=8
		--LE_ITEM_WEAPON_WARGLAIVE=9
		--LE_ITEM_WEAPON_STAFF=10
		--LE_ITEM_WEAPON_UNARMED=13
		--LE_ITEM_WEAPON_GENERIC=14
		--LE_ITEM_WEAPON_DAGGER=15
		--LE_ITEM_WEAPON_THROWN=16
		--LE_ITEM_WEAPON_CROSSBOW=18
		--LE_ITEM_WEAPON_WAND=19

		--LE_ITEM_ARMOR_GENERIC=0
		--LE_ITEM_ARMOR_CLOTH=1
		--LE_ITEM_ARMOR_LEATHER=2
		--LE_ITEM_ARMOR_MAIL=3
		--LE_ITEM_ARMOR_PLATE=4
		--LE_ITEM_ARMOR_COSMETIC=5
		--LE_ITEM_ARMOR_SHIELD=6

		local scoring = {

	        ["DEATHKNIGHT:1"] =           "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,0.2:STR,1.0:STA,0.3:MASTERY_RATING,0.5:VERSATILITY_RATING,0.39:ARMOR,0.7:MELEE_CRIT_RATING,0.4:AP,0.1:AVOIDANCE_RATING,0.15:LEECH_RATING,0:MELEE_HASTE_RATING,0.32:AGI,0:INT,0:SPELL_DMG,0",
	        ["DEATHKNIGHT:1:PVP"] =       "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,0.2:STR,1.0:STA,0.3:MASTERY_RATING,0.5:VERSATILITY_RATING,0.39:ARMOR,0.7:MELEE_CRIT_RATING,0.4:AP,0.1:AVOIDANCE_RATING,0.15:LEECH_RATING,0:MELEE_HASTE_RATING,0.32:AGI,0:INT,0:SPELL_DMG,0",
	        ["DEATHKNIGHT:2"] =           "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,4,7:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,1.55:DPS|MAIN,0.95:DPS|OFF,0.46:STR,1:VERSATILITY_RATING,0.64:MASTERY_RATING,0.7:MELEE_HASTE_RATING,0.65:AP,0.71:MELEE_CRIT_RATING,0.5:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["DEATHKNIGHT:2:PVP"] =       "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,4,7:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,1.55:DPS|MAIN,0.95:DPS|OFF,0.46:STR,1:VERSATILITY_RATING,0.64:MASTERY_RATING,0.7:MELEE_HASTE_RATING,0.65:AP,0.71:MELEE_CRIT_RATING,0.5:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["DEATHKNIGHT:3"] =           "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,0.78:STR,1:VERSATILITY_RATING,0.64:MELEE_HASTE_RATING,0.82:MELEE_CRIT_RATING,0.65:AP,0.75:MASTERY_RATING,0.6:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["DEATHKNIGHT:3:PVP"] =       "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,0.78:STR,1:VERSATILITY_RATING,0.64:MELEE_HASTE_RATING,0.82:MELEE_CRIT_RATING,0.65:AP,0.75:MASTERY_RATING,0.6:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["MONK:1"] =                  "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,6,10:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.2:DPS|MAIN,0.2:DPS|OFF,0.1:AGI,0.9:VERSATILITY_RATING,0.58:MELEE_HASTE_RATING,0.24:MELEE_CRIT_RATING,0.56:ARMOR,1.6:MASTERY_RATING,1.02:AVOIDANCE_RATING,0:AP,0.2:STA,0.65:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["MONK:1:PVP"] =              "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,6,10:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.2:DPS|MAIN,0.2:DPS|OFF,0.1:AGI,0.9:VERSATILITY_RATING,0.58:MELEE_HASTE_RATING,0.24:MELEE_CRIT_RATING,0.56:ARMOR,1.6:MASTERY_RATING,1.02:AVOIDANCE_RATING,0:AP,0.2:STA,0.65:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["MONK:2"] =                  "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,4,6,7,10,13:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:INT,89:SPELL_DMG,1.68:SPELL_HASTE_RATING,0.61:SPELL_CRIT_RATING,0.98:MASTERY_RATING,0.34:VERSATILITY_RATING,0.92:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["MONK:2:PVP"] =              "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,4,6,7,10,13:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:INT,89:SPELL_DMG,1.68:SPELL_HASTE_RATING,0.61:SPELL_CRIT_RATING,0.98:MASTERY_RATING,0.34:VERSATILITY_RATING,0.92:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["MONK:3"] =                  "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,7,13:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,2.68:DPS|MAIN,2.32:DPS|OFF,1.19:AGI,1:VERSATILITY_RATING,0.39:MELEE_HASTE_RATING,0.4:MELEE_CRIT_RATING,0.41:MASTERY_RATING,0.23:AP,0.9:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["MONK:3:PVP"] =              "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,7,13:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,2.68:DPS|MAIN,2.32:DPS|OFF,1.19:AGI,1:VERSATILITY_RATING,0.39:MELEE_HASTE_RATING,0.4:MELEE_CRIT_RATING,0.41:MASTERY_RATING,0.23:AP,0.9:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["WARRIOR:1"] =               "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,2.51:STR,1:VERSATILITY_RATING,0.69:MELEE_CRIT_RATING,0.96:AP,0.89:MASTERY_RATING,0.86:MELEE_HASTE_RATING,0.63:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["WARRIOR:1:PVP"] =           "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,2.51:STR,1:VERSATILITY_RATING,0.69:MELEE_CRIT_RATING,0.96:AP,0.89:MASTERY_RATING,0.86:MELEE_HASTE_RATING,0.63:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["WARRIOR:2"] =               "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,1.78:DPS|MAIN,1.78:DPS|OFF,1.52:STR,1:VERSATILITY_RATING,0.73:MELEE_CRIT_RATING,0.85,0.30,0.7:MASTERY_RATING,0.82:MELEE_HASTE_RATING,0.77:AP,0.9:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["WARRIOR:2:PVP"] =           "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,1.78:DPS|MAIN,1.78:DPS|OFF,1.52:STR,1:VERSATILITY_RATING,0.73:MELEE_CRIT_RATING,0.85,0.30,0.7:MASTERY_RATING,0.82:MELEE_HASTE_RATING,0.77:AP,0.9:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["WARRIOR:3"] =               "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,2.0:STA,0.6:MASTERY_RATING,0.7:STR,0.65:AVOIDANCE_RATING,0.17:ARMOR,0.85:MELEE_HASTE_RATING,0.28:MELEE_CRIT_RATING,0.57:AP,0.2:LEECH_RATING,0:VERSATILITY_RATING,0.54:AGI,0:INT,0:SPELL_DMG,0",
	        ["WARRIOR:3:PVP"] =           "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,2.0:STA,0.6:MASTERY_RATING,0.7:STR,0.65:AVOIDANCE_RATING,0.17:ARMOR,0.85:MELEE_HASTE_RATING,0.28:MELEE_CRIT_RATING,0.57:AP,0.2:LEECH_RATING,0:VERSATILITY_RATING,0.54:AGI,0:INT,0:SPELL_DMG,0",
	        ["PALADIN:1"] =               "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:MASTERY_RATING,0.8:SPELL_CRIT_RATING,1.1:SPELL_HASTE_RATING,1:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PALADIN:1:PVP"] =           "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:MASTERY_RATING,0.8:SPELL_CRIT_RATING,1.1:SPELL_HASTE_RATING,1:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PALADIN:2"] =               "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,0.2:STA,0.5:MELEE_CRIT_RATING,0.4:VERSATILITY_RATING,0.45:MASTERY_RATING,0.59:ARMOR,0.6:MELEE_HASTE_RATING,0.6:STR,0.75:AVOIDANCE_RATING,0.0:LEECH_RATING,0.00:AP,0.2:AGI,0:INT,0:SPELL_DMG,0",
	        ["PALADIN:2:PVP"] =           "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,0,1,4,5,6,7,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4,6:XP_BONUS,1000:DPS,0.2:STA,0.5:MELEE_CRIT_RATING,0.4:VERSATILITY_RATING,0.45:MASTERY_RATING,0.59:ARMOR,0.6:MELEE_HASTE_RATING,0.6:STR,0.75:AVOIDANCE_RATING,0.0:LEECH_RATING,0.00:AP,0.2:AGI,0:INT,0:SPELL_DMG,0",
	        ["PALADIN:3"] =               "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,1,5,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,2.09:STR,1:VERSATILITY_RATING,0.7:MELEE_HASTE_RATING,0.76:MASTERY_RATING,0.8:AP,0.9:MELEE_CRIT_RATING,0.75:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["PALADIN:3:PVP"] =           "ARMOR_SPECIALIZATION_STAT,STR:LE_ITEM_CLASS_WEAPON,1,5,8:LE_ITEM_CLASS_ARMOR,0,1,2,3,4:XP_BONUS,1000:DPS,2.09:STR,1:VERSATILITY_RATING,0.7:MELEE_HASTE_RATING,0.76:MASTERY_RATING,0.8:AP,0.9:MELEE_CRIT_RATING,0.75:STA,0:AGI,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0",
	        ["DRUID:1"] =                 "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:INT,1:SPELL_DMG,0.9:SPELL_HASTE_RATING,0.64:SPELL_CRIT_RATING,0.68:MASTERY_RATING,0.75:VERSATILITY_RATING,0.6:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["DRUID:1:PVP"] =             "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:INT,1:SPELL_DMG,0.9:SPELL_HASTE_RATING,0.64:SPELL_CRIT_RATING,0.68:MASTERY_RATING,0.75:VERSATILITY_RATING,0.6:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["DRUID:2"] =                 "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.82:AGI,1:AP,0.88:VERSATILITY_RATING,0.46:MASTERY_RATING,0.54:MELEE_CRIT_RATING,0.61:MELEE_HASTE_RATING,0.42:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["DRUID:2:PVP"] =             "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.82:AGI,1:AP,0.88:VERSATILITY_RATING,0.46:MASTERY_RATING,0.54:MELEE_CRIT_RATING,0.61:MELEE_HASTE_RATING,0.42:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["DRUID:3"] =                 "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.2:AGI,0.7:STA,0.4:MELEE_CRIT_RATING,0.21:VERSATILITY_RATING,0.39:MASTERY_RATING,0.44:AVOIDANCE_RATING,0:MELEE_HASTE_RATING,0.3:ARMOR,2:AP,0.2:LEECH_RATING,0:INT,0:SPELL_DMG,0:STR,0",
	        ["DRUID:3:PVP"] =             "ARMOR_SPECIALIZATION_STAT,STA:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.2:AGI,0.7:STA,0.4:MELEE_CRIT_RATING,0.21:VERSATILITY_RATING,0.39:MASTERY_RATING,0.44:AVOIDANCE_RATING,0:MELEE_HASTE_RATING,0.3:ARMOR,2:AP,0.2:LEECH_RATING,0:INT,0:SPELL_DMG,0:STR,0",
	        ["DRUID:4"] =                 "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:SPELL_HASTE_RATING,1.1:MASTERY_RATING,1:SPELL_CRIT_RATING,0.8:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["DRUID:4:PVP"] =             "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,4,5,6,10,13,14,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:SPELL_HASTE_RATING,1.1:MASTERY_RATING,1:SPELL_CRIT_RATING,0.8:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["ROGUE:1"] =                 "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.75:DPS|MAIN,0.75:DPS|OFF,0.22:AGI,1:VERSATILITY_RATING,0.48:MASTERY_RATING,0.57:MELEE_HASTE_RATING,0.44:AP,0.91:MELEE_CRIT_RATING,0.52:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["ROGUE:1:PVP"] =             "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.75:DPS|MAIN,0.75:DPS|OFF,0.22:AGI,1:VERSATILITY_RATING,0.48:MASTERY_RATING,0.57:MELEE_HASTE_RATING,0.44:AP,0.91:MELEE_CRIT_RATING,0.52:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["ROGUE:2"] =                 "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,7,13,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.78:DPS|MAIN,0.78:DPS|OFF,0.42:AGI,1:VERSATILITY_RATING,0.48:MELEE_HASTE_RATING,0.67:MASTERY_RATING,0.49:MELEE_CRIT_RATING,0.51:AP,0.91:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["ROGUE:2:PVP"] =             "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,7,13,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,0.78:DPS|MAIN,0.78:DPS|OFF,0.42:AGI,1:VERSATILITY_RATING,0.48:MELEE_HASTE_RATING,0.67:MASTERY_RATING,0.49:MELEE_CRIT_RATING,0.51:AP,0.91:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["ROGUE:3"] =                 "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,7,13,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,1.07:DPS|MAIN,1.07:DPS|OFF,0.22:AGI,1:VERSATILITY_RATING,0.4:MASTERY_RATING,0.51:MELEE_HASTE_RATING,0.41:AP,0.78:MELEE_CRIT_RATING,0.42:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["ROGUE:3:PVP"] =             "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,7,13,15:LE_ITEM_CLASS_ARMOR,0,1,2:XP_BONUS,1000:DPS,1.07:DPS|MAIN,1.07:DPS|OFF,0.22:AGI,1:VERSATILITY_RATING,0.4:MASTERY_RATING,0.51:MELEE_HASTE_RATING,0.41:AP,0.78:MELEE_CRIT_RATING,0.42:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["SHAMAN:1"] =                "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,1,4,5,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,6:XP_BONUS,1000:INT,1:SPELL_DMG,0.9:SPELL_HASTE_RATING,0.78:MASTERY_RATING,0.55:SPELL_CRIT_RATING,0.73:VERSATILITY_RATING,0.66:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["SHAMAN:1:PVP"] =            "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,1,4,5,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,6:XP_BONUS,1000:INT,1:SPELL_DMG,0.9:SPELL_HASTE_RATING,0.78:MASTERY_RATING,0.55:SPELL_CRIT_RATING,0.73:VERSATILITY_RATING,0.66:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["SHAMAN:2"] =                "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,6:XP_BONUS,1000:DPS,0.51:DPS|MAIN,0.51:DPS|OFF,0.38:AGI,1:VERSATILITY_RATING,0.49:MELEE_HASTE_RATING,0.56:MASTERY_RATING,0.54:MELEE_CRIT_RATING,0.5:AP,0.91:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["SHAMAN:2:PVP"] =            "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,4,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,6:XP_BONUS,1000:DPS,0.51:DPS|MAIN,0.51:DPS|OFF,0.38:AGI,1:VERSATILITY_RATING,0.49:MELEE_HASTE_RATING,0.56:MASTERY_RATING,0.54:MELEE_CRIT_RATING,0.5:AP,0.91:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["SHAMAN:3"] =                "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,1,4,5,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,6:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:MASTERY_RATING,1.1:SPELL_CRIT_RATING,0.9:VERSATILITY_RATING,0.7:DPS,0.01:SPELL_HASTE_RATING,1.0:LEECH_RATING,0.6:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:STR,0",
	        ["SHAMAN:3:PVP"] =            "ARMOR_SPECIALIZATION_STAT,INT:LE_ITEM_CLASS_WEAPON,0,1,4,5,10,13,15:LE_ITEM_CLASS_ARMOR,0,1,2,3,6:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:MASTERY_RATING,1.1:SPELL_CRIT_RATING,0.9:VERSATILITY_RATING,0.7:DPS,0.01:SPELL_HASTE_RATING,1.0:LEECH_RATING,0.6:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:STR,0",
	        ["HUNTER:1"] =                "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,2,3,18:LE_ITEM_CLASS_ARMOR,0,1,2,3:XP_BONUS,1000:DPS,0.76:AGI,1:VERSATILITY_RATING,0.48:AP,0.9:RANGED_CRIT_RATING,0.49:RANGED_HASTE_RATING,0.53,0.1928,0.5:MASTERY_RATING,0.46:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["HUNTER:1:PVP"] =            "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,2,3,18:LE_ITEM_CLASS_ARMOR,0,1,2,3:XP_BONUS,1000:DPS,0.76:AGI,1:VERSATILITY_RATING,0.48:AP,0.9:RANGED_CRIT_RATING,0.49:RANGED_HASTE_RATING,0.53,0.1928,0.5:MASTERY_RATING,0.46:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["HUNTER:2"] =                "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,2,3,18:LE_ITEM_CLASS_ARMOR,0,1,2,3:XP_BONUS,1000:DPS,2.48:AGI,1:VERSATILITY_RATING,0.56:RANGED_CRIT_RATING,0.65:RANGED_HASTE_RATING,0.43:AP,0.9:MASTERY_RATING,0.55:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["HUNTER:2:PVP"] =            "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,2,3,18:LE_ITEM_CLASS_ARMOR,0,1,2,3:XP_BONUS,1000:DPS,2.48:AGI,1:VERSATILITY_RATING,0.56:RANGED_CRIT_RATING,0.65:RANGED_HASTE_RATING,0.43:AP,0.9:MASTERY_RATING,0.55:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["HUNTER:3"] =                "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,1,6,8,10:LE_ITEM_CLASS_ARMOR,0,1,2,3:XP_BONUS,1000:DPS,1:AGI,1:VERSATILITY_RATING,0.5:AP,0.9:RANGED_CRIT_RATING,0.49:RANGED_HASTE_RATING,0.42:MASTERY_RATING,0.41:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["HUNTER:3:PVP"] =            "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,1,6,8,10:LE_ITEM_CLASS_ARMOR,0,1,2,3:XP_BONUS,1000:DPS,1:AGI,1:VERSATILITY_RATING,0.5:AP,0.9:RANGED_CRIT_RATING,0.49:RANGED_HASTE_RATING,0.42:MASTERY_RATING,0.41:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["MAGE:1"] =                  "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.91:SPELL_HASTE_RATING,0.75:SPELL_CRIT_RATING,0.73:VERSATILITY_RATING,0.64:MASTERY_RATING,0.76:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["MAGE:1:PVP"] =              "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.91:SPELL_HASTE_RATING,0.75:SPELL_CRIT_RATING,0.73:VERSATILITY_RATING,0.64:MASTERY_RATING,0.76:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["MAGE:2"] =                  "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.91:SPELL_HASTE_RATING,0.61:SPELL_CRIT_RATING,0.85:VERSATILITY_RATING,0.53:MASTERY_RATING,0.75:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["MAGE:2:PVP"] =              "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.91:SPELL_HASTE_RATING,0.61:SPELL_CRIT_RATING,0.85:VERSATILITY_RATING,0.53:MASTERY_RATING,0.75:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["MAGE:3"] =                  "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.91:SPELL_HASTE_RATING,0.73:SPELL_CRIT_RATING,0.7:VERSATILITY_RATING,0.62:MASTERY_RATING,0.58:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["MAGE:3:PVP"] =              "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.91:SPELL_HASTE_RATING,0.73:SPELL_CRIT_RATING,0.7:VERSATILITY_RATING,0.62:MASTERY_RATING,0.58:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PRIEST:1"] =                "LE_ITEM_CLASS_WEAPON,10,15,4,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:SPELL_HASTE_RATING,0.8:SPELL_CRIT_RATING,1:MASTERY_RATING,1.2:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PRIEST:1:PVP"] =            "LE_ITEM_CLASS_WEAPON,10,15,4,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:SPELL_HASTE_RATING,0.8:SPELL_CRIT_RATING,1:MASTERY_RATING,1.2:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PRIEST:2"] =                "LE_ITEM_CLASS_WEAPON,10,15,4,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:SPELL_HASTE_RATING,1:SPELL_CRIT_RATING,0.8:MASTERY_RATING,0.9:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PRIEST:2:PVP"] =            "LE_ITEM_CLASS_WEAPON,10,15,4,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,2:SPELL_DMG,1.8:SPELL_HASTE_RATING,1:SPELL_CRIT_RATING,0.8:MASTERY_RATING,0.9:VERSATILITY_RATING,0.7:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PRIEST:3"] =                "LE_ITEM_CLASS_WEAPON,10,15,4,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.9:SPELL_HASTE_RATING,0.84:SPELL_CRIT_RATING,0.97:MASTERY_RATING,0.55:VERSATILITY_RATING,0.65:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["PRIEST:3:PVP"] =            "LE_ITEM_CLASS_WEAPON,10,15,4,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.9:SPELL_HASTE_RATING,0.84:SPELL_CRIT_RATING,0.97:MASTERY_RATING,0.55:VERSATILITY_RATING,0.65:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["WARLOCK:1"] =               "LE_ITEM_CLASS_WEAPON,6,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.87:MASTERY_RATING,0.85:SPELL_HASTE_RATING,0.86:SPELL_CRIT_RATING,0.64:VERSATILITY_RATING,0.59:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["WARLOCK:1:PVP"] =           "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.87:MASTERY_RATING,0.85:SPELL_HASTE_RATING,0.86:SPELL_CRIT_RATING,0.64:VERSATILITY_RATING,0.59:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["WARLOCK:2"] =               "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.87:MASTERY_RATING,0.85:SPELL_HASTE_RATING,0.78:SPELL_CRIT_RATING,0.7:VERSATILITY_RATING,0.62:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["WARLOCK:2:PVP"] =           "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.87:MASTERY_RATING,0.85:SPELL_HASTE_RATING,0.78:SPELL_CRIT_RATING,0.7:VERSATILITY_RATING,0.62:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["WARLOCK:3"] =               "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.93:SPELL_CRIT_RATING,0.87:SPELL_HASTE_RATING,0.68:MASTERY_RATING,0.9:VERSATILITY_RATING,0.61:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["WARLOCK:3:PVP"] =           "LE_ITEM_CLASS_WEAPON,7,10,14,15,19:LE_ITEM_CLASS_ARMOR,0,1:XP_BONUS,1000:INT,1:SPELL_DMG,0.93:SPELL_CRIT_RATING,0.87:SPELL_HASTE_RATING,0.68:MASTERY_RATING,0.9:VERSATILITY_RATING,0.61:DPS,0.01:STA,0:AGI,0:AP,0:ARMOR,0:AVOIDANCE_RATING,0:LEECH_RATING,0:STR,0",
	        ["DEMONHUNTER:1"] =           "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,15:LE_ITEM_CLASS_ARMOR,0,2:XP_BONUS,1000:DPS,0.75:DPS|MAIN,0.75:DPS|OFF,0.22:AGI,1:VERSATILITY_RATING,0.48:MASTERY_RATING,0.57:MELEE_HASTE_RATING,0.44:AP,0.91:MELEE_CRIT_RATING,0.52:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["DEMONHUNTER:1:PVP"] =       "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,15:LE_ITEM_CLASS_ARMOR,0,2:XP_BONUS,1000:DPS,0.75:DPS|MAIN,0.75:DPS|OFF,0.22:AGI,1:VERSATILITY_RATING,0.48:MASTERY_RATING,0.57:MELEE_HASTE_RATING,0.44:AP,0.91:MELEE_CRIT_RATING,0.52:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["DEMONHUNTER:2"] =           "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,7,9,13,15:LE_ITEM_CLASS_ARMOR,0,2:XP_BONUS,1000:DPS,0.78:DPS|MAIN,0.78:DPS|OFF,0.42:AGI,1:VERSATILITY_RATING,0.48:MELEE_HASTE_RATING,0.67:MASTERY_RATING,0.49:MELEE_CRIT_RATING,0.51:AP,0.91:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",
	        ["DEMONHUNTER:2:PVP"] =       "ARMOR_SPECIALIZATION_STAT,AGI:LE_ITEM_CLASS_WEAPON,0,7,9,13,15:LE_ITEM_CLASS_ARMOR,0,2:XP_BONUS,1000:DPS,0.78:DPS|MAIN,0.78:DPS|OFF,0.42:AGI,1:VERSATILITY_RATING,0.48:MELEE_HASTE_RATING,0.67:MASTERY_RATING,0.49:MELEE_CRIT_RATING,0.51:AP,0.91:STA,0:ARMOR,0:AVOIDANCE_RATING,0:INT,0:LEECH_RATING,0:SPELL_DMG,0:STR,0",			
			["tiebreaker"] = {{"ARMOR",1},{"DPS",1},{"STA",2}}
		}

		--source: simulationcraft (https://github.com/simulationcraft/simc/blob/master/COPYING)
		--  Combat rating values for level 1 - 110, wow build 22248
		local combat_ratings = {
		  -- Dodge
		  ["DODGE_RATING"] = {
		    0.796153166,	0.796153046,	0.796153082,	0.796153077,	0.796152972,	--    5
		    0.796153104,	0.796153098,	0.796153002,	0.796153001,	0.796153118,	--   10
		    1.194230577,	1.592308122,	1.990383791,	2.38846122,	2.786539133,	--   15
		    3.184616721,	3.582691914,	3.980769816,	4.378847535,	4.776922649,	--   20
		    5.175000204,	5.573077757,	5.971153416,	6.369230973,	6.767308527,	--   25
		    7.16538348,	7.563461768,	7.961538858,	8.359617396,	8.757692249,	--   30
		    9.155768223,	9.553846577,	9.951925635,	10.3500015,	10.7480775,	--   35
		    11.1461535,	11.544231,	11.942307,	12.3403845,	12.738462,	--   40
		    13.136538,	13.5346155,	13.932693,	14.330769,	14.7288465,	--   45
		    15.1269255,	15.525,	15.9230775,	16.321155,	16.719231,	--   50
		    17.11731,	17.515386,	17.913462,	18.3115395,	18.709617,	--   55
		    19.107693,	19.5057705,	19.903848,	20.301924,	20.7000015,	--   60
		    20.9244973,	21.1989946,	21.4734919,	21.7479892,	22.0224865,	--   65
		    22.29698381,	22.57148111,	22.84597841,	23.12047571,	23.39497301,	--   70
		    23.66947031,	23.94396761,	24.21846491,	24.49296221,	24.76745951,	--   75
		    25.04195682,	25.31645412,	25.59095142,	25.86544872,	26.13994602,	--   80
		    27.20688259,	28.27381916,	28.80728745,	28.80728745,	29.34075574,	--   85
		    30.40769231,	31.47462888,	32.54156545,	33.60850202,	34,	--   90
		    40,	47,	56,	65,	75,	--   95
		    89,	103,	121,	140,	162,	--  100
		    185,	210,	237.5,	267.5,	300,	--  105
		    335,	372.5,	415,	462.5,	515,	--  110
		  },
		  -- Parry
		  ["PARRY_RATING"] = {
		    0.796153166,	0.796153046,	0.796153082,	0.796153077,	0.796152972,	--    5
		    0.796153104,	0.796153098,	0.796153002,	0.796153001,	0.796153118,	--   10
		    1.194230577,	1.592308122,	1.990383791,	2.38846122,	2.786539133,	--   15
		    3.184616721,	3.582691914,	3.980769816,	4.378847535,	4.776922649,	--   20
		    5.175000204,	5.573077757,	5.971153416,	6.369230973,	6.767308527,	--   25
		    7.16538348,	7.563461768,	7.961538858,	8.359617396,	8.757692249,	--   30
		    9.155768223,	9.553846577,	9.951925635,	10.3500015,	10.7480775,	--   35
		    11.1461535,	11.544231,	11.942307,	12.3403845,	12.738462,	--   40
		    13.136538,	13.5346155,	13.932693,	14.330769,	14.7288465,	--   45
		    15.1269255,	15.525,	15.9230775,	16.321155,	16.719231,	--   50
		    17.11731,	17.515386,	17.913462,	18.3115395,	18.709617,	--   55
		    19.107693,	19.5057705,	19.903848,	20.301924,	20.7000015,	--   60
		    20.9244973,	21.1989946,	21.4734919,	21.7479892,	22.0224865,	--   65
		    22.29698381,	22.57148111,	22.84597841,	23.12047571,	23.39497301,	--   70
		    23.66947031,	23.94396761,	24.21846491,	24.49296221,	24.76745951,	--   75
		    25.04195682,	25.31645412,	25.59095142,	25.86544872,	26.13994602,	--   80
		    27.20688259,	28.27381916,	28.80728745,	28.80728745,	29.34075574,	--   85
		    30.40769231,	31.47462888,	32.54156545,	33.60850202,	34,	--   90
		    40,	47,	56,	65,	75,	--   95
		    89,	103,	121,	140,	162,	--  100
		    185,	210,	237.5,	267.5,	300,	--  105
		    335,	372.5,	415,	462.5,	515,	--  110
		  },
		  -- Block
		  ["BLOCK_RATING"] = {
		    0.26538435,	0.265384311,	0.265384325,	0.265384359,	0.265384324,	--    5
		    0.265384336,	0.265384366,	0.265384334,	0.265384363,	0.265384372,	--   10
		    0.398076859,	0.530769428,	0.663461198,	0.79615374,	0.928846465,	--   15
		    1.061538712,	1.194230638,	1.326923039,	1.459615845,	1.592307549,	--   20
		    1.724999927,	1.857692288,	1.990384472,	2.123076828,	2.255769169,	--   25
		    2.38846116,	2.521154105,	2.653846101,	2.786538758,	2.919230561,	--   30
		    3.051922741,	3.184615337,	3.317308545,	3.45,	3.582692,	--   35
		    3.715384,	3.848077,	3.980769,	4.113461,	4.246154,	--   40
		    4.378846,	4.511539,	4.644231,	4.776923,	4.909616,	--   45
		    5.042308,	5.175,	5.307693,	5.440384,	5.573077,	--   50
		    5.705769,	5.838462,	5.971154,	6.103846,	6.236538,	--   55
		    6.369231,	6.501923,	6.634615,	6.767308,	6.900001,	--   60
		    6.974832434,	7.066331534,	7.157830634,	7.249329735,	7.340828835,	--   65
		    7.432327935,	7.523827036,	7.615326136,	7.706825236,	7.798324336,	--   70
		    7.889823437,	7.981322537,	8.072821637,	8.164320738,	8.255819838,	--   75
		    8.347318938,	8.438818039,	8.530317139,	8.621816239,	8.71331534,	--   80
		    9.068960864,	9.424606388,	9.60242915,	9.60242915,	9.780251912,	--   85
		    10.13589744,	10.49154296,	10.84718848,	11.20283401,	11,	--   90
		    13,	16,	19,	22,	25,	--   95
		    30,	34,	40,	47,	54,	--  100
		    56.5,	60,	65,	72.5,	80,	--  105
		    90,	100,	112.5,	127.5,	145,	--  110
		  },
		  -- Hit - Melee
		  ["MELEE_HIT_RATING"] = {
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--    5
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--   10
		    0.461538,	0.615385,	0.769231,	0.923077,	1.076923,	--   15
		    1.230769,	1.384615,	1.538462,	1.692308,	1.846154,	--   20
		    2,	2.153846,	2.307692,	2.461539,	2.615385,	--   25
		    2.769231,	2.923077,	3.076923,	3.230769,	3.384615,	--   30
		    3.538462,	3.692308,	3.846154,	4,	4.153846,	--   35
		    4.307692,	4.461538,	4.615385,	4.769231,	4.923077,	--   40
		    5.076923,	5.230769,	5.384615,	5.538462,	5.692307,	--   45
		    5.846154,	6,	6.153846,	6.307693,	6.461538,	--   50
		    6.615385,	6.769231,	6.923077,	7.076923,	7.230769,	--   55
		    7.384615,	7.538462,	7.692307,	7.846154,	8,	--   60
		    8.038789924,	8.144246514,	8.249703104,	8.355159694,	8.460616284,	--   65
		    8.566072874,	8.671529465,	8.776986055,	8.882442645,	8.987899235,	--   70
		    9.093355825,	9.198812416,	9.304269006,	9.409725596,	9.515182186,	--   75
		    9.620638776,	9.726095367,	9.831551957,	9.937008547,	10.04246514,	--   80
		    10.45236167,	10.86225821,	11.06720648,	11.06720648,	11.27215475,	--   85
		    11.68205128,	12.09194782,	12.50184435,	12.91174089,	13,	--   90
		    15,	18,	22,	25,	29,	--   95
		    34,	40,	46,	54,	62,	--  100
		    68,	73,	79,	86,	93,	--  105
		    101,	110,	119,	130,	141,	--  110
		  },
		  -- Hit - Ranged
		  ["RANGED_HIT_RATING"] = {
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--    5
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--   10
		    0.461538,	0.615385,	0.769231,	0.923077,	1.076923,	--   15
		    1.230769,	1.384615,	1.538462,	1.692308,	1.846154,	--   20
		    2,	2.153846,	2.307692,	2.461539,	2.615385,	--   25
		    2.769231,	2.923077,	3.076923,	3.230769,	3.384615,	--   30
		    3.538462,	3.692308,	3.846154,	4,	4.153846,	--   35
		    4.307692,	4.461538,	4.615385,	4.769231,	4.923077,	--   40
		    5.076923,	5.230769,	5.384615,	5.538462,	5.692307,	--   45
		    5.846154,	6,	6.153846,	6.307693,	6.461538,	--   50
		    6.615385,	6.769231,	6.923077,	7.076923,	7.230769,	--   55
		    7.384615,	7.538462,	7.692307,	7.846154,	8,	--   60
		    8.038789924,	8.144246514,	8.249703104,	8.355159694,	8.460616284,	--   65
		    8.566072874,	8.671529465,	8.776986055,	8.882442645,	8.987899235,	--   70
		    9.093355825,	9.198812416,	9.304269006,	9.409725596,	9.515182186,	--   75
		    9.620638776,	9.726095367,	9.831551957,	9.937008547,	10.04246514,	--   80
		    10.45236167,	10.86225821,	11.06720648,	11.06720648,	11.27215475,	--   85
		    11.68205128,	12.09194782,	12.50184435,	12.91174089,	13,	--   90
		    15,	18,	22,	25,	29,	--   95
		    34,	40,	46,	54,	62,	--  100
		    68,	73,	79,	86,	93,	--  105
		    101,	110,	119,	130,	141,	--  110
		  },
		  -- Hit - Spell
		  ["SPELL_HIT_RATING"] = {
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--    5
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--   10
		    0.461538,	0.615385,	0.769231,	0.923077,	1.076923,	--   15
		    1.230769,	1.384615,	1.538462,	1.692308,	1.846154,	--   20
		    2,	2.153846,	2.307692,	2.461539,	2.615385,	--   25
		    2.769231,	2.923077,	3.076923,	3.230769,	3.384615,	--   30
		    3.538462,	3.692308,	3.846154,	4,	4.153846,	--   35
		    4.307692,	4.461538,	4.615385,	4.769231,	4.923077,	--   40
		    5.076923,	5.230769,	5.384615,	5.538462,	5.692307,	--   45
		    5.846154,	6,	6.153846,	6.307693,	6.461538,	--   50
		    6.615385,	6.769231,	6.923077,	7.076923,	7.230769,	--   55
		    7.384615,	7.538462,	7.692307,	7.846154,	8,	--   60
		    8.038789924,	8.144246514,	8.249703104,	8.355159694,	8.460616284,	--   65
		    8.566072874,	8.671529465,	8.776986055,	8.882442645,	8.987899235,	--   70
		    9.093355825,	9.198812416,	9.304269006,	9.409725596,	9.515182186,	--   75
		    9.620638776,	9.726095367,	9.831551957,	9.937008547,	10.04246514,	--   80
		    10.45236167,	10.86225821,	11.06720648,	11.06720648,	11.27215475,	--   85
		    11.68205128,	12.09194782,	12.50184435,	12.91174089,	13,	--   90
		    15,	18,	22,	25,	29,	--   95
		    34,	40,	46,	54,	62,	--  100
		    68,	73,	79,	86,	93,	--  105
		    101,	110,	119,	130,	141,	--  110
		  },
		  -- Crit - Melee
		  ["MELEE_CRIT_RATING"] = {
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--    5
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--   10
		    0.807692,	1.076923,	1.346154,	1.615385,	1.884615,	--   15
		    2.153846,	2.423077,	2.692308,	2.961538,	3.230769,	--   20
		    3.5,	3.769231,	4.038462,	4.307692,	4.576923,	--   25
		    4.846154,	5.115385,	5.384615,	5.653846,	5.923077,	--   30
		    6.192307,	6.461538,	6.730769,	7,	7.269231,	--   35
		    7.538462,	7.807693,	8.076923,	8.346154,	8.615384,	--   40
		    8.884615,	9.153846,	9.423077,	9.692307,	9.961538,	--   45
		    10.23077,	10.5,	10.769232,	11.038462,	11.307693,	--   50
		    11.576923,	11.846155,	12.115385,	12.384617,	12.653847,	--   55
		    12.923079,	13.192308,	13.461539,	13.73077,	14,	--   60
		    14.18609987,	14.37219973,	14.5582996,	14.74439946,	14.93049933,	--   65
		    15.11659919,	15.30269906,	15.48879892,	15.67489879,	15.86099865,	--   70
		    16.04709852,	16.23319838,	16.41929825,	16.60539811,	16.79149798,	--   75
		    16.97759784,	17.16369771,	17.34979757,	17.53589744,	17.7219973,	--   80
		    18.44534413,	19.16869096,	19.53036437,	19.53036437,	19.89203779,	--   85
		    20.61538462,	21.33873144,	22.06207827,	22.7854251,	23,	--   90
		    27,	32,	38,	44,	51,	--   95
		    60,	70,	82,	95,	110,	--  100
		    122.5,	137.5,	155,	175,	197.5,	--  105
		    222.5,	250,	280,	312.5,	350,	--  110
		  },
		  -- Crit - Ranged
		  ["RANGED_CRIT_RATING"] = {
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--    5
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--   10
		    0.807692,	1.076923,	1.346154,	1.615385,	1.884615,	--   15
		    2.153846,	2.423077,	2.692308,	2.961538,	3.230769,	--   20
		    3.5,	3.769231,	4.038462,	4.307692,	4.576923,	--   25
		    4.846154,	5.115385,	5.384615,	5.653846,	5.923077,	--   30
		    6.192307,	6.461538,	6.730769,	7,	7.269231,	--   35
		    7.538462,	7.807693,	8.076923,	8.346154,	8.615384,	--   40
		    8.884615,	9.153846,	9.423077,	9.692307,	9.961538,	--   45
		    10.23077,	10.5,	10.769232,	11.038462,	11.307693,	--   50
		    11.576923,	11.846155,	12.115385,	12.384617,	12.653847,	--   55
		    12.923079,	13.192308,	13.461539,	13.73077,	14,	--   60
		    14.18609987,	14.37219973,	14.5582996,	14.74439946,	14.93049933,	--   65
		    15.11659919,	15.30269906,	15.48879892,	15.67489879,	15.86099865,	--   70
		    16.04709852,	16.23319838,	16.41929825,	16.60539811,	16.79149798,	--   75
		    16.97759784,	17.16369771,	17.34979757,	17.53589744,	17.7219973,	--   80
		    18.44534413,	19.16869096,	19.53036437,	19.53036437,	19.89203779,	--   85
		    20.61538462,	21.33873144,	22.06207827,	22.7854251,	23,	--   90
		    27,	32,	38,	44,	51,	--   95
		    60,	70,	82,	95,	110,	--  100
		    122.5,	137.5,	155,	175,	197.5,	--  105
		    222.5,	250,	280,	312.5,	350,	--  110
		  },
		  -- Crit - Spell
		  ["SPELL_CRIT_RATING"] = {
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--    5
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--   10
		    0.807692,	1.076923,	1.346154,	1.615385,	1.884615,	--   15
		    2.153846,	2.423077,	2.692308,	2.961538,	3.230769,	--   20
		    3.5,	3.769231,	4.038462,	4.307692,	4.576923,	--   25
		    4.846154,	5.115385,	5.384615,	5.653846,	5.923077,	--   30
		    6.192307,	6.461538,	6.730769,	7,	7.269231,	--   35
		    7.538462,	7.807693,	8.076923,	8.346154,	8.615384,	--   40
		    8.884615,	9.153846,	9.423077,	9.692307,	9.961538,	--   45
		    10.23077,	10.5,	10.769232,	11.038462,	11.307693,	--   50
		    11.576923,	11.846155,	12.115385,	12.384617,	12.653847,	--   55
		    12.923079,	13.192308,	13.461539,	13.73077,	14,	--   60
		    14.18609987,	14.37219973,	14.5582996,	14.74439946,	14.93049933,	--   65
		    15.11659919,	15.30269906,	15.48879892,	15.67489879,	15.86099865,	--   70
		    16.04709852,	16.23319838,	16.41929825,	16.60539811,	16.79149798,	--   75
		    16.97759784,	17.16369771,	17.34979757,	17.53589744,	17.7219973,	--   80
		    18.44534413,	19.16869096,	19.53036437,	19.53036437,	19.89203779,	--   85
		    20.61538462,	21.33873144,	22.06207827,	22.7854251,	23,	--   90
		    27,	32,	38,	44,	51,	--   95
		    60,	70,	82,	95,	110,	--  100
		    122.5,	137.5,	155,	175,	197.5,	--  105
		    222.5,	250,	280,	312.5,	350,	--  110
		  },
		  -- Resilience - Player Damage
		  ["RESILIENCE_RATING"] = {
		    0.357366573,	0.357366536,	0.357366616,	0.357366581,	0.357366548,	--    5
		    0.357366619,	0.357366588,	0.357366557,	0.357366529,	0.357366592,	--   10
		    0.536050313,	0.714734078,	0.893416837,	1.072100745,	1.250784588,	--   15
		    1.429468391,	1.608151272,	1.786834785,	1.965518814,	2.144201357,	--   20
		    2.322885157,	2.50156877,	2.680251931,	2.858935326,	3.037619351,	--   25
		    3.216301781,	3.394985689,	3.573669604,	3.752353462,	3.931036088,	--   30
		    4.109719625,	4.288403497,	4.467087809,	4.645770086,	4.824453351,	--   35
		    5.003137264,	5.18182053,	5.360503796,	5.539187708,	5.717870974,	--   40
		    5.896554239,	6.075237505,	6.253921417,	6.432604683,	6.611288597,	--   45
		    6.789971862,	6.968655775,	7.14733904,	7.326022953,	7.504706865,	--   50
		    7.683389484,	7.862073398,	8.040756017,	8.219439932,	8.398123197,	--   55
		    8.576807107,	8.755489729,	8.934174284,	9.112856905,	9.291540171,	--   60
		    7.32948493,	7.425636527,	7.521788124,	7.617939721,	7.714091318,	--   65
		    7.810242915,	7.906394512,	8.002546109,	8.098697706,	8.194849303,	--   70
		    8.2910009,	8.387152497,	8.483304094,	8.579455691,	8.675607287,	--   75
		    8.771758884,	8.867910481,	8.964062078,	9.060213675,	9.156365272,	--   80
		    9.530094467,	9.903823662,	10.09068826,	10.09068826,	10.27755286,	--   85
		    10.65128205,	11.02501125,	11.39874044,	11.77246964,	12,	--   90
		    14,	17,	20,	23,	26,	--   95
		    31.00000001,	36,	42,	49,	57,	--  100
		    62,	67,	73,	79,	86,	--  105
		    93,	101,	110,	119,	130,	--  110
		  },
		  -- Lifesteal
		  ["LEECH_RATING"] = {
		    0.342657636,	0.342657636,	0.342657636,	0.342657636,	0.342657636,	--    5
		    0.342657636,	0.342657636,	0.342657636,	0.342657636,	0.342657636,	--   10
		    0.513985818,	0.685314636,	0.856643455,	1.027972273,	1.199300455,	--   15
		    1.370629273,	1.541958091,	1.713286909,	1.884615091,	2.055943909,	--   20
		    2.227272727,	2.398601545,	2.569930364,	2.741258545,	2.912587364,	--   25
		    3.083916182,	3.255245,	3.426573182,	3.597902,	3.769230818,	--   30
		    3.940559,	4.111887818,	4.283216636,	4.454545455,	4.625874273,	--   35
		    4.797203091,	4.968531909,	5.139860091,	5.311188909,	5.482517091,	--   40
		    5.653845909,	5.825174727,	5.996503545,	6.167831727,	6.339160545,	--   45
		    6.51049,	6.681818182,	6.853147636,	7.024475818,	7.195804636,	--   50
		    7.367132818,	7.538462273,	7.709790455,	7.881119909,	8.052448091,	--   55
		    8.223777545,	8.395105091,	8.566433909,	8.737762727,	8.909090909,	--   60
		    9.0275181,	9.145945284,	9.264372473,	9.382799656,	9.501226848,	--   65
		    9.619654031,	9.73808122,	9.856508404,	9.974935595,	10.09336278,	--   70
		    10.21178997,	10.33021715,	10.44864434,	10.56707153,	10.68549871,	--   75
		    10.8039259,	10.92235309,	11.04078027,	11.15920746,	11.27763465,	--   80
		    11.73794627,	12.19825788,	12.42841369,	12.42841369,	12.6585695,	--   85
		    13.11888112,	13.57919273,	14.03950435,	14.49981597,	14.50909091,	--   90
		    17.30909091,	20.36363636,	24.18181818,	28,	32.58181818,	--   95
		    38.18181818,	44.54545455,	52.18181818,	60.58181818,	70,	--  100
		    80,	91,	104,	118,	134,	--  105
		    150,	168,	187,	207,	230,	--  110
		  },
		  -- Haste - Melee
		  ["MELEE_HASTE_RATING"] = {
		    0.48076875,	0.48076875,	0.48076875,	0.48076875,	0.48076875,	--    5
		    0.48076875,	0.48076875,	0.48076875,	0.48076875,	0.48076875,	--   10
		    0.72115375,	0.96153875,	1.2019225,	1.4423075,	1.6826925,	--   15
		    1.9230775,	2.16346125,	2.40384625,	2.64423125,	2.884615,	--   20
		    3.125,	3.365385,	3.60576875,	3.84615375,	4.08653875,	--   25
		    4.3269225,	4.5673075,	4.8076925,	5.0480775,	5.28846125,	--   30
		    5.52884625,	5.76923125,	6.00961625,	6.25,	6.490385,	--   35
		    6.73076875,	6.97115375,	7.21153875,	7.4519225,	7.6923075,	--   40
		    7.9326925,	8.1730775,	8.41346125,	8.65384625,	8.89423125,	--   45
		    9.13461625,	9.375,	9.61538375,	9.85576875,	10.09615375,	--   50
		    10.33653875,	10.5769225,	10.8173075,	11.0576925,	11.2980775,	--   55
		    11.53846125,	11.77884625,	12.01923125,	12.25961625,	12.5,	--   60
		    12.56060925,	12.72538518,	12.8901611,	13.05493703,	13.21971295,	--   65
		    13.38448886,	13.54926479,	13.71404071,	13.87881664,	14.04359255,	--   70
		    14.20836848,	14.3731444,	14.53792033,	14.70269625,	14.86747216,	--   75
		    15.03224809,	15.19702401,	15.36179994,	15.52657585,	15.69135178,	--   80
		    16.33181511,	16.97227845,	17.29251013,	17.29251013,	17.61274179,	--   85
		    18.25320513,	18.89366846,	19.5341318,	20.17459514,	20,	--   90
		    25,	29,	35,	40,	46,	--   95
		    55,	64,	75,	86,	100,	--  100
		    110,	122.5,	137.5,	155,	177.5,	--  105
		    202.5,	230,	260,	292.5,	325,	--  110
		  },
		  -- Haste - Ranged
		  ["RANGED_HASTE_RATING"] = {
		    0.48076875,	0.48076875,	0.48076875,	0.48076875,	0.48076875,	--    5
		    0.48076875,	0.48076875,	0.48076875,	0.48076875,	0.48076875,	--   10
		    0.72115375,	0.96153875,	1.2019225,	1.4423075,	1.6826925,	--   15
		    1.9230775,	2.16346125,	2.40384625,	2.64423125,	2.884615,	--   20
		    3.125,	3.365385,	3.60576875,	3.84615375,	4.08653875,	--   25
		    4.3269225,	4.5673075,	4.8076925,	5.0480775,	5.28846125,	--   30
		    5.52884625,	5.76923125,	6.00961625,	6.25,	6.490385,	--   35
		    6.73076875,	6.97115375,	7.21153875,	7.4519225,	7.6923075,	--   40
		    7.9326925,	8.1730775,	8.41346125,	8.65384625,	8.89423125,	--   45
		    9.13461625,	9.375,	9.61538375,	9.85576875,	10.09615375,	--   50
		    10.33653875,	10.5769225,	10.8173075,	11.0576925,	11.2980775,	--   55
		    11.53846125,	11.77884625,	12.01923125,	12.25961625,	12.5,	--   60
		    12.56060925,	12.72538518,	12.8901611,	13.05493703,	13.21971295,	--   65
		    13.38448886,	13.54926479,	13.71404071,	13.87881664,	14.04359255,	--   70
		    14.20836848,	14.3731444,	14.53792033,	14.70269625,	14.86747216,	--   75
		    15.03224809,	15.19702401,	15.36179994,	15.52657585,	15.69135178,	--   80
		    16.33181511,	16.97227845,	17.29251013,	17.29251013,	17.61274179,	--   85
		    18.25320513,	18.89366846,	19.5341318,	20.17459514,	20,	--   90
		    25,	29,	35,	40,	46,	--   95
		    55,	64,	75,	86,	100,	--  100
		    110,	122.5,	137.5,	155,	177.5,	--  105
		    202.5,	230,	260,	292.5,	325,	--  110
		  },
		  -- Haste - Spell
		  ["SPELL_HASTE_RATING"] = {
		    0.48076875,	0.48076875,	0.48076875,	0.48076875,	0.48076875,	--    5
		    0.48076875,	0.48076875,	0.48076875,	0.48076875,	0.48076875,	--   10
		    0.72115375,	0.96153875,	1.2019225,	1.4423075,	1.6826925,	--   15
		    1.9230775,	2.16346125,	2.40384625,	2.64423125,	2.884615,	--   20
		    3.125,	3.365385,	3.60576875,	3.84615375,	4.08653875,	--   25
		    4.3269225,	4.5673075,	4.8076925,	5.0480775,	5.28846125,	--   30
		    5.52884625,	5.76923125,	6.00961625,	6.25,	6.490385,	--   35
		    6.73076875,	6.97115375,	7.21153875,	7.4519225,	7.6923075,	--   40
		    7.9326925,	8.1730775,	8.41346125,	8.65384625,	8.89423125,	--   45
		    9.13461625,	9.375,	9.61538375,	9.85576875,	10.09615375,	--   50
		    10.33653875,	10.5769225,	10.8173075,	11.0576925,	11.2980775,	--   55
		    11.53846125,	11.77884625,	12.01923125,	12.25961625,	12.5,	--   60
		    12.56060925,	12.72538518,	12.8901611,	13.05493703,	13.21971295,	--   65
		    13.38448886,	13.54926479,	13.71404071,	13.87881664,	14.04359255,	--   70
		    14.20836848,	14.3731444,	14.53792033,	14.70269625,	14.86747216,	--   75
		    15.03224809,	15.19702401,	15.36179994,	15.52657585,	15.69135178,	--   80
		    16.33181511,	16.97227845,	17.29251013,	17.29251013,	17.61274179,	--   85
		    18.25320513,	18.89366846,	19.5341318,	20.17459514,	20,	--   90
		    25,	29,	35,	40,	46,	--   95
		    55,	64,	75,	86,	100,	--  100
		    110,	122.5,	137.5,	155,	177.5,	--  105
		    202.5,	230,	260,	292.5,	325,	--  110
		  },
		  -- Expertise
		  ["EXPERTISE_RATING"] = {
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--    5
		    0.307692,	0.307692,	0.307692,	0.307692,	0.307692,	--   10
		    0.461538,	0.615385,	0.769231,	0.923077,	1.076923,	--   15
		    1.230769,	1.384615,	1.538462,	1.692308,	1.846154,	--   20
		    2,	2.153846,	2.307692,	2.461539,	2.615385,	--   25
		    2.769231,	2.923077,	3.076923,	3.230769,	3.384615,	--   30
		    3.538462,	3.692308,	3.846154,	4,	4.153846,	--   35
		    4.307692,	4.461538,	4.615385,	4.769231,	4.923077,	--   40
		    5.076923,	5.230769,	5.384615,	5.538462,	5.692307,	--   45
		    5.846154,	6,	6.153846,	6.307693,	6.461538,	--   50
		    6.615385,	6.769231,	6.923077,	7.076923,	7.230769,	--   55
		    7.384615,	7.538462,	7.692307,	7.846154,	8,	--   60
		    8.038789924,	8.144246514,	8.249703104,	8.355159694,	8.460616284,	--   65
		    8.566072874,	8.671529465,	8.776986055,	8.882442645,	8.987899235,	--   70
		    9.093355825,	9.198812416,	9.304269006,	9.409725596,	9.515182186,	--   75
		    9.620638776,	9.726095367,	9.831551957,	9.937008547,	10.04246514,	--   80
		    10.45236167,	10.86225821,	11.06720648,	11.06720648,	11.27215475,	--   85
		    11.68205128,	12.09194782,	12.50184435,	12.91174089,	13,	--   90
		    15,	18,	22,	25,	29,	--   95
		    34,	40,	46,	54,	62,	--  100
		    68,	73,	79,	86,	93,	--  105
		    101,	110,	119,	130,	141,	--  110
		  },
		  -- Mastery
		  ["MASTERY_RATING"] = {
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--    5
		    0.538462,	0.538462,	0.538462,	0.538462,	0.538462,	--   10
		    0.807692,	1.076923,	1.346154,	1.615385,	1.884615,	--   15
		    2.153846,	2.423077,	2.692308,	2.961538,	3.230769,	--   20
		    3.5,	3.769231,	4.038462,	4.307692,	4.576923,	--   25
		    4.846154,	5.115385,	5.384615,	5.653846,	5.923077,	--   30
		    6.192307,	6.461538,	6.730769,	7,	7.269231,	--   35
		    7.538462,	7.807693,	8.076923,	8.346154,	8.615384,	--   40
		    8.884615,	9.153846,	9.423077,	9.692307,	9.961538,	--   45
		    10.23077,	10.5,	10.769232,	11.038462,	11.307693,	--   50
		    11.576923,	11.846155,	12.115385,	12.384617,	12.653847,	--   55
		    12.923079,	13.192308,	13.461539,	13.73077,	14,	--   60
		    14.18609987,	14.37219973,	14.5582996,	14.74439946,	14.93049933,	--   65
		    15.11659919,	15.30269906,	15.48879892,	15.67489879,	15.86099865,	--   70
		    16.04709852,	16.23319838,	16.41929825,	16.60539811,	16.79149798,	--   75
		    16.97759784,	17.16369771,	17.34979757,	17.53589744,	17.7219973,	--   80
		    18.44534413,	19.16869096,	19.53036437,	19.53036437,	19.89203779,	--   85
		    20.61538462,	21.33873144,	22.06207827,	22.7854251,	23,	--   90
		    27,	32,	38,	44,	51,	--   95
		    60,	70,	82,	95,	110,	--  100
		    122.5,	137.5,	155,	175,	197.5,	--  105
		    222.5,	250,	280,	312.5,	350,	--  110
		  },
		  -- PvP Power
		  ["PVP_POWER"] = {
		    0.237820717,	0.237820717,	0.237820717,	0.237820717,	0.237820717,	--    5
		    0.237820717,	0.237820717,	0.237820717,	0.237820717,	0.237820717,	--   10
		    0.356730633,	0.475640992,	0.59455135,	0.713461708,	0.832371625,	--   15
		    0.951281983,	1.070192342,	1.1891027,	1.308012617,	1.426922975,	--   20
		    1.545833333,	1.664743692,	1.78365405,	1.902563967,	2.021474325,	--   25
		    2.140384683,	2.259295042,	2.378204958,	2.497115317,	2.616025675,	--   30
		    2.734935592,	2.85384595,	2.972756308,	3.091666667,	3.210577025,	--   35
		    3.329487383,	3.448397742,	3.567307658,	3.686218017,	3.805127933,	--   40
		    3.924038292,	4.04294865,	4.161859008,	4.280768925,	4.399679283,	--   45
		    4.518590083,	4.6375,	4.7564108,	4.875320717,	4.994231075,	--   50
		    5.113140992,	5.232051792,	5.350961708,	5.469872508,	5.588782425,	--   55
		    5.707693225,	5.8266027,	5.945513058,	6.064423417,	6.183333333,	--   60
		    6.26552744,	6.347721547,	6.429915655,	6.512109762,	6.594303869,	--   65
		    6.676497976,	6.758692083,	6.84088619,	6.923080297,	7.005274404,	--   70
		    7.087468511,	7.169662618,	7.251856725,	7.334050832,	7.416244939,	--   75
		    7.498439046,	7.580633153,	7.66282726,	7.745021368,	7.827215475,	--   80
		    8.146693657,	8.46617184,	8.625910931,	8.625910931,	8.785650022,	--   85
		    9.105128205,	9.424606388,	9.74408457,	10.06356275,	10,	--   90
		    12,	14,	17,	19,	23,	--   95
		    27,	31,	36,	42,	49,	--  100
		    53,	58,	62,	68,	74,	--  105
		    80,	87,	94,	102,	111,	--  110
		  },
		  -- Versatility - Damage Done
		  ["VERSATILITY_RATING_DAMAGE_DONE"] = {
		    0.636364182,	0.636364182,	0.636364182,	0.636364182,	0.636364182,	--    5
		    0.636364182,	0.636364182,	0.636364182,	0.636364182,	0.636364182,	--   10
		    0.954545091,	1.272727182,	1.590909273,	1.909091364,	2.227272273,	--   15
		    2.545454364,	2.863636455,	3.181818545,	3.499999455,	3.818181545,	--   20
		    4.136363636,	4.454545727,	4.772727818,	5.090908727,	5.409090818,	--   25
		    5.727272909,	6.045455,	6.363635909,	6.681818,	7.000000091,	--   30
		    7.318181,	7.636363091,	7.954545182,	8.272727273,	8.590909364,	--   35
		    8.909091455,	9.227273545,	9.545454455,	9.863636545,	10.18181745,	--   40
		    10.49999955,	10.81818164,	11.13636373,	11.45454464,	11.77272673,	--   45
		    12.09091,	12.40909091,	12.72727418,	13.04545509,	13.36363718,	--   50
		    13.68181809,	14.00000136,	14.31818227,	14.63636555,	14.95454645,	--   55
		    15.27272973,	15.59090945,	15.90909155,	16.22727364,	16.54545455,	--   60
		    16.76539076,	16.98532695,	17.20526316,	17.42519936,	17.64513557,	--   65
		    17.86507177,	18.08500798,	18.30494418,	18.52488039,	18.74481659,	--   70
		    18.9647528,	19.18468899,	19.4046252,	19.6245614,	19.84449761,	--   75
		    20.06443381,	20.28437002,	20.50430622,	20.72424243,	20.94417863,	--   80
		    21.79904306,	22.6539075,	23.08133971,	23.08133971,	23.50877193,	--   85
		    24.36363637,	25.21850079,	26.07336523,	26.92822966,	27,	--   90
		    32,	38,	45,	52,	60,	--   95
		    71,	83,	97,	112,	130,	--  100
		    145,	162.5,	182.5,	205,	230,	--  105
		    257.5,	287.5,	322.5,	360,	400,	--  110
		  },
		  -- Versatility - Healing Done
		  ["VERSATILITY_RATING_HEALING"] = {
		    0.636364182,	0.636364182,	0.636364182,	0.636364182,	0.636364182,	--    5
		    0.636364182,	0.636364182,	0.636364182,	0.636364182,	0.636364182,	--   10
		    0.954545091,	1.272727182,	1.590909273,	1.909091364,	2.227272273,	--   15
		    2.545454364,	2.863636455,	3.181818545,	3.499999455,	3.818181545,	--   20
		    4.136363636,	4.454545727,	4.772727818,	5.090908727,	5.409090818,	--   25
		    5.727272909,	6.045455,	6.363635909,	6.681818,	7.000000091,	--   30
		    7.318181,	7.636363091,	7.954545182,	8.272727273,	8.590909364,	--   35
		    8.909091455,	9.227273545,	9.545454455,	9.863636545,	10.18181745,	--   40
		    10.49999955,	10.81818164,	11.13636373,	11.45454464,	11.77272673,	--   45
		    12.09091,	12.40909091,	12.72727418,	13.04545509,	13.36363718,	--   50
		    13.68181809,	14.00000136,	14.31818227,	14.63636555,	14.95454645,	--   55
		    15.27272973,	15.59090945,	15.90909155,	16.22727364,	16.54545455,	--   60
		    16.76539076,	16.98532695,	17.20526316,	17.42519936,	17.64513557,	--   65
		    17.86507177,	18.08500798,	18.30494418,	18.52488039,	18.74481659,	--   70
		    18.9647528,	19.18468899,	19.4046252,	19.6245614,	19.84449761,	--   75
		    20.06443381,	20.28437002,	20.50430622,	20.72424243,	20.94417863,	--   80
		    21.79904306,	22.6539075,	23.08133971,	23.08133971,	23.50877193,	--   85
		    24.36363637,	25.21850079,	26.07336523,	26.92822966,	27,	--   90
		    32,	38,	45,	52,	60,	--   95
		    71,	83,	97,	112,	130,	--  100
		    145,	162.5,	182.5,	205,	230,	--  105
		    257.5,	287.5,	322.5,	360,	400,	--  110
		  },
		  -- Versatility - Damage Taken
		  ["VERSATILITY_RATING_DAMAGE_TAKEN"] = {
		    1.272728364,	1.272728364,	1.272728364,	1.272728364,	1.272728364,	--    5
		    1.272728364,	1.272728364,	1.272728364,	1.272728364,	1.272728364,	--   10
		    1.909090182,	2.545454364,	3.181818545,	3.818182727,	4.454544545,	--   15
		    5.090908727,	5.727272909,	6.363637091,	6.999998909,	7.636363091,	--   20
		    8.272727273,	8.909091455,	9.545455636,	10.18181745,	10.81818164,	--   25
		    11.45454582,	12.09091,	12.72727182,	13.363636,	14.00000018,	--   30
		    14.636362,	15.27272618,	15.90909036,	16.54545455,	17.18181873,	--   35
		    17.81818291,	18.45454709,	19.09090891,	19.72727309,	20.36363491,	--   40
		    20.99999909,	21.63636327,	22.27272745,	22.90908927,	23.54545345,	--   45
		    24.18182,	24.81818182,	25.45454836,	26.09091018,	26.72727436,	--   50
		    27.36363618,	28.00000273,	28.63636455,	29.27273109,	29.90909291,	--   55
		    30.54545945,	31.18181891,	31.81818309,	32.45454727,	33.09090909,	--   60
		    33.53078151,	33.97065391,	34.41052633,	34.85039872,	35.29027114,	--   65
		    35.73014354,	36.17001596,	36.60988836,	37.04976078,	37.48963317,	--   70
		    37.92950559,	38.36937799,	38.80925041,	39.24912281,	39.68899523,	--   75
		    40.12886762,	40.56874004,	41.00861244,	41.44848486,	41.88835725,	--   80
		    43.59808613,	45.307815,	46.16267942,	46.16267942,	47.01754387,	--   85
		    48.72727274,	50.43700159,	52.14673046,	53.85645933,	54,	--   90
		    64,	76,	90,	104,	120,	--   95
		    142,	166,	194,	224,	260,	--  100
		    290,	325,	365,	410,	460,	--  105
		    515,	575,	645,	720,	800,	--  110
		  },
		  -- Speed
		  --[[{
		    0.122377727,	0.122377727,	0.122377727,	0.122377727,	0.122377727,	--    5
		    0.122377727,	0.122377727,	0.122377727,	0.122377727,	0.122377727,	--   10
		    0.183566364,	0.244755227,	0.305944091,	0.367132955,	0.428321591,	--   15
		    0.489510455,	0.550699318,	0.611888182,	0.673076818,	0.734265682,	--   20
		    0.795454545,	0.856643409,	0.917832273,	0.979020909,	1.040209773,	--   25
		    1.101398636,	1.1625875,	1.223776136,	1.284965,	1.346153864,	--   30
		    1.4073425,	1.468531364,	1.529720227,	1.590909091,	1.652097955,	--   35
		    1.713286818,	1.774475682,	1.835664318,	1.896853182,	1.958041818,	--   40
		    2.019230682,	2.080419545,	2.141608409,	2.202797045,	2.263985909,	--   45
		    2.325175,	2.386363636,	2.447552727,	2.508741364,	2.569930227,	--   50
		    2.631118864,	2.692307955,	2.753496591,	2.814685682,	2.875874318,	--   55
		    2.937063409,	2.998251818,	3.059440682,	3.120629545,	3.181818182,	--   60
		    3.224113607,	3.26640903,	3.308704455,	3.350999877,	3.393295302,	--   65
		    3.435590725,	3.47788615,	3.520181573,	3.562476998,	3.60477242,	--   70
		    3.647067845,	3.689363268,	3.731658693,	3.773954116,	3.816249541,	--   75
		    3.858544964,	3.900840389,	3.943135811,	3.985431236,	4.027726659,	--   80
		    4.192123666,	4.356520673,	4.438719175,	4.438719175,	4.52091768,	--   85
		    4.685314686,	4.849711691,	5.014108698,	5.178505705,	5.227272727,	--   90
		    6.136363636,	7.272727273,	8.636363636,	10,	11.59090909,	--   95
		    13.63636364,	15.90909091,	18.63636364,	21.59090909,	25,	--  100
		    28,	31,	35,	40,	45,	--  105
		    50.5,	57,	64,	71,	80,	--  110
		  },]]
		  -- Avoidance
		  ["AVOIDANCE_RATING"] = {
		    0.122377727,	0.122377727,	0.122377727,	0.122377727,	0.122377727,	--    5
		    0.122377727,	0.122377727,	0.122377727,	0.122377727,	0.122377727,	--   10
		    0.183566364,	0.244755227,	0.305944091,	0.367132955,	0.428321591,	--   15
		    0.489510455,	0.550699318,	0.611888182,	0.673076818,	0.734265682,	--   20
		    0.795454545,	0.856643409,	0.917832273,	0.979020909,	1.040209773,	--   25
		    1.101398636,	1.1625875,	1.223776136,	1.284965,	1.346153864,	--   30
		    1.4073425,	1.468531364,	1.529720227,	1.590909091,	1.652097955,	--   35
		    1.713286818,	1.774475682,	1.835664318,	1.896853182,	1.958041818,	--   40
		    2.019230682,	2.080419545,	2.141608409,	2.202797045,	2.263985909,	--   45
		    2.325175,	2.386363636,	2.447552727,	2.508741364,	2.569930227,	--   50
		    2.631118864,	2.692307955,	2.753496591,	2.814685682,	2.875874318,	--   55
		    2.937063409,	2.998251818,	3.059440682,	3.120629545,	3.181818182,	--   60
		    3.224113607,	3.26640903,	3.308704455,	3.350999877,	3.393295302,	--   65
		    3.435590725,	3.47788615,	3.520181573,	3.562476998,	3.60477242,	--   70
		    3.647067845,	3.689363268,	3.731658693,	3.773954116,	3.816249541,	--   75
		    3.858544964,	3.900840389,	3.943135811,	3.985431236,	4.027726659,	--   80
		    4.192123666,	4.356520673,	4.438719175,	4.438719175,	4.52091768,	--   85
		    4.685314686,	4.849711691,	5.014108698,	5.178505705,	5.227272727,	--   90
		    6.136363636,	7.272727273,	8.636363636,	10,	11.59090909,	--   95
		    13.63636364,	15.90909091,	18.63636364,	21.59090909,	25,	--  100
		    28,	31,	35,	40,	45,	--  105
		    50.5,	57,	64,	71,	80,	--  110
		  },
		};

		local function GetEffectFromRating(rating, stat, level)
			level = level or UnitLevel("player")
			local ratingPerBonus = combat_ratings[stat][level]
			local bonus = rating / ratingPerBonus;
			return bonus, ratingPerBonus
		end

    -- [classIdentifier][specializationIndex] = {specializationId, }
    GA.classIdentifier2SpecializationsMap = {}


    LuaUtils:loop(600, function(specId)

		local switchId = specId
		if specId == 269 then switchId = 270 end
		if specId == 270 then switchId = 269 end

        local id, name, description, icon, background, role, class = GetSpecializationInfoByID(switchId)

        if (class ~= nil) then
            if GA.classIdentifier2SpecializationsMap[class] == nil then
                GA.classIdentifier2SpecializationsMap[class] = {}
            end

            local classTable = GA.classIdentifier2SpecializationsMap[class]

            classTable[#classTable + 1] = {["name"] = name, ["id"] = id}
        end
    end)

    if DugisGuideUser.userCustomWeights == nil then
        DugisGuideUser.userCustomWeights = {}
    end

    local function GetAllPossibleWeightIdentifiers(classId, specIndex)
        local excludeList = {
            "ARMOR_SPECIALIZATION_STAT"
            ,"LE_ITEM_CLASS_WEAPON"
            ,"LE_ITEM_CLASS_ARMOR"
            --,"XP_BONUS"
        }

        local weightNames = {}
        if scoring[PackStrings(classId, specIndex)] ~= nil then
            local weightValuePairs = {strsplit(":", scoring[PackStrings(classId, specIndex)])}
            LuaUtils:foreach(weightValuePairs, function(weightValuePair, index)
                local weightName_weightValue = {strsplit(",", weightValuePair)}
                local weightName = weightName_weightValue[1]
                if not LuaUtils:isInTable(weightName, excludeList) then
                    weightNames[#weightNames + 1] = weightName
                end
            end)
        else
            print("|cFF00FF00CANNOT FIND IN THE scoring TABLE (GearAdvisor) THE FOLLOWING ENTRY:", classId..":"..specIndex.."|r")
        end
        return weightNames
    end

    function GA:SpecExists(specIndex)
        local classId = GA:GetCurrentSelectedClassIdentifier()
        return scoring[PackStrings(classId, specIndex)] ~= nil
    end

    local function InitializeUserCustomWeights()
        -- test:   /script print(DugisGuideViewer.Modules.GearAdvisor.userCustomWeights["WARLOCK"][1]["INT"])
        LuaUtils:foreach(DugisGuideViewer.defaultLevelingSpec, function(info, classId)
            local classIndex = info.index
            --max: 4. Used 10 for compatibility with new releases/version
            LuaUtils:loop(10, function (specIndex)
                local classAndSpecId = PackStrings(classId, specIndex)
                if scoring[classAndSpecId] ~= nil then
                    local weightValuePairs = {strsplit(":", scoring[classAndSpecId])}
                    LuaUtils:foreach(weightValuePairs, function(weightValuePair)
                        local weightName_weightValue = {strsplit(",", weightValuePair)}
                        local weightName = weightName_weightValue[1]
                        local weightValue = weightName_weightValue[2]

                        if not DugisGuideUser.userCustomWeights[classId] then
                            DugisGuideUser.userCustomWeights[classId] = {}
                        end

                        if not DugisGuideUser.userCustomWeights[classId][specIndex] then
                            DugisGuideUser.userCustomWeights[classId][specIndex] = {}
                        end

                        if DugisGuideUser.userCustomWeights[classId][specIndex][weightName] == nil then
                            DugisGuideUser.userCustomWeights[classId][specIndex][weightName] = weightValue
                        end
                    end)
                else
                    --print("CANNOT FIND IN THE sscoring TABLE THE FOLLOWING ENTRY:", classId..":"..specIndex)
                end
            end)
        end)
    end

    InitializeUserCustomWeights()
	;
    local classDropDownIndex2classIdentifierMap = {
         [1] = "DEATHKNIGHT"
        ,[2] = "MONK"
        ,[3] = "WARRIOR"
        ,[4] = "PALADIN"
        ,[5] = "DRUID"
        ,[6] = "ROGUE"
        ,[7] = "SHAMAN"
        ,[8] = "HUNTER"
        ,[9] = "MAGE"
        ,[10] = "PRIEST"
        ,[11] = "WARLOCK"
		,[12] = "DEMONHUNTER"
    }

    function GA:GetCurrentSelectedClassIdentifier()
        return classDropDownIndex2classIdentifierMap[DugisGuideViewer.Modules.GearAdvisor.selectedClassIndex]
    end

    --["classId:specIndex"]= "AGI,INT..."
    --Please add tothis list all other stats that wou want to display only when "Display All Stats" is checked.
    local advancedList = {
        ["DEATHKNIGHT:1"]        = "AGI,INT,SPELL_DMG,XP_BONUS",
        ["DEATHKNIGHT:1:PVP"]    = "AGI,INT,SPELL_DMG,XP_BONUS",
        ["DEATHKNIGHT:2"]        = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["DEATHKNIGHT:2:PVP"]    = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["DEATHKNIGHT:3"]        = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["DEATHKNIGHT:3:PVP"]    = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["MONK:1"]               = "INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["MONK:1:PVP"]           = "INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["MONK:2"]               = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["MONK:2:PVP"]           = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["MONK:3"]               = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["MONK:3:PVP"]           = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["WARRIOR:1"]            = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["WARRIOR:1:PVP"]        = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["WARRIOR:2"]            = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["WARRIOR:2:PVP"]        = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["WARRIOR:3"]            = "AGI,INT,SPELL_DMG,XP_BONUS",
        ["WARRIOR:3:PVP"]        = "AGI,INT,SPELL_DMG,XP_BONUS",
        ["PALADIN:1"]            = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PALADIN:1:PVP"]        = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PALADIN:2"]            = "AGI,INT,SPELL_DMG,XP_BONUS",
        ["PALADIN:2:PVP"]        = "AGI,INT,SPELL_DMG,XP_BONUS",
        ["PALADIN:3"]            = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["PALADIN:3:PVP"]        = "STA,AGI,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,XP_BONUS",
        ["DRUID:1"]              = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["DRUID:1:PVP"]          = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["DRUID:2"]              = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["DRUID:2:PVP"]          = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["DRUID:3"]              = "INT,SPELL_DMG,STR,XP_BONUS",
        ["DRUID:3:PVP"]          = "INT,SPELL_DMG,STR,XP_BONUS",
        ["DRUID:4"]              = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["DRUID:4:PVP"]          = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["ROGUE:1"]              = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["ROGUE:1:PVP"]          = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["ROGUE:2"]              = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["ROGUE:2:PVP"]          = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["ROGUE:3"]              = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["ROGUE:3:PVP"]          = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["SHAMAN:1"]             = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["SHAMAN:1:PVP"]         = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["SHAMAN:2"]             = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["SHAMAN:2:PVP"]         = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["SHAMAN:3"]             = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,STR,XP_BONUS",
        ["SHAMAN:3:PVP"]         = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,STR,XP_BONUS",
        ["HUNTER:1"]             = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["HUNTER:1:PVP"]         = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["HUNTER:2"]             = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["HUNTER:2:PVP"]         = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["HUNTER:3"]             = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["HUNTER:3:PVP"]         = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["MAGE:1"]               = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["MAGE:1:PVP"]           = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["MAGE:2"]               = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["MAGE:2:PVP"]           = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["MAGE:3"]               = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["MAGE:3:PVP"]           = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PRIEST:1"]             = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PRIEST:1:PVP"]         = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PRIEST:2"]             = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PRIEST:2:PVP"]         = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PRIEST:3"]             = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["PRIEST:3:PVP"]         = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["WARLOCK:1"]            = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["WARLOCK:1:PVP"]        = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["WARLOCK:2"]            = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["WARLOCK:2:PVP"]        = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["WARLOCK:3"]            = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["WARLOCK:3:PVP"]        = "STA,AGI,AP,ARMOR,AVOIDANCE_RATING,LEECH_RATING,STR,XP_BONUS",
        ["DEMONHUNTER:1"]        = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["DEMONHUNTER:1:PVP"]    = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["DEMONHUNTER:2"]        = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
        ["DEMONHUNTER:2:PVP"]    = "STA,ARMOR,AVOIDANCE_RATING,INT,LEECH_RATING,SPELL_DMG,STR,XP_BONUS",
    }

    -- test:   /script DugisGuideViewer.Modules.GearAdvisor:UpdateWeightsTextboxes("WARLOCK", 1)
    function GA:UpdateWeightsTextboxesForClassAndSpec(classId, specIndex)
        if DugisGuideUser.userCustomWeights == nil then
            return
        end

        specIndex = tonumber(specIndex)
        local possibleWeightIdentifiers = GetAllPossibleWeightIdentifiers(classId, specIndex)

        LuaUtils:loop(28, function(item)
            _G["GA_TextWeight"..item]:Hide()
            _G["GA_EditBoxWeight"..item]:Hide()
        end)

        local weightsTop = 10

        LuaUtils:foreach(possibleWeightIdentifiers, function (weightIdentifier, index)
             local weightValue = DugisGuideUser.userCustomWeights[classId][specIndex][weightIdentifier]

             local shouldDisplayWeigt = true
             local advancedListWeights = advancedList["" .. classId .. ":" .. specIndex]

             if advancedListWeights ~= nil then
                local advancedWeights = {strsplit(",", advancedListWeights)}

                if LuaUtils:isInTable(weightIdentifier, advancedWeights) then
                    if not DugisGuideViewer:UserSetting(DGV_DISPLAYALLSTATS) then
                        shouldDisplayWeigt = false
                    end
                end
             end

             if shouldDisplayWeigt then
                _G["GA_EditBoxWeight"..index]:SetText(weightValue)
                _G["GA_TextWeight"..index]:SetText(weightIdentifier2weightLabelMap[weightIdentifier] or weightIdentifier)
                _G["GA_EditBoxWeight"..index]['weightIdentifier'] = weightIdentifier

                _G["GA_EditBoxWeight"..index]:Show()
                _G["GA_TextWeight"..index]:Show()

                _G["GA_TextWeight"..index]:ClearAllPoints( )
                _G["GA_TextWeight"..index]:SetPoint("TOPLEFT", DugisGuideViewer.Modules.GearAdvisor.scrollFrame.frame.content, "TOPLEFT", 6, weightsTop)
                weightsTop = weightsTop - 22
            end
        end)

        DugisGuideViewer.Modules.GearAdvisor.scrollFrame.scrollBar:GetParent():SetVerticalScroll(0)
        DugisGuideViewer.Modules.GearAdvisor.scrollFrame.scrollBar:SetValue(0)
        local newScrollHeight = -weightsTop - 200
        if newScrollHeight < 0 then
            newScrollHeight = 0
        end
        DugisGuideViewer.Modules.GearAdvisor.scrollFrame.scrollBar:SetMinMaxValues(0, newScrollHeight)
    end

    function GA:ResetWeights()
        DugisGuideUser.userCustomWeights = {}
        InitializeUserCustomWeights()
        GA:UpdateWeightsTextboxes()
    end

    function GA:ApplyWeights()
        local specIndex = tonumber(DugisGuideViewer.Modules.GearAdvisor.selectedSpecIndex)
        local classId = GA:GetCurrentSelectedClassIdentifier()
        local possibleWeightIdentifiers = GetAllPossibleWeightIdentifiers(classId, specIndex)

        LuaUtils:loop(28, function(index)
             if _G["GA_EditBoxWeight"..index]:IsShown() then
                 local weightIdentifier = _G["GA_EditBoxWeight"..index]['weightIdentifier']
                 local weightValue = tonumber(_G["GA_EditBoxWeight"..index]:GetText())

                 --print("set", weightIdentifier, " to ",weightValue)
                 DugisGuideUser.userCustomWeights[classId][specIndex][weightIdentifier] = weightValue

             end
        end)

        if DugisCharacterCache and DugisCharacterCache.CalculateScore_cache_v9 then
            DugisCharacterCache.CalculateScore_cache_v9 = {}
        end

        GA:ResetCalculateScoreCache()
		DGV:ShowReloadUi()
    end

    -- test:   /script DugisGuideViewer.Modules.GearAdvisor:UpdateWeightsTextboxes("WARLOCK", 1)
    function GA:UpdateWeightsTextboxes()
        if DugisGuideViewer.Modules.GearAdvisor.selectedClassIndex and DugisGuideViewer.Modules.GearAdvisor.selectedSpecIndex then
           GA:UpdateWeightsTextboxesForClassAndSpec(GA:GetCurrentSelectedClassIdentifier(), DugisGuideViewer.Modules.GearAdvisor.selectedSpecIndex)
        end
    end


	local defaultLevelingSpec = DugisGuideViewer.defaultLevelingSpec

	local SPELL_SCHOOL_HOLY = 2
	local SPELL_SCHOOL_FIRE = 3
	local SPELL_SCHOOL_NATURE = 4
	local SPELL_SCHOOL_FROST = 5
	local SPELL_SCHOOL_SHADOW = 6
	local SPELL_SCHOOL_ARCANE = 7
	local defaultSpellSchool = {
		["MONK:2"] = SPELL_SCHOOL_NATURE,
		["PALADIN:1"] = SPELL_SCHOOL_HOLY,
		["DRUID:1"] = SPELL_SCHOOL_NATURE,
		["DRUID:4"] = SPELL_SCHOOL_NATURE,
		["SHAMAN:1"] = SPELL_SCHOOL_NATURE,
		["SHAMAN:3"] = SPELL_SCHOOL_NATURE,
		["MAGE:1"] = SPELL_SCHOOL_ARCANE,
		["MAGE:2"] = SPELL_SCHOOL_FIRE,
		["MAGE:3"] = SPELL_SCHOOL_FROST,
		["PRIEST:1"] = SPELL_SCHOOL_HOLY,
		["PRIEST:2"] = SPELL_SCHOOL_HOLY,
		["PRIEST:3"] = SPELL_SCHOOL_SHADOW,
		["WARLOCK:1"] = SPELL_SCHOOL_SHADOW,
		["WARLOCK:2"] = SPELL_SCHOOL_SHADOW,
		["WARLOCK:3"] = SPELL_SCHOOL_SHADOW
	}

	local orig_GetSpecialization
	local function GetSpecialization(...)
		if not orig_GetSpecialization then orig_GetSpecialization = _G.GetSpecialization end
		local spec = orig_GetSpecialization(...)
		if not spec and select("#", ...)==0 then return defaultLevelingSpec[select(2,UnitClass("player"))].index end
		return spec
	end

    --/run print(GetSpecialization_dugis())
    GetSpecialization_dugis = GetSpecialization

    --/dump DugisGuideViewer.Modules.GearAdvisor:GetGearAdvisorScoringValues("LE_ITEM_CLASS_WEAPON")
	function GA:GetGearAdvisorScoringValues(tagName)
		local spec = GetSpecialization()
        local _, class = UnitClass("player")

        local scoringDefinition = scoring[class..":"..spec]
        local tagvalueslist = LuaUtils:split(scoringDefinition, ":")

        local result = {}

        LuaUtils:foreach(tagvalueslist, function(tagvalues)

            local tag_values = LuaUtils:split(tagvalues, ",")

            local tag = tag_values[1]

            if tagName == tag then
                LuaUtils:foreach(tag_values, function(value, index)
                    if index > 1 then
                        if LuaUtils:matchString(value, "|") ~= "" then
                            local value_level = LuaUtils:split(value, "|")
                            value = value_level[1]
                            local level = value_level[2]
                            local playerLevel = UnitLevel("player")

                            if tonumber(level) > playerLevel then
                                value = nil
                            end
                        end

                        if value ~= nil then
                            result[#result + 1] = value
                        end
                    end
                end)
            end
        end)

        return  result
	end


	-- local function VisitCSV(func, itemLink, spec, level, slot, itemSums, uncapped, ...)
		-- local returns = GetCreateTable()
		-- for i=1,select("#", ...) do
			-- returns:InsertList(func(itemLink, spec, level, slot, itemSums, uncapped, strsplit(",", (select(i,...)))))
			-- local last = returns[returns.n]
			-- if type(last)=="number" and last<0 then --allow short circuit on negative values
				-- returns:Pool()
				-- return last
			-- end
		-- end
		-- return returns:Pool()
	-- end

	local function GetAllCSV(...)
		local tbl = {}
		for i=1,select("#", ...) do
			tinsert(tbl, {strsplit(",", (select(i,...)))})
		end
		return tbl
	end

	local scoringSpecs = {}
	local function GetScoringInfo(spec, pvp)
		if not spec then
			spec = GetSpecialization()
		end
		local key = pvp and spec.."PVP" or spec
		if not scoringSpecs[key]  then
			local _, class = UnitClass("player")
			if pvp then
				scoringSpecs[spec.."PVP"] = GetAllCSV(strsplit(":", scoring[PackStrings(class, spec, "PVP")]))
			else
				scoringSpecs[spec] = GetAllCSV(strsplit(":", scoring[PackStrings(class, spec)]))
                LuaUtils:foreach(scoringSpecs[spec], function(row)
                    local weightName = row[1]
                    if DugisGuideUser.userCustomWeights[class] ~= nil
                    and DugisGuideUser.userCustomWeights[class][spec] ~= nil
                    and DugisGuideUser.userCustomWeights[class][spec][weightName] ~= nil then
                       local weightValue = DugisGuideUser.userCustomWeights[class][spec][weightName]
                       row[2] = weightValue
                    end
                end)
			end
		end
		return unpack(scoringSpecs[key])
	end
	GA.scoringSpecs = scoringSpecs

	local function GetTieBreakerScoringInfo()

		return unpack(scoring.tiebreaker)
	end

	local function PlayerCanTitansGrip(weaponSubClass, spec, level)
		local hasTitansGrip = select(2,UnitClass("player"))=="WARRIOR" and spec==2 and level>=10
		if not hasTitansGrip then return end
		return weaponSubClass==LE_ITEM_WEAPON_AXE2H or weaponSubClass==LE_ITEM_WEAPON_MACE2H or weaponSubClass==LE_ITEM_WEAPON_SWORD2H or weaponSubClass==LE_ITEM_WEAPON_POLEARM
	end

	local function PlayerCanDualWield(spec, level)
		local class = select(2,UnitClass("player"))
		if class=="DEATHKNIGHT" or class=="ROGUE" or class=="DEMONHUNTER" then
			return true
		elseif class=="SHAMAN" and level>=10 and spec==2 then
			return true
		elseif class=="MONK" and level>=10 and (spec==1 or spec==3) then
			return true
		elseif class=="WARRIOR" and level>=10 and spec==2 then
			return true
		end
	end


	local function PlayerShouldDualWieldWithMainHand(spec, level, equipSlot, itemSubclass)
		return
			PlayerCanDualWield(spec, level) and
			(equipSlot=="INVTYPE_WEAPON" or equipSlot=="INVTYPE_WEAPONMAINHAND" or PlayerCanTitansGrip(itemSubclass, spec, level))
	end

	local function GetValidKeyTransform(key, itemLink, spec, level, uniqueInventorySlot, itemClass, itemSubclass, equipSlot)



		if uniqueInventorySlot==INVSLOT_OFFHAND then
			if key=="DPS" or key=="DPS|MAIN" then return end
			if key=="DPS|OFF" and itemClass==LE_ITEM_CLASS_WEAPON then
				if not PlayerCanDualWield(spec, level) then return end
				if
					equipSlot=="INVTYPE_SHIELD" or equipSlot=="INVTYPE_WEAPONOFFHAND" or equipSlot=="INVTYPE_HOLDABLE" or equipSlot=="INVTYPE_WEAPON" or
					PlayerCanTitansGrip(itemSubclass, spec, level)
				then
					return "DPS"
				end
			end
		elseif uniqueInventorySlot==INVSLOT_MAINHAND then
			if key=="DPS|OFF" then return end
			if (key=="DPS" or key=="DPS|MAIN") and PlayerShouldDualWieldWithMainHand(spec, level, equipSlot, itemSubclass) then
				return key=="DPS|MAIN" and "DPS" or nil
			end
		end
		return key
	end

	local function nextValidSlot(includeEmpty, slot)
		if not slot then slot = EQUIPPED_FIRST
		else slot = slot+1 end

		for i=slot,EQUIPPED_LAST do
			local itemLink = GetInventoryItemLink("player", i)
			if includeEmpty or itemLink then return i,itemLink end
		end
		return
	end

	local function NextUniqueInventorySlot(invariant, control)
		if not control then return "INVTYPE_HEAD", INVSLOT_HEAD
		elseif control=="INVTYPE_HEAD" then return "INVTYPE_NECK", INVSLOT_NECK
		elseif control=="INVTYPE_NECK" then return "INVTYPE_SHOULDER", INVSLOT_SHOULDER
		elseif control=="INVTYPE_SHOULDER" then return "INVTYPE_CHEST", INVSLOT_CHEST
		elseif control=="INVTYPE_CHEST" then return "INVTYPE_WAIST", INVSLOT_WAIST
		elseif control=="INVTYPE_WAIST" then return "INVTYPE_LEGS", INVSLOT_LEGS
		elseif control=="INVTYPE_LEGS" then return "INVTYPE_FEET", INVSLOT_FEET
		elseif control=="INVTYPE_FEET" then return "INVTYPE_WRIST", INVSLOT_WRIST
		elseif control=="INVTYPE_WRIST" then return "INVTYPE_HAND", INVSLOT_HAND
		elseif control=="INVTYPE_HAND" then return "INVTYPE_FINGER", INVSLOT_FINGER1, INVSLOT_FINGER2
		elseif control=="INVTYPE_FINGER" and DGV:UserSetting(DGV_SUGGESTTRINKET) then
			return "INVTYPE_TRINKET", INVSLOT_TRINKET1, INVSLOT_TRINKET2 --ignore trinkets
		elseif control=="INVTYPE_FINGER" and not DGV:UserSetting(DGV_SUGGESTTRINKET) then
			return "INVTYPE_CLOAK", INVSLOT_BACK --ignore trinkets
		elseif control=="INVTYPE_TRINKET" and DGV:UserSetting(DGV_SUGGESTTRINKET) then
			return "INVTYPE_CLOAK", INVSLOT_BACK --ignore trinkets
		--elseif control=="INVTYPE_FINGER" then return "INVTYPE_CLOAK", INVSLOT_BACK
		elseif control=="INVTYPE_CLOAK" then return INVSLOT_MAINHAND, INVSLOT_MAINHAND, INVSLOT_OFFHAND
		end
	end

	local function PoolItemSums(reaction)
		reaction:UnpackCache():Pool()
	end

	local function GetItemSums(itemLink, threading)
		-- local cacheKey = "GetItemSums"..itemLink
		-- local cacheReaction = TryGetCacheReaction(cacheKey)
		-- if cacheReaction then
			-- return cacheReaction:UnpackCache()
		-- end

		local itemSums = GetCreateTable()
		StatLogic:GetSum(itemLink, itemSums, threading)
		DGV.InitTable(itemSums)
--DGV:DebugFormat("GetItemSums", "itemLink", itemLink, "itemSums", itemSums)
		-- return RegisterReaction("BAG_UPDATE"):SetCache(cacheKey, itemSums):WithAction(PoolItemSums):UnpackCache()
		return itemSums
	end

	--Armor Specialization Stats
	--STA, AGI, INT, STR
	local function GetItemStatSum(key, itemLink, invSlot, itemSums, threading)
		local poolItemSums
		if not itemSums then
			poolItemSums = true
			itemSums = GetItemSums(itemLink, threading)
		end
		local itemSum
			itemSum = itemSums[key] or 0
		if poolItemSums then itemSums:Pool() end
		return itemSum--, bonus, ratingPerBonus, ratingId
	end

	local function UniqueInventoryToInvSlot(uniqueInventorySlot)
		if type(uniqueInventorySlot)=="number" then
			return uniqueInventorySlot
		elseif uniqueInventorySlot=="INVTYPE_FINGER" then
			return INVSLOT_FINGER1, INVSLOT_FINGER2
		elseif uniqueInventorySlot=="INVTYPE_TRINKET" and DGV:UserSetting(DGV_SUGGESTTRINKET) then
			return INVSLOT_TRINKET1, INVSLOT_TRINKET2
		elseif uniqueInventorySlot=="INVTYPE_CLOAK" then
			return INVSLOT_BACK
		else
			return _G["INVSLOT"..strsub(uniqueInventorySlot, 8)]
		end
	end

	local function UnbindSpecDataTable(dataTable)
--DGV:DebugFormat("UnbindSpecDataTable")
		if dataTable.cacheInValid then
--DGV:DebugFormat("UnbindSpecDataTable", "dataTable", tostring(dataTable))
			dataTable:Pool()
			return
		end
	end

	local function GetBestBaselineRating(key, spec, pvp, level, uniqueInventorySlot, itemLink, threading)
--DGV:DebugFormat("GetBestBaselineRating", "key", key, "spec", spec, "level", level, "uniqueInventorySlot", uniqueInventorySlot)
		local dataTable = GA:GetSpecDataTable(spec, pvp, nil, nil, true):BindToAutoroutineLifetime(UnbindSpecDataTable)
--if true then return 0 end
		local invSlot
		local inv1, inv2 = UniqueInventoryToInvSlot(uniqueInventorySlot)
		local current1, current2 = GetInventoryItemLink("player", inv1), GetInventoryItemLink("player", inv2)
		local bestEquippedSlot =
			(dataTable[inv1] == current1 and inv1) or
			(dataTable[inv1] == current2 and inv1) or
			(inv2 and dataTable[inv2] == current1 and inv2) or
			(inv2 and dataTable[inv2] == current2 and inv2)
--if true then return 0 end
		if not inv2 then
			invSlot = inv1
		elseif dataTable[inv1]==itemLink then
			invSlot = inv1
		elseif dataTable[inv2]==itemLink then
			invSlot = inv2
		elseif bestEquippedSlot then
			invSlot = bestEquippedSlot
		else
			local score1 = CalculateScore(dataTable[inv1], spec, pvp, level, uniqueInventorySlot, nil, nil, true)
			local score2 = CalculateScore(dataTable[inv2], spec, pvp, level, uniqueInventorySlot, nil, nil, true)
			-- local score1 = DGV:CalculateScore(nil, current1, spec, level, uniqueInventorySlot, nil, nil, nil, true)
			-- local score2 = DGV:CalculateScore(nil, current2, spec, level, uniqueInventorySlot, nil, nil, nil, true)
			invSlot = score1>=score2 and inv2 or inv1
		end
--if true then return 0 end
		local ratingCount = 0
		for unique,inv1,inv2 in NextUniqueInventorySlot do
			if invSlot~=inv1 then
				ratingCount = ratingCount + GetItemStatSum(key, dataTable[inv1], inv1, nil, threading)
			end
			if invSlot~=inv2 and inv2 and dataTable[inv2] then
				ratingCount = ratingCount + GetItemStatSum(key, dataTable[inv2], inv2, nil, threading)
			end
		end
		return ratingCount
	end

	local function GetCurrentRating(key)
		local cacheKey = "GetCurrentRating"..key
		local cacheReaction = TryGetCacheReaction(cacheKey)
		if cacheReaction then
			return cacheReaction:UnpackCache()
		end
		local ratingCount = 0
		for unique,inv1,inv2 in NextUniqueInventorySlot do
			local inv1Item = GetInventoryItemLink("player", inv1)
			if inv1Item then
				ratingCount = ratingCount + GetItemStatSum(key, inv1Item, inv1)
			end
			local inv2Item = GetInventoryItemLink("player", inv2)
			if inv2 and inv2Item then
				ratingCount = ratingCount + GetItemStatSum(key, inv2Item, inv2)
			end
		end
		RegisterReaction("PLAYER_EQUIPMENT_CHANGED"):SetCache(cacheKey, ratingCount)
		return ratingCount
	end

	local function GetSpecializationSpellSchool(spec)
		if not spec then
			spec = GetSpecialization()
		end
		local _, class = UnitClass("player")
		return defaultSpellSchool[PackStrings(class, spec)]
	end

	--EXPERTISE_RATING
	--MELEE_HIT_RATING
	--SPELL_HIT_RATING
	--RANGED_HIT_RATING
	--MELEE_HASTE_RATING
	--RANGED_HASTE_RATING
	--SPELL_HASTE_RATING
	--MELEE_CRIT_RATING
	--SPELL_CRIT_RATING
	--RANGED_CRIT_RATING
	local function GetCurrentRatingBonus(key, spec)
		local _, class = UnitClass("player")
		-- if key=="EXPERTISE_RATING" then
			-- local expertise, offhandExpertise, rangedExpertise = GetExpertise()
			-- return class=="HUNTER" and rangedExpertise or expertise
		-- elseif key=="MELEE_HIT_RATING" then
			-- return GetCombatRatingBonus(CR_HIT_MELEE) + GetHitModifier()
		-- elseif key=="SPELL_HIT_RATING" then
			-- return GetCombatRatingBonus(CR_HIT_SPELL) + GetSpellHitModifier()
		-- elseif key=="RANGED_HIT_RATING" then
			-- return GetCombatRatingBonus(CR_HIT_RANGED) - GetHitModifier()
		-- else
		if key=="MELEE_HASTE_RATING" then
			return GetMeleeHaste()
		elseif key=="RANGED_HASTE_RATING" then
			return GetRangedHaste()
		elseif key=="SPELL_HASTE_RATING" then
			return UnitSpellHaste("player")
		elseif key=="MELEE_CRIT_RATING" then
			return GetCritChance()
		elseif key=="SPELL_CRIT_RATING" then
			return GetSpellCritChance(GetSpecializationSpellSchool(spec))
		elseif key=="RANGED_CRIT_RATING" then
			return GetRangedCritChance()
		end
	end

	local function GetBestBaselineBonus(key, spec, pvp, level, uniqueInventorySlot, itemLink, ratingPerBonus, threading)
		-- local cacheKey = strformat("%s%s%d%d%s", "GetBestBaselineBonus", key, spec, level, tostring(uniqueInventorySlot))
		-- local cacheReaction = TryGetCacheReaction(cacheKey)
		-- if cacheReaction then
			-- return cacheReaction:UnpackCache()
		-- end
		local currentBonus = GetCurrentRatingBonus(key, spec)
		if not currentBonus then return end
		local baseLineRating = GetBestBaselineRating(key, spec, pvp, level, uniqueInventorySlot, itemLink, threading)
		local currentRating = GetCurrentRating(key)
		local ratingDifference = baseLineRating - currentRating
		local baselineBonus = currentBonus + ratingDifference/ratingPerBonus
--DGV:DebugFormat("GetBestBaselineBonus", "key", key, "baselineBonus", baselineBonus, "baseLineRating", baseLineRating, "currentRating", currentRating, "ratingDifference", ratingDifference, "currentBonus", currentBonus, "ratingDifference/ratingPerBonus", ratingDifference/ratingPerBonus)
		-- RegisterStopwatchReaction(0):SetCache(cacheKey, baselineBonus)
		return baselineBonus
	end

	local function GetCapValue(key, cap)
		local capNumber = tonumber(cap)
		if capNumber then return capNumber*100 end
		local levelDifference = DGV:UserSetting(DGV_GASTATCAPLEVELDIFFERENCE)
		if cap=="NO_MISS" then
			-- if key=="MELEE_HIT_RATING" or key=="RANGED_HIT_RATING" then
				-- capNumber = BASE_MISS_CHANCE_PHYSICAL[levelDifference]
			-- elseif key=="SPELL_HIT_RATING" then
				-- capNumber = BASE_MISS_CHANCE_SPELL[levelDifference]
			-- end
		elseif key=="EXPERTISE_RATING" then
			-- if cap=="NO_DODGE" then
				-- capNumber = BASE_ENEMY_DODGE_CHANCE[levelDifference]
			-- elseif cap=="NO_PARRY" then
				-- capNumber = BASE_ENEMY_DODGE_CHANCE[levelDifference]+BASE_ENEMY_PARRY_CHANCE[levelDifference]
			-- end
		end
--DGV:DebugFormat("GetCapValue", "key", key, "capNumber", capNumber)
		return capNumber
	end

	local function CalculateRatingScore(key, baseLineBonus, rating, bonus, ratingPerBonus, weight, cap, ...)
--DGV:DebugFormat("CalculateRatingScore", "baseLineBonus", baseLineBonus, "bonus", bonus, "rating", rating, "weight", weight, "cap", cap)
		weight = tonumber(weight)
		cap = GetCapValue(key, cap)
		local scoreBelowCap, scoreAboveCap = 0,0
--DGV:DebugFormat("CalculateRatingScore 1", "baseLineBonus", baseLineBonus, "rating", rating, "bonus", bonus)
		if not cap or (baseLineBonus+bonus)<cap then
			scoreBelowCap = rating*weight
		elseif baseLineBonus<cap then
			local bonusRatio = (cap-baseLineBonus)/bonus
			scoreBelowCap = (rating*bonusRatio)*weight
			bonus = (1-bonusRatio)*bonus
			rating = (1-bonusRatio)*rating
		end
		if cap and baseLineBonus+bonus>cap then
			scoreAboveCap = CalculateRatingScore(key, baseLineBonus, rating, bonus, ratingPerBonus, ...)
		end

--DGV:DebugFormat("CalculateRatingScore 2", "key", key, "baseLineBonus", baseLineBonus, "rating", rating, "bonus", bonus, "weight", weight, "cap", cap, "scoreBelowCap", scoreBelowCap, "scoreAboveCap", scoreAboveCap)
		return scoreBelowCap+scoreAboveCap
	end

	local function CheckSubclass(level, itemRarity, itemSubclass, ...)
		for i=1,select("#", ...) do
			local arg = select(i, ...)
			local number, requiredLevel = strsplit("|", arg)
			number = tonumber(number)
			requiredLevel = tonumber(requiredLevel) or 0
			local selectedArg = (requiredLevel<=level or itemRarity==7) and number or -1 --itemRarity==7 excuses heirlooms, so long as character can eventually use the armor type
			if selectedArg==itemSubclass then return true end
		end
	end


	local function IsArmorSpecSlot(slot)
		return
			slot==INVSLOT_CHEST or
			slot==INVSLOT_FEET or
			slot==INVSLOT_HAND or
			slot==INVSLOT_HEAD or
			slot==INVSLOT_LEGS or
			slot==INVSLOT_SHOULDER or
			slot==INVSLOT_WAIST or
			slot==INVSLOT_WRIST
	end

	local function ItemIsEquipped(itemLink, itemIteratorTable)
		if itemIteratorTable then
			return itemIteratorTable.player and not itemIteratorTable.bags
		else
			for slot,link in nextValidSlot do
				if link==itemLink then return true end
			end
		end
	end

	local function ItemIsBanned(itemLink, itemIteratorTable)
		return DGV.chardb.GA_Blacklist and DGV.chardb.GA_Blacklist[DGV:GetItemIdFromLink(itemLink)] and not ItemIsEquipped(itemLink, itemIteratorTable)
	end

	local function ItemIsBannedWeapon(itemLink)
		if not itemLink then return end
		return DGV.chardb.GA_Blacklist and DGV.chardb.GA_Blacklist[DGV:GetItemIdFromLink(itemLink)]
	end

	function CalculateScoreForInfo(itemLink, spec, pvp, level, uniqueInventorySlot, itemSums, enforceArmorSpecSubclass, uncapped, forGearFinder, key, value, ...)
		if key == "ARMOR_SPECIALIZATION_STAT" then return 0 end
        
        local _, itemId = nil, 0
        
        if type(itemLink) == "number" then
            itemId = tostring(itemLink)
        else
            local itemString = string.match(itemLink, "item[%-?%d:]+")
            _, itemId = strsplit(":", itemString)
        end

		local itemRarity, equipSlot, itemClass, itemSubclass
        
        if forGearFinder then
            _, _, itemRarity, _, _, _, _, _, equipSlot, _, _, itemClass, itemSubclass = GetItemInfo_dugi(itemLink, true)
        else
            _, _, itemRarity, _, _, _, _, _, equipSlot, _, _, itemClass, itemSubclass = GetItemInfo(itemLink)
        end
    
		if itemClass~=LE_ITEM_CLASS_WEAPON and itemClass~=LE_ITEM_CLASS_ARMOR then return -2 end

		if DGV:UserSetting(DGV_FISHINGPOLE) and ItemIsEquipped(itemLink) then
			if itemClass==LE_ITEM_CLASS_WEAPON and itemSubclass==LE_ITEM_WEAPON_FISHINGPOLE then
				return 2000000
			elseif tonumber(itemId) == 6256 or
				tonumber(itemId) == 19969 or
				tonumber(itemId) == 50287 or
				tonumber(itemId) == 93732 or
				tonumber(itemId) == 118380 or
				tonumber(itemId) == 117405 or
				tonumber(itemId) == 88710 or
				tonumber(itemId) == 33820 or
				tonumber(itemId) == 19972 or
				tonumber(itemId) == 33820 or
				tonumber(itemId) == 118393
				then
				return 2000000
			end
		end

		if DGV:UserSetting(DGV_COOKINGITEM) and ItemIsEquipped(itemLink) then
			if tonumber(itemId) == 46349 or
				tonumber(itemId) == 86468 or
				tonumber(itemId) == 86559 or
				tonumber(itemId) == 86558
				then
				return 2000000
			end
		end

		if DGV:UserSetting(DGV_DAILYITEM) and ItemIsEquipped(itemLink) then
			if tonumber(itemId) == 46106 or --argent lance
				tonumber(itemId) == 46069 or --alliance lance
				tonumber(itemId) == 46070 --horde lance
				then
				return 2000000
			end
		end

		if itemClass==LE_ITEM_CLASS_ARMOR and itemSubclass==LE_ITEM_ARMOR_COSMETIC then return -2 end
		if (key == "LE_ITEM_CLASS_WEAPON" and itemClass==LE_ITEM_CLASS_WEAPON) or (key == "LE_ITEM_CLASS_ARMOR" and itemClass==LE_ITEM_CLASS_ARMOR) then
			if
				enforceArmorSpecSubclass and
				itemClass==LE_ITEM_CLASS_ARMOR and
				itemSubclass~=LE_ITEM_ARMOR_SHIELD and
				itemSubclass~=enforceArmorSpecSubclass and
				IsArmorSpecSlot(UniqueInventoryToInvSlot(uniqueInventorySlot))
			then
				return -4
			end
			return CheckSubclass(level, itemRarity, itemSubclass, value, ...) and 0 or -3
		elseif key ~= "LE_ITEM_CLASS_WEAPON" and key ~= "LE_ITEM_CLASS_ARMOR" then
			local keyTransform = GetValidKeyTransform(key, itemLink, spec, level, uniqueInventorySlot, itemClass, itemSubclass, equipSlot)
			if not keyTransform then return 0 end
			local itemSum = GetItemStatSum(keyTransform, itemLink, uniqueInventorySlot, itemSums, forGearFinder)
--if true then return itemSum*value end
			local slot1, slot2 = UniqueInventoryToInvSlot(uniqueInventorySlot)
			if uncapped or itemSum==0 or not select(1, ...) or not GetCurrentRatingBonus(keyTransform, spec) then
				return itemSum*value, itemSum
			end
			local bonus, ratingPerBonus = GetEffectFromRating(itemSum, keyTransform)
			local baseLineBonus = GetBestBaselineBonus(keyTransform, spec, pvp, level, uniqueInventorySlot, itemLink, ratingPerBonus, forGearFinder)
--if true then return itemSum*value end

			local score = CalculateRatingScore(keyTransform, baseLineBonus, itemSum, bonus, ratingPerBonus, value, ...)
			return score, itemSum
		end
	end


	local function GetUniqueID(control)
		return string.format("%s|%s|%d", tostring(control.func), tostring(control.bag), control.slot)
	end

	local function ItemWasFirst(control, link)
		if control.first==link then
			control.first = nil
			return true
		end
	end

	--GetContainerItemLink(container, slot)
	--BACKPACK_CONTAINER: Backpack (0)
	--1 through GetNumBankSlots(): Bag slots (as presented in the default UI, numbered right to left)
	--GetInventoryItemLink("unit", slot) EQUIPPED_FIRST, EQUIPPED_LAST
	--GetNumQuestRewards
	--GetNumQuestChoices
	--GetQuestLogItemLink("itemType", index)
	--GetQuestItemLink("itemType", index)
	--name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemID) or GetItemInfo("itemName") or GetItemInfo("itemLink")
	local ITEM_ITERATOR_SKIP_EQUIPPED = 1
	local ITEM_ITERATOR_SKIP_REWARDS = 2
	local ITEM_ITERATOR_SKIP_LOOT_ROLL = 4
	local ITEM_ITERATOR_SKIP_ENCOUNTER_JOURNAL = 8
	local ITEM_ITERATOR_SKIP_VENDOR = 16
	local ITEM_ITERATOR_SKIP_LOOT = 32
	local ITEM_ITERATOR_SKIP_ALL_EXTERNAL = bit.bor(ITEM_ITERATOR_SKIP_REWARDS, ITEM_ITERATOR_SKIP_LOOT_ROLL, ITEM_ITERATOR_SKIP_ENCOUNTER_JOURNAL, ITEM_ITERATOR_SKIP_VENDOR, ITEM_ITERATOR_SKIP_LOOT)
	local function ItemIterator(invariant, control)
		if not control then
			if type(invariant)=="table" then
				control = invariant
				control:BindToAutoroutineLifetime(tPool)
				if control.first then
					return control, control.first
				end
			else
				control = GetCreateTable():BindToAutoroutineLifetime(tPool)
				control.skip = invariant
			end
		end
		if not control.func then
			control.func = GetInventoryItemLink
		end
		local skip = control.skip
		local skipEquipped = skip and bit.band(skip, ITEM_ITERATOR_SKIP_EQUIPPED)==ITEM_ITERATOR_SKIP_EQUIPPED
		local skipRewards = skip and bit.band(skip, ITEM_ITERATOR_SKIP_REWARDS)==ITEM_ITERATOR_SKIP_REWARDS
		local skipLootRoll = skip and bit.band(skip, ITEM_ITERATOR_SKIP_LOOT_ROLL)==ITEM_ITERATOR_SKIP_LOOT_ROLL
		local skipEncounterJournal = skip and bit.band(skip, ITEM_ITERATOR_SKIP_ENCOUNTER_JOURNAL)==ITEM_ITERATOR_SKIP_ENCOUNTER_JOURNAL
		local skipVendor = skip and bit.band(skip, ITEM_ITERATOR_SKIP_VENDOR)==ITEM_ITERATOR_SKIP_VENDOR
		local skipLoot = skip and bit.band(skip, ITEM_ITERATOR_SKIP_LOOT)==ITEM_ITERATOR_SKIP_LOOT
		while true do
			local func = control.func
			local slot = control.slot
			if func==GetInventoryItemLink then
				if not slot then control.slot = skipEquipped and BANK_CONTAINER_INVENTORY_OFFSET+1 or EQUIPPED_FIRST
				else control.slot = slot + 1 end
				if control.slot==EQUIPPED_LAST+1 then control.slot = BANK_CONTAINER_INVENTORY_OFFSET+1 end
				slot = control.slot
				if slot<=BANK_CONTAINER_INVENTORY_OFFSET+NUM_BANKGENERIC_SLOTS then
					local itemLink = GetInventoryItemLink("player", slot)
					if itemLink and select(9, GetItemInfo(itemLink))~="" and not ItemWasFirst(control, itemLink) then
						control.player = slot<=EQUIPPED_LAST
						control.bank = not control.player
						control.bags = false
						return control, itemLink
					end
				else
					control.player = nil
					control.bank = nil
					control.bags = nil
					control.func = GetContainerItemLink
					control.slot = nil
				end
			elseif func==GetContainerItemLink then
				if not slot then slot = 0 end
				slot = slot + 1
				control.slot = slot
				if not control.bag then control.bag = BACKPACK_CONTAINER end
				if slot<=GetContainerNumSlots(control.bag) then
					local itemLink = GetContainerItemLink(control.bag, slot)
					control.player = control.bag<=NUM_BAG_SLOTS
					control.bank = not control.player
					control.bags = true
					if itemLink then
						local itemName, _, _, _, _, _, _, _, itemEquipSlot = GetItemInfo(itemLink)
						if
							itemName
							and itemEquipSlot
							and itemEquipSlot~=""
							and not ItemWasFirst(control, itemLink)
						then
							return control, itemLink
						end
					end
				else
					control.bag = control.bag + 1
					if control.bag>NUM_BAG_SLOTS+GetNumBankSlots() then
						control.bag = nil
						control.player = nil
						control.bank = nil
						control.bags = nil
						control.func = GetNumQuestRewards
						control.itemType = "reward"
					end
					control.slot = nil
				end
			elseif control.itemType then
				if
					not skipRewards and
					(QuestFrame:IsShown() or
					QuestLogPopupDetailFrame:IsShown() or
					(WorldMapFrame:IsShown() and QuestMapFrame.DetailsFrame:IsShown())) then
					if not slot then slot = 0 end
					slot = slot + 1
					control.slot = slot
					if slot<=func() then
						local itemLink, itemName
						if QuestInfoFrame.questLog then
							itemLink = GetQuestLogItemLink(control.itemType, slot)
							if control.itemType=="choice" then
								itemName = GetQuestLogChoiceInfo(slot)
							else
								itemName = GetQuestLogRewardInfo(slot)
							end
						else
							itemLink = GetQuestItemLink(control.itemType, slot)
							itemName = GetQuestItemInfo(control.itemType, slot)
						end
						local itemFrame = _G["QuestInfoItem"..control.slot]
						if
							itemLink
							and itemFrame
							and itemFrame:IsShown()
							and itemFrame.type==control.itemType
							and itemFrame.name:GetText()==itemName
							and not ItemWasFirst(control, itemLink)
						then
							return control, itemLink
						end
					elseif func == GetNumQuestRewards then
						control.func = GetNumQuestChoices
						control.itemType = "choice"
						control.slot = nil
					elseif func == GetNumQuestChoices then
						control.func = GetNumQuestLogRewards
						control.itemType = "reward"
						control.slot = nil
					elseif func == GetNumQuestLogRewards then
						control.func = GetNumQuestLogChoices
						control.itemType = "choice"
						control.slot = nil
					else
						control.func = GetLootRollItemLink
						control.slot = nil
						control.itemType = nil
					end
				else
					control.func = GetLootRollItemLink
					control.slot = nil
					control.itemType = nil
				end
			elseif func==GetLootRollItemLink then
				if not slot then slot = 0 end
				slot = (not skipLootRoll) and slot + 1
				control.slot = slot
				if slot and slot<=NUM_GROUP_LOOT_FRAMES and GroupLootFrame1 and GroupLootFrame1:IsShown() then
					local lootFrame =  _G["GroupLootFrame"..slot]
					if lootFrame and lootFrame:IsShown() then
						local itemLink = func(lootFrame.rollID)
						if itemLink and not ItemWasFirst(control, itemLink) then
							return control, func(lootFrame.rollID)
						end
					end
				end
				control.func = EJ_GetLootInfoByIndex
				control.slot = nil
			elseif func==EJ_GetLootInfoByIndex then
				if not skipEncounterJournal and
					EncounterJournal and
					EncounterJournal:IsShown() and
					EncounterJournal.encounter.info.lootScroll:IsShown()
				then
					if not slot then slot = 0 end
					slot = slot + 1
					control.slot = slot
					local numLoot = EJ_GetNumLoot()
					if slot<=numLoot then
						local name, icon, slot, armorType, itemID, link, encounterID = func(slot)
						if link and not ItemWasFirst(control, link) then
							return control, link
						end
					end
				end
				control.func = GetMerchantItemLink
				control.slot = nil
			elseif func==GetMerchantItemLink then
				if
					not skipVendor and
					MerchantFrame:IsShown()
				then
					if not slot then slot = 0 end
					slot = slot + 1
					control.slot = slot
					local link = func(slot)
					if link and not ItemWasFirst(control, link) then
						return control, link
					end
				end
				control.func = GetLootSlotLink
				control.slot = nil
			elseif func==GetLootSlotLink then
				if not slot then slot = 0 end

				slot = slot + 1
				control.slot = slot
				if
					not skipLoot and
					LootFrame:IsShown()
				then
					local link = func(slot)
					if link and not ItemWasFirst(control, link) then
						return control, link
					end
				end
				if slot > GetNumLootItems() then
					control:Pool()
					return
				end
			else
				control:Pool()
				return
			end
		end
	end

    GeadAdvisorItemIterator = ItemIterator

	function GA.ItemChoiceIterator(invariant, control)
		if not control then control = 0 end
		control = control + 1
		local isLogFrame = QuestFrame:IsShown() or QuestLogPopupDetailFrame:IsShown() or (WorldMapFrame:IsShown() and QuestMapFrame.DetailsFrame:IsShown())
		local getNumChoices = isLogFrame and GetNumQuestLogChoices or GetNumQuestChoices
		local getItemLink = isLogFrame and GetQuestLogItemLink or GetQuestItemLink
		if control<=getNumChoices() then
			local itemLink = getItemLink("choice", control)
			if itemLink then
				return control, itemLink, QuestInfo_GetRewardButton(QuestInfoFrame.rewardsFrame, control)
			end
		else return end
	end

	function GA.LootRollIterator(invariant, control)
		if not control then control = 0 end
		control = control + 1
		if control<=NUM_GROUP_LOOT_FRAMES then
			local lootFrame =  _G["GroupLootFrame"..control]
			if lootFrame and lootFrame:IsShown() then
				local itemLink = GetLootRollItemLink(lootFrame.rollID)
				if itemLink then
					return control, itemLink, lootFrame
				end
			end
		end
		return
	end

	local ejLootScroll
	function GA.EncounterJournalIterator(invariant, control)
--DGV:DebugFormat("EncounterJournalIterator", "control", control)
		local EncounterJournal = EncounterJournal
		if not EncounterJournal then return end
		ejLootScroll = ejLootScroll or EncounterJournal.encounter.info.lootScroll
		if
			EncounterJournal and
			EncounterJournal:IsShown() and
			ejLootScroll:IsShown()
		then
			if not control then control = 0 end
			control = control + 1

			if control<=EJ_GetNumLoot() then
				local name, icon, slot, armorType, itemID, link, encounterID = EJ_GetLootInfoByIndex(control)
				local items = ejLootScroll.buttons;
				local offset = HybridScrollFrame_GetOffset(ejLootScroll)
				local buttonIndex = control-offset
				local button
				if buttonIndex>=1 and buttonIndex<=#items then
					button = items[buttonIndex]
				end
--DGV:DebugFormat("EncounterJournalIterator", "link", link ,"has frame", button~=nil, "#items", #items, "buttonIndex", buttonIndex)
				return control, link, button
			end
		end
	end

	function GA.VendorIterator(invariant, control)
		if not control then control = 0 end
		control = control + 1
		local link = GetMerchantItemLink(control)
		if link then
			local buttonIndex = control % MERCHANT_ITEMS_PER_PAGE
			if buttonIndex==0 then buttonIndex = MERCHANT_ITEMS_PER_PAGE end
			local lootFrame =  _G["MerchantItem"..buttonIndex.."ItemButton"]
			lootFrame = lootFrame and lootFrame:IsShown() and lootFrame.link==link and lootFrame
			return control, link, lootFrame
		end
		return
	end

	function GA.LootIterator(invariant, control)
		if not control then control = 0 end
		local numItems = GetNumLootItems()
		while control < numItems do
			control = control + 1
			local link = GetLootSlotLink(control)
			if link then
				local numLootToShow = LOOTFRAME_NUMBUTTONS;
				if ( numItems > LOOTFRAME_NUMBUTTONS ) then
					numLootToShow = numLootToShow - 1;
				end
				local buttonIndex = control % numLootToShow
				if buttonIndex==0 then buttonIndex = numLootToShow end
				local lootFrame =  _G["LootButton"..buttonIndex]
				lootFrame = lootFrame and lootFrame:IsShown() and lootFrame.slot==control and lootFrame
				return control, link, lootFrame
			end
		end
	end

	local function EvaluateWinCriteron(criterion, itemLink)
		if criterion:Predicate(itemLink, unpack(criterion)) then
			local win, altWinner, score, altScore, uniqueSlot = criterion:GetScore(itemLink, unpack(criterion))
			if win then
				return score, altWinner, altScore, uniqueSlot
			end
		end
	end

	math.sum = function(...)
		local sum = 0
		for i=1,select("#", ...) do
			sum = sum + (select(i,...))
		end
		return sum
	end

	local function MainHandBlocksSecondarySlot(itemSubclass, equipSlot, link, spec, level)
		if PlayerCanTitansGrip(itemSubclass, spec, level) then

			return false
		elseif itemSubclass==LE_ITEM_WEAPON_CROSSBOW or itemSubclass==LE_ITEM_WEAPON_GUNS or itemSubclass==LE_ITEM_WEAPON_BOWS or itemSubclass==LE_ITEM_WEAPON_POLEARM or itemSubclass==LE_ITEM_WEAPON_STAFF then --crossbow does, but wands (another INVTYPE_RANGEDRIGHT) don't
			return true
		elseif equipSlot=="INVTYPE_RANGED" or equipSlot=="INVTYPE_2HWEAPON" then
			return true
		end
		return false

	end

	function GetDefaultUniqueInventorySlot(equipSlot)
		if equipSlot=="" then return end
		if equipSlot=="INVTYPE_ROBE" then
			return "INVTYPE_CHEST"
		elseif
			equipSlot=="INVTYPE_WEAPON" or equipSlot=="INVTYPE_RANGED" or equipSlot=="INVTYPE_2HWEAPON" or
			equipSlot=="INVTYPE_WEAPONMAINHAND" or equipSlot=="INVTYPE_RANGEDRIGHT"
		then
			return INVSLOT_MAINHAND
		elseif equipSlot=="INVTYPE_SHIELD" or equipSlot=="INVTYPE_WEAPONOFFHAND" or equipSlot=="INVTYPE_HOLDABLE" then
			return INVSLOT_OFFHAND
		end
		return equipSlot
	end

	local function CanAdornUniqueInventorySlot(uniqueInventorySlot, itemClass, itemSubclass, equipSlot, spec, level)
		local class = select(2,UnitClass("player"))
		if not equipSlot or equipSlot=="" then return false end
		if equipSlot=="INVTYPE_WEAPONOFFHAND" and not PlayerCanDualWield(spec, level) then return false end
		if uniqueInventorySlot==GetDefaultUniqueInventorySlot(equipSlot) then
			return true
		elseif uniqueInventorySlot==INVSLOT_OFFHAND and itemClass==LE_ITEM_CLASS_WEAPON then
			if (equipSlot=="INVTYPE_WEAPONOFFHAND" or equipSlot=="INVTYPE_WEAPON") and PlayerCanDualWield(spec, level) then
				return true
			elseif equipSlot=="INVTYPE_SHIELD" or equipSlot=="INVTYPE_HOLDABLE" and class~="HUNTER" then
				return true
			elseif PlayerCanTitansGrip(itemSubclass, spec, level) then
				return true
			end
		end
		return false
	end

	local function DualWieldPreferenceIsMainhandDPS()
		local class = select(2,UnitClass("player"))
		return class=="MONK"
	end

	local function DualWieldPreferenceIsOffhandDPH()
		local class = select(2,UnitClass("player"))
		return class=="SHAMAN"
	end

	local function DualWieldPreferenceIsMainhandDagger()
		local class = select(2,UnitClass("player"))
		local spec = GetSpecialization()
		return class=="ROGUE" and spec==3 --Subtlety Rogue
	end

	local function ShouldSwapMainOffWeapons(mainLink, offLink, spec, level)
		if not mainLink or not offLink then return end
		if ItemIsBannedWeapon(mainLink) or ItemIsBannedWeapon(offLink) then return end --don't swap hands if item is banned
		local mainSums, offSums = GetItemSums(mainLink), GetItemSums(offLink)
		local mainItemClass, mainItemSubclass = select(12, GetItemInfo(mainLink))
		local offItemClass, offItemSubclass = select(12, GetItemInfo(offLink))

		local continueSwap = false
		if DualWieldPreferenceIsMainhandDPS() and DGV:UserSetting(DGV_WEAPONPREF)=="Auto" then
			continueSwap = (mainSums["DPS"] or 0) < (offSums["DPS"] or 0)
		elseif DualWieldPreferenceIsOffhandDPH() or DGV:UserSetting(DGV_WEAPONPREF)=="Fast / Slow" then
			continueSwap = (mainSums["MAX_DAMAGE"] or 0) > (offSums["MAX_DAMAGE"] or 0)
		elseif DGV:UserSetting(DGV_WEAPONPREF)=="Slow / Fast" and PlayerCanDualWield(spec, level) then
			continueSwap = (mainSums["MAX_DAMAGE"] or 0) < (offSums["MAX_DAMAGE"] or 0)
		elseif DualWieldPreferenceIsMainhandDagger() and (mainItemSubclass == 15 or offItemSubclass == 15) then
			continueSwap = mainItemSubclass ~= 15 --and (mainSums["MAX_DAMAGE"] or 0) < ((offSums["MAX_DAMAGE"]*1.5) or 0) --Dagger unless other item does 50% more MAX_DAMAGE
		else
			continueSwap = (mainSums["MAX_DAMAGE"] or 0) < (offSums["MAX_DAMAGE"] or 0)
		end
		mainSums:Pool()

		offSums:Pool()
		if not continueSwap then return end
		local itemEquipSlot, _, _, itemClass, itemSubclass = select(9, GetItemInfo(mainLink))
		if not CanAdornUniqueInventorySlot(INVSLOT_OFFHAND, itemClass, itemSubclass, itemEquipSlot, spec, level) then return end
		local itemEquipSlot, _, _, itemClass, itemSubclass = select(9, GetItemInfo(offLink))
		if not CanAdornUniqueInventorySlot(INVSLOT_MAINHAND, itemClass, itemSubclass, itemEquipSlot, spec, level) then return end
		return true
	end

	local function IsEnforcedTableComplete(enforcedTable)
		for unique,inv1,inv2 in NextUniqueInventorySlot do
			if IsArmorSpecSlot(inv1) and not enforcedTable[inv1] then return end
		end
		return true
	end

	local function StandardTableGrantsArmorSpecialization(standardTable, enforcedTable)
		for unique,inv1,inv2 in NextUniqueInventorySlot do
			if IsArmorSpecSlot(inv1) and (not standardTable[inv1]) or enforcedTable[inv1]~=standardTable[inv1] then return end
		end
		return true
	end

	local function StatKeyToUnitStatIndex(statKey)
		if statKey=="STR" then return 1
		elseif statKey=="AGI" then return 2
		elseif statKey=="STA" then return 3
		elseif statKey=="INT" then return 4
--		elseif statKey=="SPI" then return 5
		end
	end

	local function EstimateStatBaseValue(statKey, spec)
		local stat, effectiveStat, posBuff, negBuff = UnitStat("player", StatKeyToUnitStatIndex(statKey))
		local base =  stat - posBuff - negBuff
		local coefficient = 1
		if StatLogic.GetArmorSpecActive()==spec then
			coefficient = coefficient + .05
		end
		if statKey=="STA" then
			if IsSpellKnown(29144) then --Unwavering Sentinel
				coefficient = coefficient + .15
			elseif UnitBuff("player", (GetSpellInfo(5487))) then --Bear Form
				coefficient = coefficient + .2
			else
				if IsSpellKnown(50029) then --Veteran of the Third War
					coefficient = coefficient + .09
				end
				if StatLogic.SlotHasEnchant(3847, INVSLOT_MAINHAND) then --Rune of the Stoneskin Gargoyle
					coefficient = coefficient + .02
				elseif StatLogic.SlotHasEnchant(3883, INVSLOT_MAINHAND) then --Rune of the Nerubian Carapace Mainhand
					coefficient = coefficient + .01
				end
				if StatLogic.SlotHasEnchant(3883, INVSLOT_OFFHAND) then --Rune of the Nerubian Carapace Mainhand Offhand
					coefficient = coefficient + .01
				end
			end
		elseif statKey=="STR" and IsSpellKnown(91107) then --Unholy Might
			coefficient = coefficient + .1
		end
		base = base/coefficient
--DGV:DebugFormat("EstimateStatBaseValue", "statKey", statKey, "spec", spec, "base", base)
		return base
	end

	local function GetArmorSpecStat(data)
		if data[1]=="ARMOR_SPECIALIZATION_STAT" then
			return data[2]
		end
	end

	local function GetArmorSpecStatWeight(...)
		local armorSpecStatKey
		for i=1,select("#", ...) do
			local data = (select(i, ...))
			armorSpecStatKey = armorSpecStatKey or GetArmorSpecStat(data)
			if armorSpecStatKey and data[1]==armorSpecStatKey then
				return armorSpecStatKey, data[2]
			end
		end
	end

	local function ArmorSpecializationBonusWins(spec, standardTable, enforcedTable)
		local armorSpecStatKey, armorSpecStatWeight = GetArmorSpecStatWeight(GetScoringInfo(spec))
		local bonusStatValue = (enforcedTable.ArmorSpecStatTotal + EstimateStatBaseValue(armorSpecStatKey, spec))*.05
--DGV:DebugFormat("ArmorSpecializationBonusWins", "bonusStatValue", bonusStatValue, "enforcedTable.ArmorSpecStatTotal", enforcedTable.ArmorSpecStatTotal, "EstimateStatBaseValue(armorSpecStatKey, spec)", EstimateStatBaseValue(armorSpecStatKey, spec))
		local bonusScore = bonusStatValue * armorSpecStatWeight
		local enforcedTableScore = enforcedTable.ScoreTotal + bonusScore
-- if not standardTable.ScoreTotal	then
-- DGV:DebugFormat("ArmorSpecializationBonusWins", "standardTable", tostring(standardTable), "standardTable:IsBoundToAutoroutineLifetime()", standardTable:IsBoundToAutoroutineLifetime())
-- end

        if enforcedTableScore == nil then
            enforcedTableScore = 0
        end

        local scoreTotal = standardTable.ScoreTotal

        if scoreTotal == nil then
            scoreTotal = 0
        end

		return enforcedTableScore>scoreTotal
	end

	local function GetArmorSpecSubclassFrom(data)
		if data[1]=="LE_ITEM_CLASS_ARMOR" then
			local greatest
			for i=2,#data do
				local value = data[i]
				value = tonumber(value:match("%d"))
				if
					value
					and value~=LE_ITEM_ARMOR_SHIELD
					and value~=LE_ITEM_ARMOR_COSMETIC
					and (not greatest or value>greatest)
				then
					greatest = value
				end
			end
			return greatest
		end
	end

	local function GetArmorSpecSubclass(...)
		for i=1,select("#", ...) do
			local data = (select(i, ...))
			local subclass = GetArmorSpecSubclassFrom(data)
			if subclass then return subclass end
		end
	end

	local function GetCurrentBestInSlot(uniqueInventorySlot, spec, pvp, level, skip, enforceArmorSpecSubclass, uncapped, ignoreLevelRequirement, itemMustWin)
		level = level or UnitLevel("player")
		spec = spec or GetSpecialization()
		local armorSpecSubclass =  itemMustWin and GetArmorSpecSubclass(GetScoringInfo(spec, pvp))
		local winner, winScore, winArmorSpecStatValue, winnerGrantsArmorSpecSubclass,
			altWinner, altScore, altArmorSpecStatValue,
			mainHandWinner, mainHandScore, mainHandOffHandScore, mainHandArmorSpecStatValue,
			offHandWinner, offHandScore, offHandArmorSpecStatValue,
			twoHandWinner, twoHandScore, twoHandArmorSpecStatValue,
			mustWinGrantsArmorSpecSubclass
		local itemInvariant = GetCreateTable()
		itemInvariant.first = itemMustWin
		itemInvariant.skip = skip
		for control,iteratedItemLink in ItemIterator,itemInvariant do
			local reqLevel, itemClass, itemSubclass, itemEquipSlot
			if not ItemIsBanned(iteratedItemLink, control) then
				local score, armorSpecStatValue
				reqLevel, _, _, _, itemEquipSlot, _, _, itemClass, itemSubclass = select(5, GetItemInfo(iteratedItemLink))
				if not ignoreLevelRequirement then
					if not level or not reqLevel or level<reqLevel then score = -1 end
				end
				if iteratedItemLink==itemMustWin and itemClass==LE_ITEM_CLASS_ARMOR and itemSubclass==armorSpecSubclass then
					mustWinGrantsArmorSpecSubclass = true
				end
				local canAdornOffHand
				local canAdornSlot = CanAdornUniqueInventorySlot(uniqueInventorySlot, itemClass, itemSubclass, itemEquipSlot, spec, level)
				if uniqueInventorySlot==INVSLOT_MAINHAND then
					canAdornOffHand = CanAdornUniqueInventorySlot(INVSLOT_OFFHAND, itemClass, itemSubclass, itemEquipSlot, spec, level)
				end
				if not canAdornSlot then
					if not canAdornOffHand then
						score = -4
					end
				end
				if not score then
					score, armorSpecStatValue = CalculateScore(iteratedItemLink, spec, pvp, level, uniqueInventorySlot, nil, enforceArmorSpecSubclass, uncapped)
				end

				if score>=0 then--pair holdable, ring and trinket slots
					if uniqueInventorySlot==INVSLOT_MAINHAND then
						if canAdornSlot and MainHandBlocksSecondarySlot(itemSubclass, itemEquipSlot, iteratedItemLink, spec, level) then
							if not twoHandScore or twoHandScore<score then
								twoHandScore=score
								twoHandWinner=iteratedItemLink
								twoHandArmorSpecStatValue=armorSpecStatValue
							end
						else
							if canAdornSlot and (not mainHandScore or mainHandScore<score) then
								if mainHandOffHandScore and (not offHandScore or offHandScore<mainHandOffHandScore) then
									offHandScore = mainHandOffHandScore
									offHandWinner = mainHandWinner
									offHandArmorSpecStatValue = mainHandArmorSpecStatValue
								end
								mainHandScore=score
								mainHandWinner=iteratedItemLink
								mainHandArmorSpecStatValue=armorSpecStatValue
								if canAdornOffHand then
									mainHandOffHandScore = CalculateScore(iteratedItemLink, spec, pvp, level, INVSLOT_OFFHAND, nil, enforceArmorSpecSubclass, uncapped)
								end
							elseif canAdornOffHand then
								score = CalculateScore(iteratedItemLink, spec, pvp, level, INVSLOT_OFFHAND, nil, enforceArmorSpecSubclass, uncapped)
								if not offHandScore or offHandScore<score then
									offHandScore = score
									offHandWinner = iteratedItemLink
									offHandArmorSpecStatValue = armorSpecStatValue
								end
							end
						end
					else
						local iteratedItemWonTieBreaker
						if score==winScore then
							local winnerTieBreaker = CalculateScore(winner, spec, pvp, level, uniqueInventorySlot, true, enforceArmorSpecSubclass, uncapped)
							local iteratedItemTieBreaker = CalculateScore(iteratedItemLink, spec, pvp, level, uniqueInventorySlot, true, enforceArmorSpecSubclass, uncapped)
							if iteratedItemTieBreaker>winnerTieBreaker then

								iteratedItemWonTieBreaker = true
							end
						end
						if not winScore then
							winScore = score
							winner = iteratedItemLink
							winArmorSpecStatValue = armorSpecStatValue
							winnerGrantsArmorSpecSubclass = itemClass==LE_ITEM_CLASS_ARMOR and itemSubclass==armorSpecSubclass
						elseif winScore<score or iteratedItemWonTieBreaker then
							altScore = winScore
							altWinner = winner
							altArmorSpecStatValue = winArmorSpecStatValue
							winScore = score
							winner = iteratedItemLink
							winArmorSpecStatValue = armorSpecStatValue
							winnerGrantsArmorSpecSubclass = itemClass==LE_ITEM_CLASS_ARMOR and itemSubclass==armorSpecSubclass
						elseif not altScore or altScore<score then
							altScore = score
							altWinner = iteratedItemLink
							altArmorSpecStatValue = armorSpecStatValue
						end
					end
				end
			end
			if
				itemMustWin
				and (winner~=itemMustWin and ((not mustWinGrantsArmorSpecSubclass) or winnerGrantsArmorSpecSubclass))
				and altWinner~=itemMustWin
				and mainHandWinner~=itemMustWin
				and offHandWinner~=itemMustWin
				and twoHandWinner~=itemMustWin
			then
				control:Pool()
				return
			end
			YieldAutoroutine()
		end
		if uniqueInventorySlot~="INVTYPE_FINGER" and uniqueInventorySlot~="INVTYPE_TRINKET" then
			altScore = nil
			altWinner = nil
			altArmorSpecStatValue = nil
		end
		if uniqueInventorySlot==INVSLOT_MAINHAND then
			offHandScore = offHandScore or 0
			mainHandScore = mainHandScore or -1
			twoHandScore = twoHandScore or -1
			if mainHandScore+offHandScore>=twoHandScore then
				if
					itemMustWin
					and mainHandWinner~=itemMustWin
					and offHandWinner~=itemMustWin
				then
					return
				end
				if DGV:UserSetting(DGV_WEAPONPREF)~="Never Swap" then
					if ShouldSwapMainOffWeapons(mainHandWinner, offHandWinner, spec, level) then
						winner, winScore, altWinner, altScore = offHandWinner, mainHandScore+offHandScore, mainHandWinner, mainHandScore
						winArmorSpecStatValue, altArmorSpecStatValue = offHandArmorSpecStatValue, mainHandArmorSpecStatValue
					elseif not ItemIsBannedWeapon(mainHandWinner) and not ItemIsBannedWeapon(offHandWinner) then
						winner, winScore, altWinner, altScore = mainHandWinner, mainHandScore+offHandScore, offHandWinner, offHandScore
						winArmorSpecStatValue, altArmorSpecStatValue = mainHandArmorSpecStatValue, offHandArmorSpecStatValue
					end
				end
			else
				if
					itemMustWin
					and twoHandWinner~=itemMustWin
				then
					return
				end
				winner, winScore, winArmorSpecStatValue = twoHandWinner, twoHandScore, twoHandArmorSpecStatValue
			end
		end
		local invSlot = UniqueInventoryToInvSlot(uniqueInventorySlot)
		if
			enforceArmorSpecSubclass==nil and
			not winnerGrantsArmorSpecSubclass and
			IsArmorSpecSlot(invSlot)
		then
			local standardTable = GA:GetSpecDataTable(spec, pvp, nil, false, uncapped):BindToAutoroutineLifetime(UnbindSpecDataTable)
--DGV:DebugFormat("GetCurrentBestInSlot", "standardTable", tostring(standardTable), "standardTable:IsBoundToAutoroutineLifetime()", standardTable:IsBoundToAutoroutineLifetime())
			local enforcedTable = GA:GetSpecDataTable(spec, pvp, nil, true, uncapped):BindToAutoroutineLifetime(UnbindSpecDataTable)
			if
				IsEnforcedTableComplete(enforcedTable) and
				not StandardTableGrantsArmorSpecialization(standardTable, enforcedTable)
				and UnitStat("player", 1) --check if working... sometime this can fail during level up event.
				and ArmorSpecializationBonusWins(spec, standardTable, enforcedTable)
			then
				winner = enforcedTable[invSlot]
				local score, armorSpecStatValue = CalculateScore(winner, spec, pvp, level, uniqueInventorySlot, nil, enforceArmorSpecSubclass, uncapped)
				return winner, score, nil, nil, armorSpecStatValue
			end
		end
		if winScore and winScore>=0 then
			return winner, winScore, altWinner, altScore, (winArmorSpecStatValue or 0) + (altArmorSpecStatValue or 0)
		end
	end

	local function CalculateByScoringInfo(itemLink, spec, pvp, level, uniqueInventorySlot, itemSums, enforceArmorSpecSubclass, uncapped, forGearFinder, ...)
		local total, armorSpecStatKey, armorSpecStatValue = 0
		for i=1,select("#", ...) do
			local data = (select(i, ...))
			if enforceArmorSpecSubclass then
				enforceArmorSpecSubclass = GetArmorSpecSubclassFrom(data) or enforceArmorSpecSubclass
			end
			armorSpecStatKey = armorSpecStatKey or GetArmorSpecStat(data)
			local score, statSum = CalculateScoreForInfo(itemLink, spec, pvp, level, uniqueInventorySlot, itemSums, enforceArmorSpecSubclass, uncapped, forGearFinder, unpack(data))
            
            local itemLevel, _, _, _, _, equipSlot
            
            if forGearFinder then
                itemLevel, _, _, _, _, equipSlot = select(4, GetItemInfo_dugi(itemLink, true))
            else
                itemLevel, _, _, _, _, equipSlot = select(4, GetItemInfo(itemLink))
            end
            
			if equipSlot == "INVTYPE_TRINKET" and DGV:UserSetting(DGV_SUGGESTTRINKET) then
				if score and score>0 then
                    if not forGearFinder then
                        score = score + itemLevel
                    end
				end
			end

			if armorSpecStatKey and data[1]==armorSpecStatKey then
				armorSpecStatValue = statSum
			end
			if score then
				if score<0 then return score end
				total = total + score
			end
			YieldAutoroutine()
		end
		return total, armorSpecStatValue
	end

	CalculateScore =  function(itemLink, spec, pvp, level, uniqueInventorySlot, tiebreaker, enforceArmorSpecSubclass, uncapped)
		--uncapped = true
		local cacheKey = not tiebreaker and strformat("%s%s%d%s%d%s%s%s", "CalculateScore", itemLink, spec, pvp and "true" or "false", level, tostring(uniqueInventorySlot), enforceArmorSpecSubclass and tostring(enforceArmorSpecSubclass) or "nil", uncapped and "0" or "1")
		if cacheKey then
			local cacheReaction = TryGetCacheReaction(cacheKey)
			if cacheReaction then
--DGV:DebugFormat("CalculateScore cached", "itemLink", itemLink, "uncapped", uncapped,  "score", (cacheReaction:UnpackCache()))
				return cacheReaction:UnpackCache()
			end
		end

		local itemSums = GetItemSums(itemLink):BindToAutoroutineLifetime(tPool)
		--StatLogic:GetSum(itemLink, itemSums)
		--DGV.InitTable(itemSums)
		local levelLimit = itemSums["PLAYER_LEVEL_LIMIT"]
		if levelLimit and level>=levelLimit then
			itemSums["XP_BONUS"] = 0 -- Heirloom XP Bonus stops working after Level limit
			--itemSums:Pool()
			--return -6
		end
		local score, armorSpecStatValue
		if not tiebreaker then
			score, armorSpecStatValue = CalculateByScoringInfo(itemLink, spec, pvp, level, uniqueInventorySlot, itemSums, enforceArmorSpecSubclass, uncapped, false, GetScoringInfo(spec, pvp))
		else
			score = CalculateByScoringInfo(itemLink, spec, pvp, level, uniqueInventorySlot, itemSums, enforceArmorSpecSubclass, uncapped, false, GetTieBreakerScoringInfo())

		end
		itemSums:Pool()

		if cacheKey then
			RegisterReaction("BAG_UPDATE")
				:Or(RegisterMemberFunctionReaction("DugisGuideViewer.Modules.GearAdvisor", "ResetCalculateScoreCache"))
				:SetCache(cacheKey, score, armorSpecStatValue)
		end
--DGV:DebugFormat("CalculateScore", "itemLink", itemLink,"uncapped", uncapped, "score", score)
		return score, armorSpecStatValue
	end

    if not DugisCharacterCache.CalculateScore_cache_v9 then
        DugisCharacterCache.CalculateScore_cache_v9 = {}
    end

	CalculateScoreForGearFinder = function(itemLink, spec, pvp, level, uniqueInventorySlot, tiebreaker, enforceArmorSpecSubclass, uncapped)
		--uncapped = true
		local cacheKey = not tiebreaker and strformat("%s%s%d%s%d%s%s%s", "CalculateScore", itemLink, spec, pvp and "true" or "false", level, tostring(uniqueInventorySlot), enforceArmorSpecSubclass and tostring(enforceArmorSpecSubclass) or "nil", uncapped and "0" or "1")
		if cacheKey then
            if DugisCharacterCache.CalculateScore_cache_v9[cacheKey] then
                return unpack(DugisCharacterCache.CalculateScore_cache_v9[cacheKey])
            end

		--	local cacheReaction = TryGetCacheReaction(cacheKey)
		--	if cacheReaction then
--DGV:De--bugFormat("CalculateScore cached", "itemLink", itemLink, "uncapped", uncapped,  "score", (cacheReaction:UnpackCache()))
        --
        --        --print("X", cacheReaction:UnpackCache())
		--		return cacheReaction:UnpackCache()
		--	end
		end
         --print("NC")
		--Added parameter to make LibStatLogic back waiting until Tooltip gets loaded for scratching data. Without that fix LibStatLogic often fails.
		local itemSums = GetItemSums(itemLink, true) --:BindToAutoroutineLifetime(tPool)
		--StatLogic:GetSum(itemLink, itemSums)
		--DGV.InitTable(itemSums)
		local levelLimit = itemSums["PLAYER_LEVEL_LIMIT"]
		if levelLimit and level>=levelLimit then
			itemSums["XP_BONUS"] = 0 -- Heirloom XP Bonus stops working after Level limit
			--itemSums:Pool()
			--return -6
		end
		local score, armorSpecStatValue
		if not tiebreaker then
			score, armorSpecStatValue = CalculateByScoringInfo(itemLink, spec, pvp, level, uniqueInventorySlot, itemSums, enforceArmorSpecSubclass, uncapped, true, GetScoringInfo(spec, pvp))
		else
			score = CalculateByScoringInfo(itemLink, spec, pvp, level, uniqueInventorySlot, itemSums, enforceArmorSpecSubclass, uncapped, true, GetTieBreakerScoringInfo())

		end
		--itemSums:Pool()

		if cacheKey then
		--[[	RegisterReaction("BAG_UPDATE")
				:Or(RegisterMemberFunctionReaction("DugisGuideViewer.Modules.GearAdvisor", "ResetCalculateScoreCache"))
				:SetTargetCache(, cacheKey, score, armorSpecStatValue)
                ]]
           DugisCharacterCache.CalculateScore_cache_v9[cacheKey] = {score, armorSpecStatValue}
		end
--DGV:DebugFormat("CalculateScore", "itemLink", itemLink,"uncapped", uncapped, "score", score)
		return score, armorSpecStatValue
	end

	function GA.ResetCalculateScoreCache()
		QueueInvocation(GA.AutoEquipSmartSet, false)
	end

	local function StandardCompare(criterion, link, level)
		local uniqueSlot = GetDefaultUniqueInventorySlot(select(9, GetItemInfo(link)))
		local winningItem, winningScore, altWinner, altScore = GetCurrentBestInSlot(uniqueSlot, criterion.specNum, criterion.pvp, level, nil, nil, nil, true, link)
		return winningItem==link or altWinner==link, altWinner==link and winningItem or altWinner, winningItem==link and winningScore or altScore, winningItem==link and altScore or winningScore, uniqueSlot
	end

	local function CoinCompare(criterion, link)
		local highest, value = true, select(11, GetItemInfo(link))
		for _, choice in GA.ItemChoiceIterator do
			local choiceValue = select(11, GetItemInfo(choice))
			highest = highest and choiceValue<=value
		end
		return highest, value, value
	end

    local function GetStorylineButtonByRewardName(rewardName)
        for i = 1, 50 do
            if _G["Storyline_ItemButton"..i] and _G["Storyline_ItemButton"..i].Name:GetText() == rewardName then
                return _G["Storyline_ItemButton"..i]
            end
        end
    end

	local function StandardRewardAdorner(criterion, link, rewardFrame)
        if Storyline_NPCFrame and Storyline_NPCFrame:IsShown() then
            local storyLineRewardFrame = GetStorylineButtonByRewardName(rewardFrame.Name:GetText())
            if storyLineRewardFrame then
                rewardFrame = storyLineRewardFrame
            end
        end

		local chosenColor
		if not DugisGreenArrowRewardAdornment:IsShown() then
			chosenColor = DugisGreenArrowRewardAdornment
		elseif not DugisYellowArrowRewardAdornment:IsShown() then
			if DugisGreenArrowRewardAdornment:IsShown() then
				local _,relativeTo = DugisGreenArrowRewardAdornment:GetPoint()
				if relativeTo==rewardFrame then return end
			end
			chosenColor = DugisYellowArrowRewardAdornment
		end
		if not chosenColor then return end
		chosenColor:SetParent(rewardFrame)
		chosenColor:ClearAllPoints()
		chosenColor:SetSize(28, 28)
		chosenColor:SetPoint("TOP", rewardFrame, "BOTTOMLEFT", 30, 25)
        chosenColor:SetFrameStrata("DIALOG")
        chosenColor:SetFrameLevel(501)
		chosenColor:Show()
	end

	local function StandardLootRollAdorner(criterion, link, groupLootFrame)
		if not groupLootFrame.dugisGreenArrow then
			groupLootFrame.dugisGreenArrow = CreateFrame("Frame", nil, groupLootFrame.IconFrame, "DugisGreenArrowAdornmentTemplate")
		end
		groupLootFrame.dugisGreenArrow:ClearAllPoints()
		groupLootFrame.dugisGreenArrow:SetSize(28, 28)
		groupLootFrame.dugisGreenArrow:SetPoint("TOP", groupLootFrame.IconFrame, "BOTTOMLEFT", 30, 25)
		groupLootFrame.dugisGreenArrow.link = link
		groupLootFrame.dugisGreenArrow:Show()
	end

	local function StandardItemButtonAdorner(criterion, link, buttonFrame, color)
		local frame
		if color=="green" then
			if not buttonFrame.dugisGreenArrow then
				buttonFrame.dugisGreenArrow = CreateFrame("Frame", nil, buttonFrame, "DugisGreenArrowAdornmentTemplate")
			end
			frame = buttonFrame.dugisGreenArrow
		else
			if buttonFrame.dugisGreenArrow and buttonFrame.dugisGreenArrow:IsShown() then return end
			if not buttonFrame.dugisYellowArrow then
				buttonFrame.dugisYellowArrow = CreateFrame("Frame", nil, buttonFrame, "DugisYellowArrowAdornmentTemplate")
			end
			frame = buttonFrame.dugisYellowArrow
		end

		frame:ClearAllPoints()
		frame:SetSize(28, 28)
		frame:SetPoint("TOP", buttonFrame.icon, "BOTTOMLEFT", 30, 25)
		frame:Show()
	end

	function StandardTiebreaker(criterion, linkA, linkB, level)
		level = level or UnitLevel("player")
		local uniqueInventorySlotA = GetDefaultUniqueInventorySlot(select(9, GetItemInfo(linkA)))
		local uniqueInventorySlotB = GetDefaultUniqueInventorySlot(select(9, GetItemInfo(linkB)))

		local tieBreakerA = CalculateScore(linkA, criterion.specNum, criterion.pvp, level, uniqueInventorySlotA, true)
		local tieBreakerB = CalculateScore(linkB, criterion.specNum, criterion.pvp, level, uniqueInventorySlotB, true)
		return tieBreakerA>tieBreakerB and linkA or linkB
	end

	local function FallbackTooltipAdorner(tooltip, option)
		tooltip:AddLine(option)
	end

	local function IsQuestRewardLootRollOrEJItem(link)
		for control,iteratedItemLink in ItemIterator,ITEM_ITERATOR_SKIP_EQUIPPED do
			if link==iteratedItemLink then
				local pass = control.itemType or control.func==GetLootRollItemLink or control.func==EJ_GetLootInfoByIndex
				control:Pool()
				return pass
			end
		end
	end

	local function StandardTooltipAdorner(criterion, tooltip, link, altWinner, score, altScore)
		local color = RAID_CLASS_COLORS[select(2,UnitClass("player"))].colorStr
		if altWinner then
			tooltip:AddLine(L["Best in slot with %s - |c%s%s|r"]:format(altWinner, color, criterion.specName))
		else

			tooltip:AddLine(L["Best in slot - |c%s%s|r"]:format(color, criterion.specName))
		end
		--if IsQuestRewardLootRollOrEJItem(link) then
			local uniqueInventorySlot = GetDefaultUniqueInventorySlot(select(9, GetItemInfo(link)))
			if uniqueInventorySlot ~= "INVTYPE_TRINKET" and not DGV:UserSetting(DGV_SUGGESTTRINKET) then
				local currentWinningItem, currentWinningScore, currentAltWinner, currentAltScore = GetCurrentBestInSlot(uniqueInventorySlot, criterion.specNum, criterion.pvp, nil, ITEM_ITERATOR_SKIP_ALL_EXTERNAL)
				if not currentWinningItem or currentWinningScore==0 or score==0 then return end
				local upgrade, upgradeOver, upgradeOverAlt
				if altWinner==currentWinningItem then
					if not currentAltScore then return end
					upgradeOver, upgrade, upgradeOverAlt  = currentAltWinner, (1-currentAltScore/score)*100, currentWinningItem
				else
					upgradeOver, upgrade, upgradeOverAlt = currentWinningItem, (1-currentWinningScore/score)*100, currentAltWinner
				end
	--DGV:DebugFormat("StandardTooltipAdorner", "link", link, "altWinner", altWinner, "currentWinningItem",currentWinningItem, "currentAltWinner", currentAltWinner, "upgrade", upgrade, "currentWinningScore", currentWinningScore, "score", score, "altScore", altScore, "currentAltScore", currentAltScore)
				if upgradeOverAlt and upgradeOver and upgrade > 0 then
					tooltip:AddLine(L["|TInterface\\AddOns\\DugisGuideViewerZ\\Artwork\\UpgradeArrow:0|t|cff1eff00+%d%%|r upgrade over %s with %s"]:format(upgrade, upgradeOver, upgradeOverAlt))
				elseif upgradeOver and upgrade > 0 then

					tooltip:AddLine(L["|TInterface\\AddOns\\DugisGuideViewerZ\\Artwork\\UpgradeArrow:0|t|cff1eff00+%d%%|r upgrade over %s"]:format(upgrade, upgradeOver))
				end
			end
		--end
	end

	local function CoinRewardAdorner(criterion, link, rewardFrame)
        if Storyline_NPCFrame and Storyline_NPCFrame:IsShown() then
            local storyLineRewardFrame = GetStorylineButtonByRewardName(rewardFrame.Name:GetText())
            if storyLineRewardFrame then
                rewardFrame = storyLineRewardFrame
            end
        end

		DugisCoinRewardAdornment:ClearAllPoints()
		DugisCoinRewardAdornment:SetParent(rewardFrame)
		--DugisCoinRewardAdornment:SetFrameLevel(129)
		DugisCoinRewardAdornment:SetSize(35, 35)
		DugisCoinRewardAdornment:SetPoint("TOPRIGHT", rewardFrame, 5, 9)

        DugisCoinRewardAdornment:SetFrameStrata("DIALOG")
        DugisCoinRewardAdornment:SetFrameLevel(501)
		DugisCoinRewardAdornment:Show()
	end

	local function SpecFromLocalizedName(name)
		for specNum=1,GetNumSpecializations() do
			local specName = select(2,GetSpecializationInfo(specNum))
			if specName==name then
				return specNum
			end
		end
	end

	function IsEquipment(equipLoc)
		if invType=="" then return end
		local uniqueInventorySlot = GetDefaultUniqueInventorySlot(equipLoc)
		return
			uniqueInventorySlot=="INVTYPE_HEAD" or
			uniqueInventorySlot=="INVTYPE_NECK" or
			uniqueInventorySlot=="INVTYPE_SHOULDER" or
			uniqueInventorySlot=="INVTYPE_CHEST" or
			uniqueInventorySlot=="INVTYPE_WAIST" or
			uniqueInventorySlot=="INVTYPE_LEGS" or
			uniqueInventorySlot=="INVTYPE_FEET" or
			uniqueInventorySlot=="INVTYPE_WRIST" or
			uniqueInventorySlot=="INVTYPE_HAND" or
			uniqueInventorySlot=="INVTYPE_FINGER" or
			(uniqueInventorySlot=="INVTYPE_TRINKET" and DGV:UserSetting(DGV_SUGGESTTRINKET)) or
			uniqueInventorySlot=="INVTYPE_CLOAK" or
			uniqueInventorySlot==INVSLOT_MAINHAND or
			uniqueInventorySlot==INVSLOT_OFFHAND
	end

	local function StandardPredicate(criterion, link)
		return IsEquipment((select(9, GetItemInfo(link))))
	end

	local function CoinPredicate(criterion, item)
		for _, choice in GA.ItemChoiceIterator do
			if choice==item then return true end
		end
		return false
	end

	local WIN_CRITERIA_CURRENT = "Active Talent Specialization"
	local WIN_CRITERIA_INACTIVE_SPEC = "Inactive Talent Specialization"
	local WIN_CRITERIA_NONE = "None"
	local function SpecFromOption(option)
		if option==WIN_CRITERIA_NONE then return end
		local activeSpecName = select(2, GetSpecializationInfo(GetSpecialization()))
		local inactiveSpec = GetSpecialization(false, false, GetActiveSpecGroup()==1 and 2 or 1)
		local inactiveSpecName
		if inactiveSpec then
			inactiveSpecName = select(2, GetSpecializationInfo(inactiveSpec))
		end
		local pvp
		local capture = option:match(L["(.*) %(PvP%)"])

		if capture then
			pvp = true
		end
		if option==WIN_CRITERIA_CURRENT or capture==WIN_CRITERIA_CURRENT then
				return SpecFromLocalizedName(activeSpecName), pvp and L["%s (PvP)"]:format(activeSpecName) or activeSpecName, pvp
		elseif option==WIN_CRITERIA_INACTIVE_SPEC or capture==WIN_CRITERIA_INACTIVE_SPEC then
			if inactiveSpecName then
				return SpecFromLocalizedName(inactiveSpecName), pvp and L["%s (PvP)"]:format(inactiveSpecName) or inactiveSpecName, pvp
			end
		else
			local capture = option:match(L["(.*) %(PvP%)"])
			if capture then
				return SpecFromLocalizedName(capture), option, true
			end
			return SpecFromLocalizedName(option), option
		end
	end

	local WIN_CRITERIA_COIN = "Highest Vendor Price"
	local function WinCriteriaIterator(invariant, control)
		if not control then control = 0 end

		while true do
			control = control + 1
			local option = invariant[control]
			if not option then return end
			local spec, specName, pvp
			if option==WIN_CRITERIA_COIN then
				local criterion = GetCreateTable()
				criterion.Predicate = CoinPredicate
				criterion.GetScore = CoinCompare
				criterion.AdornReward = CoinRewardAdorner
				return control, WIN_CRITERIA_COIN, criterion
			else
				spec, specName, pvp = SpecFromOption(option)
			end
			local alreadyIterated
			if spec then
				for i=1,control-1 do
					local sfo, option, pfo = SpecFromOption(invariant[i])
					if spec==sfo and pvp==pfo then
						alreadyIterated = true
						break
					end
				end
			end
			if spec and not alreadyIterated then
				local criterion = GetCreateTable()
				criterion.specNum = spec
				criterion.pvp = pvp
				criterion.specName = specName
				criterion.Predicate = StandardPredicate
				criterion.GetScore = StandardCompare
				criterion.AdornReward = StandardRewardAdorner
				criterion.AdornLootRoll = StandardLootRollAdorner
				criterion.AdornEncounterJournal = StandardItemButtonAdorner
				criterion.AdornVendorItem = StandardItemButtonAdorner
				criterion.AdornLoot = StandardItemButtonAdorner
				criterion.SettleTie = StandardTiebreaker
				criterion.AdornTooltip = StandardTooltipAdorner
				return control, specName, criterion
			end
		end
	end

	function GA:IterateWinCriteria()
		return WinCriteriaIterator, DugisGuideViewer.chardb[DGV_GAWINCRITERIACUSTOM].options
	end

	local function Repaint(tooltip)
		tooltip:Show()
		local tipTextLeft = tooltip:GetName().."TextLeft"
		for i = 2, tooltip:NumLines() do
			local fontString = _G[tipTextLeft..i]
			_, relativeTo, _, xOfs, _ = fontString:GetPoint(0)
			fontString:ClearAllPoints()
			fontString:SetPoint("TOPLEFT", relativeTo, "BOTTOMLEFT", xOfs, -2)
		end
	end

	local function ItemIsInBag(link)
		for control,iteratedItemLink in ItemIterator,ITEM_ITERATOR_SKIP_ALL_EXTERNAL do
			if iteratedItemLink==link and control.bags then
				control:Pool()
				return true
			end
		end
	end

	local function IsNewItemTooltip(scores, link, newLink)
		if newLink~=link then
			return true
		end
	end

	local function PoolScores(scores)
		if scores then
			scores:TryReleaseLifetime(PoolScores)
			for _,score in scores:IPairs() do
				if type(score)=="table" then
					score:Pool()
				end
			end
			scores:Pool()
		end
	end

	local function ProcessTooltipRoutine(tooltip, link)
		local cacheReaction = TryGetCacheReaction("ProcessTooltipRoutine")
		local scores
		if not cacheReaction then
			local reqLevel = select(5, GetItemInfo(link))
			local playerLevel = UnitLevel("player")
			if reqLevel and playerLevel and reqLevel>playerLevel and ItemIsInBag(link) then return end
			for index, option,criterion in GA:IterateWinCriteria() do
				criterion:BindToAutoroutineLifetime(tPool)
				local score, altWinner, altScore = EvaluateWinCriteron(criterion, link)
				if score then
					if not scores then
						scores = GetCreateTable():BindToAutoroutineLifetime(PoolScores)
					end
					if altWinner then
						scores[index] = GetCreateTable(score, altWinner, altScore)
					else
						scores[index] = score
					end
				end
				criterion:Pool()
			end
			if scores then
				scores:TryReleaseLifetime(PoolScores)
			end
			RegisterReaction()
				:InvokePassively()
				:WithAction(PoolScores, scores)
				:WithPredicate(IsNewItemTooltip, link)
				:SetCache("ProcessTooltipRoutine", scores)
		else
			scores = cacheReaction:UnpackCache()
		end
		if not scores then return end
		for index, option,criterion in GA:IterateWinCriteria() do
			criterion:BindToAutoroutineLifetime(tPool)
			local score, altWinner, altScore = scores[index]
			if score then
				if type(score)=="table" then
					score, altWinner, altScore = score:Unpack()
				end
				if criterion.AdornTooltip then
					criterion:AdornTooltip(tooltip, link, altWinner, score, altScore)
				else
					FallbackTooltipAdorner(tooltip, option)
				end
			end
			criterion:Pool()
		end

		Repaint(tooltip)
	end

	function ProcessTooltip(tooltip, name, link, ...)
		if tooltip:GetName():match("ShoppingTooltip") then return end

		local cacheReaction = TryGetCacheReaction("ProcessTooltipRoutine")
		if cacheReaction then
			cacheReaction:TryInvoke(link)
			cacheReaction = TryGetCacheReaction("ProcessTooltipRoutine")
		end
		if not cacheReaction then
			if not GetRunningAutoroutine("ProcessTooltipRoutine") then
				BeginAutoroutine("ProcessTooltipRoutine", ProcessTooltipRoutine, tooltip, link)
			end
		else
			ProcessTooltipRoutine(tooltip, link)
		end
	end

	local orig_GetNumEquipmentSets = GetNumEquipmentSets
	local orig_GetEquipmentSetInfo = GetEquipmentSetInfo
	local orig_GetEquipmentSetInfoByName = GetEquipmentSetInfoByName
	local orig_GetEquipmentSetItemIDs = GetEquipmentSetItemIDs
	local equipmentSetDataTable = {}
	local equipmentSetIdTable = {}
	setmetatable(equipmentSetIdTable, {
		__index = function(self, i)
			local link = rawget(equipmentSetDataTable, i)
			if i==INVSLOT_RANGED then return 0 end
			if i==INVSLOT_BODY or i==INVSLOT_TABARD then return EQUIPMENT_SET_IGNORED_SLOT end
			return link and DGV:GetItemIdFromLink(link) or EQUIPMENT_SET_IGNORED_SLOT, link
		end,
	})

	local bagUpdateReaction, levelUpReaction, activeSpecReaction, smartSetReaction, rewardShowReaction, questHideReaction, adornerParentShowReaction, equipExecutedReaction,
		lootRollShowReaction, encounterJournalUpdateReaction, vendorUpdateReaction, lootUpdateReaction
	function GA:Load()
		TipHooker:Hook(ProcessTooltip, "item")
		TipHooker:RegisterCustomTooltip("item", "WorldMapTooltip")

        --Outfitter bugfix
        if Outfitter then
            hooksecurefunc(Outfitter, "Item_CheckboxClicked", function()
                GA.lastOutfitterClickedTime = GetTime()
            end)
        end

		local function SmartSetShown()
			if DugisGuideViewer.armoryloaded or DugisGuideViewer.outfitterloaded or DugisGuideViewer.arkinventoryloaded then
				return false
			else
				return DGV:UserSetting(DGV_GASMARTSETTARGET)~=WIN_CRITERIA_NONE
			end
		end

		function GetNumEquipmentSets()
            if isCalledByBag_nonAddon() then
                return orig_GetNumEquipmentSets()
            end

			local add = SmartSetShown() and 1 or 0
			return orig_GetNumEquipmentSets()+add
		end

		local function IsInOtherSlot(itemId, slot)
			local otherSlot
			if slot==INVSLOT_TRINKET1 then
				otherSlot = INVSLOT_TRINKET2
			elseif slot==INVSLOT_TRINKET2 then
				otherSlot = INVSLOT_TRINKET1
			elseif slot==INVSLOT_FINGER1 then
				otherSlot = INVSLOT_FINGER2
			elseif slot==INVSLOT_FINGER2 then
				otherSlot = INVSLOT_FINGER1
			else return
			end
			local link = GetInventoryItemLink("player", otherSlot)
			return link and DGV:GetItemIdFromLink(link)==itemId
		end

		local function SmartSetIsEquipped(cachedOnly)
			local result = GA:GetSpecDataTable(nil, nil, equipmentSetDataTable, nil, nil, cachedOnly)
			if not result and cachedOnly and not GetRunningAutoroutine("CacheEquipmentSetDataTableRoutine") then
				BeginAutoroutine("CacheEquipmentSetDataTableRoutine", GA.GetSpecDataTable, GA, nil, nil, equipmentSetDataTable):OnCompletion(PaperDollEquipmentManagerPane_Update)
				return
			end
			for slot,link in nextValidSlot, true do
				local setLink = equipmentSetDataTable[slot]
				local setId = equipmentSetIdTable[slot]
				local id = link and DGV:GetItemIdFromLink(link)
				if
					setLink and
					(not link or setId~=id) and
					not IsInOtherSlot(setId, slot)
				then
					return false
				end
			end
			return true
		end

		local function GetSpecIcon()
			return (select(4, GetSpecializationInfo((SpecFromOption(DGV:UserSetting(DGV_GASMARTSETTARGET))))))
		end

		-- name, icon, setID, isEquipped, numItems, numEquipped, numInventory, numMissing, numIgnored = GetEquipmentSetInfo(index)
		-- Arguments:
		-- index - Index of an equipment set (between 1 and GetNumEquipmentSets()) (number)
		-- Returns:
		-- name - Name of the equipment set (string)
		-- icon - Path to an icon texture for the equipment set (string)
		-- setID - Internal ID number for the set (not used elsewhere in API) (number)
		-- isEquipped - If the set is equipped returns true, if not, false (boolean)
		-- numItems - Number of items in the set (number)
		-- numEquipped - Number of items in the set currently equipped (number)
		-- numInventory - Number of items from the set in current bags (number)
		-- numMissing - Number of items missing from the set (current bags) (number)
		-- numIgnored - Number of ignored slots (number)
		function GetEquipmentSetInfo(index)
            if isCalledByBag_nonAddon() then
                return orig_GetEquipmentSetInfo(index)
            end

			if index==1 and SmartSetShown() then
				local cacheReaction = TryGetCacheReaction("GetEquipmentSetInfo") --keep a cache until next update.  Some addons call this a lot.
				if cacheReaction then
					return cacheReaction:UnpackCache()
				end

				local isEquipped = SmartSetIsEquipped(true)
				return RegisterStopwatchReaction(0):SetCache("GetEquipmentSetInfo",
					L["Dugi Smart Set"],
					GetSpecIcon(),
					MAX_EQUIPMENT_SETS_PER_PLAYER,
					isEquipped,
					16,
					isEquipped and 16 or 0,
					isEquipped and 0 or 16,
					0,
					2):UnpackCache()
			else
				return orig_GetEquipmentSetInfo(SmartSetShown() and index-1 or index)
			end
		end

		-- Returns information about an equipment set
		-- See also Equipment Manager functions.
		-- Signature:
		-- icon, setID, isEquipped, numItems, numEquipped, unknown, numMissing, numIgnored = GetEquipmentSetInfoByName("name")
		-- Arguments:
		-- name - Name of an equipment set (case sensitive) (string)
		-- Returns:
		-- icon - Path to an icon texture for the equipment set (string)
		-- setID - Internal ID number for the set (not used elsewhere in API) (number)
		-- isEquipped - If the set is equipped returns true, if not, false (boolean)
		-- numItems - Number of items in the set (number)
		-- numEquipped - Number of items in the set currently equipped (number)
		-- unknown - Unknown, always seem to be 0 (number)
		-- numMissing - Number of items missing from the set (current bags) (number)
		-- numIgnored - Number of ignored slots (number)
		function GetEquipmentSetInfoByName(name)
            if isCalledByBag_nonAddon() then
                return orig_GetEquipmentSetInfoByName(name)
            end

			if name==L["Dugi Smart Set"] and SmartSetShown() then
				local isEquipped = SmartSetIsEquipped(true)
--DGV:DebugFormat("GetEquipmentSetInfoByName", "isEquipped", isEquipped)
				return GetSpecIcon(), MAX_EQUIPMENT_SETS_PER_PLAYER, isEquipped, 16, isEquipped and 16 or 0, 0, 0, 2
			else
				return orig_GetEquipmentSetInfoByName(name)
			end
		end

		local function GetItemForTable(uniqueInventorySlot, spec, pvp, enforceArmorSpecSubclass, uncapped)
			local sfo, option, pfo = SpecFromOption(DGV:UserSetting(DGV_GASMARTSETTARGET))
			spec = spec or sfo
			pvp = pvp or pfo
			local winner, winScore, altWinner, altScore, armorSpecStatValue = GetCurrentBestInSlot(uniqueInventorySlot, spec, pvp, nil, ITEM_ITERATOR_SKIP_ALL_EXTERNAL, enforceArmorSpecSubclass, uncapped)
			return winner, altWinner, armorSpecStatValue, (winScore or 0)+(altScore or 0)
		end

		local function PoolCachedDataTable(reaction, ...)
			local dataTable = reaction:UnpackCache()
			if not dataTable:IsBoundToAutoroutineLifetime() then
--DGV:DebugFormat("PoolCachedDataTable", "dataTable", tostring(dataTable))
				dataTable:Pool()
				return
			end
--DGV:DebugFormat("PoolCachedDataTable cacheInValid")
			dataTable.cacheInValid = true

		end

		function GA:GetSpecDataTable(spec, pvp, preferredTable, enforceArmorSpecSubclass, uncapped, cachedOnly)
--DGV:DebugFormat("GetSpecDataTable 1", "spec", spec)
			local sfo, option, pfo = SpecFromOption(DGV:UserSetting(DGV_GASMARTSETTARGET))
			spec = spec or sfo
			pvp = pvp or pfo
			local cacheKey = strformat("%s%d%s%s%s%s", "GetSpecDataTable", spec, pvp and "true" or "false", tostring(preferredTable), tostring(enforceArmorSpecSubclass), uncapped and "true" or "false")
			local cacheReaction = TryGetCacheReaction(cacheKey)
			if cacheReaction then
				return cacheReaction:UnpackCache()
			end
			if cachedOnly then return end
			local dataTable = preferredTable and wipe(preferredTable) or GetCreateTable()
--DGV:DebugFormat("GetSpecDataTable", "dataTable", tostring(dataTable))
			dataTable.ArmorSpecStatTotal , dataTable.ScoreTotal = 0,0
			for unique,inv1,inv2 in NextUniqueInventorySlot do
				local winner, altWinner, armorSpecStatValue, score = GetItemForTable(unique, spec, pvp, enforceArmorSpecSubclass, uncapped)
				dataTable.ArmorSpecStatTotal = dataTable.ArmorSpecStatTotal + (armorSpecStatValue or 0)
				dataTable.ScoreTotal = dataTable.ScoreTotal + score
				dataTable[inv1] = winner
				if inv2 then
					dataTable[inv2] = altWinner
				end
			end

			local reaction = RegisterStopwatchReaction(2):Or(RegisterReaction("BAG_UPDATE")):SetCache(cacheKey, dataTable)
			if not preferredTable then
				reaction:WithAction(PoolCachedDataTable)
			end
			return dataTable
		end

        --Underscore in the function name is used to avoid detecting "Bagnon" in the stacktrace the test function name
        function isCalledByBag_nonAddon()
            local stack = debugstack()
            local isCalledByBagnonAddon = (string.find(stack, "Bagnon") ~= nil)
            return  isCalledByBagnonAddon
        end

		-- Returns a table listing the items in an equipment set
		-- See also Equipment Manager functions.
		-- Signature:
		-- itemIDs = GetEquipmentSetItemIDs("name")
		-- Arguments:
		-- name - Name of an equipment set (case sensitive) (string)
		-- Returns:
		-- itemIDs - A table listing the itemIDs of the set's contents, keyed by inventoryID (table)
		--EQUIPMENT_SET_IGNORED_SLOT
		function GetEquipmentSetItemIDs(name)
            if isCalledByBag_nonAddon() then
                return orig_GetEquipmentSetItemIDs(name)
            end

			if name==L["Dugi Smart Set"] then
				GA:GetSpecDataTable(nil, nil, equipmentSetDataTable)
				return equipmentSetIdTable
			else
				return orig_GetEquipmentSetItemIDs(name)
			end
		end

		function PaperDollFrame_IgnoreSlotsForSet (setName)
			local set = GetEquipmentSetItemIDs(setName);
			for slot, item in ipairs(set) do
				if ( item == EQUIPMENT_SET_IGNORED_SLOT ) then
					EquipmentManagerIgnoreSlotForSave(slot);
					itemSlotButtons[slot].ignored = true;
					PaperDollItemSlotButton_Update(itemSlotButtons[slot]);
				end
			end
		end

		PaperDollEquipmentManagerPane:HookScript("OnUpdate", function ()
			if GA.loaded then
				for i = 1, #PaperDollEquipmentManagerPane.buttons do
					local button = PaperDollEquipmentManagerPane.buttons[i]
					if button.name==L["Dugi Smart Set"] then
						button.DeleteButton:Hide()
						button.EditButton:Hide()
					end
				end
				if PaperDollEquipmentManagerPane.selectedSetName==L["Dugi Smart Set"] then
					PaperDollEquipmentManagerPaneSaveSet:Disable()
				end
			end
		end)

		local function RunAdditionalAction(action)
			EquipmentManager_RunAction(action)
			action:Pool()
			--ClearCursor() --was preventing BoE equip
		end

		local function EquipmentChangedPredicate(reaction, event, slotOrElapsed, hasItem, ...)
			--DGV:DebugFormat("EquipmentChangedPredicate", "event", event, "slotOrElapsed", slotOrElapsed, "hasItem", hasItem, "reaction.requestedSlots", reaction.requestedSlots)
			if event=="time" and reaction.requestedSlots then
				return true
			elseif event=="PLAYER_EQUIPMENT_CHANGED" and hasItem and reaction.requestedSlots then
				reaction.requestedSlots:RemoveFirst(slotOrElapsed)
				return #reaction.requestedSlots==0
			end
		end

		local function EquipmentChangedAction(reaction, event, slotOrElapsed, hasItem, ...)
			if equipExecutedReaction.requestedSlots then
				equipExecutedReaction.requestedSlots:Pool()
				equipExecutedReaction.requestedSlots = nil
			end
			equipExecutedReaction:Dispose()
			equipExecutedReaction = nil
		end

		local EQUIP_ITEM, UNEQUIP_ITEM, SWAP_ITEM = 1,2,3
		local function ExecuteEquip(slot, action)
			ClearCursor()
			if (slot==INVSLOT_MAINHAND or slot==INVSLOT_OFFHAND) and action.player and not action.bags then --we may not be able to swap weapons in place
				if slot==action.slot then return end --already holding
				local requestedHand = slot
				action.type = UNEQUIP_ITEM
				action.invSlot = slot;
				EquipmentManager_RunAction(action)
				ClearCursor()
				local additionalAction = GetCreateTable()
				additionalAction.player = true
				additionalAction.slot = action.slot
				additionalAction.invSlot = slot
				additionalAction.type = EQUIP_ITEM
				QueueInvocation(RunAdditionalAction, additionalAction)
				return
			else
				action.type = (GetInventoryItemID("player", slot) and SWAP_ITEM) or EQUIP_ITEM;
				action.invSlot = slot;
				EquipmentManager_RunAction(action)
				--ClearCursor() --was preventing BoE equip
			end
			if not equipExecutedReaction then
				equipExecutedReaction = RegisterReaction("PLAYER_EQUIPMENT_CHANGED")
					:Or(RegisterStopwatchReaction(5))
					:WithPredicate(EquipmentChangedPredicate)
					:WithAction(EquipmentChangedAction)
			end
			if not equipExecutedReaction.requestedSlots then
				equipExecutedReaction.requestedSlots = GetCreateTable()
			end
			equipExecutedReaction:Reset()
			equipExecutedReaction.requestedSlots:Insert(slot)
		end

		local function IsSecondSlot(slot)
			return slot==INVSLOT_OFFHAND or slot==INVSLOT_FINGER2 or slot==INVSLOT_TRINKET2
		end

		local function GetCopyEquipAction(action, copy)
			local copy = copy or GetCreateTable()
			copy.slot = action.slot
			copy.bag = action.bag
			copy.player = action.player
			copy.bank = action.bank
			copy.bags = action.bags
			return copy
		end

		local function FindEquip(slot, itemLink, skip)
			local lastMatch
			for control,iteratedItemLink in ItemIterator,skip do
				if itemLink==iteratedItemLink then
					if IsSecondSlot(slot) then
						lastMatch = GetCopyEquipAction(control, lastMatch)
					else
						ExecuteEquip(slot, control)
						control:Pool()
						return true
					end
				end
			end
			if lastMatch then
				ExecuteEquip(slot, lastMatch)
				lastMatch:Pool()
				return true
			end
		end

		local function Equip(slot, itemLink)
			if itemLink~=nil and not ItemIsBanned(itemLink) then
				if not FindEquip(slot, itemLink, bit.bor(ITEM_ITERATOR_SKIP_EQUIPPED, ITEM_ITERATOR_SKIP_ALL_EXTERNAL)) then
					FindEquip(slot, itemLink, ITEM_ITERATOR_SKIP_ALL_EXTERNAL)
				end
			end
		end

		local function ContinueEquipRoutine(showPrompt, continueFromSlot)
			if SmartSetIsEquipped() then return end
			GA:GetSpecDataTable(nil, nil, equipmentSetDataTable)
			local diff = GetCreateTable()
			for slot,itemLink in next,equipmentSetDataTable,equipmentSetDataTable[continueFromSlot] and continueFromSlot do
				if type(slot)=="number" then
					local currentItemLink = GetInventoryItemLink("player", slot)
					local currentItemId = currentItemLink and DGV:GetItemIdFromLink(currentItemLink)
					local id = itemLink and DGV:GetItemIdFromLink(itemLink)
					if itemLink and id~=currentItemId and not IsInOtherSlot(id, slot) then
						diff:Insert(slot)
					end
				end
			end
			local continue, showPrompt, remaining = nil, showPrompt, #diff
			for _,slot in diff:IPairs() do
				local itemLink = equipmentSetDataTable[slot]
				continue, showPrompt, remaining = GA:Equip(slot,itemLink,showPrompt,remaining)
				if not continue then break end
			end
			diff:Pool()
		end

		local function ContinueEquip(showPrompt, continueFromSlot)
			if GetRunningAutoroutine("ContinueEquipRoutine") then return end
			QueueAutoroutine("ContinueEquipRoutine", ContinueEquipRoutine, showPrompt, continueFromSlot)
		end

		local function ClearCompareLines()
			if not DugisEquipPromptFrame.compare then return end
			for _,fontString in ipairs(DugisEquipPromptFrame.compare) do
				fontString:Hide()
			end
		end

		local function AddSetCompareLine(text, r, g, b, a)
			if not DugisEquipPromptFrame.compare then DugisEquipPromptFrame.compare = {} end
			local toSet
			for _,fontString in ipairs(DugisEquipPromptFrame.compare) do
				if not fontString:IsShown() then
					toSet = fontString
					break
				end
			end
			if not toSet then
				toSet = DugisEquipPromptFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
				toSet:SetJustifyH("LEFT")
				toSet:SetJustifyV("TOP")
				tinsert(DugisEquipPromptFrame.compare, toSet)
				if #DugisEquipPromptFrame.compare==1 then
					toSet:SetPoint("TOPLEFT", DugisEquipPromptFrame.recommended, "TOPRIGHT", 15, 0)
				else
					toSet:SetPoint("TOPLEFT", DugisEquipPromptFrame.compare[#DugisEquipPromptFrame.compare-1], "BOTTOMLEFT")
				end
			end
			toSet:SetHeight(13)
			toSet:SetWidth(1000)
			toSet:SetText(text)
			local width = toSet:GetStringWidth()
			if width>170 then
				toSet:SetHeight(13+math.floor(width/160)*13)
			end
			if r then
				toSet:SetTextColor(r,g,b)
			end
			toSet:SetWidth(170)
			toSet:Show()
		end

		function GA:GetSlotBackgroundInfo(slot)
			local slotFrame = ListContains(slot,
				function(frame)
					return (GetInventorySlotInfo(strsub(frame:GetName(),10)));
				end,
				PaperDollItemsFrame:GetChildren())
			return _G[strupper(strsub(slotFrame:GetName(), 10))], slotFrame.backgroundTextureName
		end

		local tempIgnoreCache = {}
		function GA:Equip(slot, itemLink, showPrompt, remaining)
			if tContains(tempIgnoreCache, itemLink..slot) then return end --avoids nagging over and over for the same piece in the same slot without long term ban listing (reset by manual gear set equip or reload ui)
			local currentItemLink = GetInventoryItemLink("player", slot)
			remaining = remaining and remaining-1
			if currentItemLink==itemLink then
				return true, showPrompt, remaining
			end
			if --[[not currentItemLink or]] not showPrompt or not DGV:UserSetting(DGV_SHOWAUTOEQUIPPROMPT) then --commented to show prompt if slot is empty
				Equip(slot, itemLink)
				return true, showPrompt, remaining
			else
                DugisEquipPromptFrame.slot = slot
				DugisEquipPromptFrame.recommended.item = itemLink
				DugisEquipPromptFrame.recommended.title = L["Equip recommended item:"]
				DugisEquipPromptFrame.action = "EQUIP"
				DugisEquipItemHighlight:SetPoint("TOPLEFT", DugisEquipPromptFrame.recommended.itemButton, "TOPLEFT", -8, 7)
				DugisEquipItemHighlight:Show()

				DugisEquipPromptFrame.forAll:Enable()
				DugisEquipPromptFrame.forAll.text:SetTextColor(1.0, 0.82, 0)
				if remaining and remaining>0 then
					DugisEquipPromptFrame.forAll:SetChecked(false)
					DugisEquipPromptFrame.forAll.text:SetText(L["Do above for remaining %d items"]:format(remaining))
					DugisEquipPromptFrame.forAll:Show()
				else
					DugisEquipPromptFrame.forAll:Hide()
				end
				DugisEquipPromptFrame.blacklist:SetChecked(false)
				DugisEquipPromptFrame.blacklist.text:SetText(L["Add %s to ban list"]:format(itemLink))
				DugisEquipPromptFrame.blacklist.text:SetWidth(352)
				DugisEquipPromptFrame.blacklist.text:SetJustifyH("LEFT")

				ClearCompareLines()
				local statTable = GetCreateTable()
				if currentItemLink then
					DugisEquipPromptFrame.existing.item = currentItemLink
					DugisEquipPromptFrame.existing.title = L["Or keep equipped item:"]
					GetItemStatDelta(itemLink, currentItemLink, statTable)
					AddSetCompareLine(ITEM_DELTA_DESCRIPTION)
				else
					DugisEquipPromptFrame.existing.item = nil
					DugisEquipPromptFrame.existing.title = L["Or leave slot empty:"]
					GetItemStats(itemLink, statTable)
					AddSetCompareLine(L["Item has the following stats:"])
				end
				DugisGuideViewer:HideLargeWindow()
				DugisEquipPromptFrame:Show()
				DugisEquipPromptFrame.compare[1]:Hide()
				for stat, value in pairs(statTable) do
					if stat~="n" and _G[stat] and type(value)=="number" then
						DugisEquipPromptFrame.compare[1]:Show()
						local color = "ff00ff00"
						if value < 0 then
							color = "ffff2020"
						end
						if mod(value, 1)==0 then
							AddSetCompareLine(L["|c%s%d|r %s"]:format(color, value, _G[stat]), 1, 1, 1) --Localization: enUS uses number-space-statname
						else
							AddSetCompareLine(L["|c%s%.1f|r %s"]:format(color, value, _G[stat]), 1, 1, 1)
						end
					end
				end
				statTable:Pool()
			end
		end

		local function EquipmentChangedContinueEquipPredicate(reaction, requestedSlot, event, slot, hasItem)
			return hasItem and requestedSlot==slot
		end

		local function EquipmentChangedContinueEquipAction(reaction, requestedSlot, event, slot, hasItem)
			ContinueEquip(true, requestedSlot)
		end

		function GA:OnPromptHidden(prompt)
			prompt.action = not prompt.blacklist:GetChecked() and prompt.action
			local quit = (prompt.forAll:GetChecked() and prompt.action == "SKIP") or prompt.action == "CANCEL"
			local showPrompt = not prompt.forAll:GetChecked()
			if prompt.blacklist:GetChecked() then
				if not DGV.chardb.GA_Blacklist then DGV.chardb.GA_Blacklist = {} end
				DGV.chardb.GA_Blacklist[DGV:GetItemIdFromLink(prompt.recommended.item)] = true
			end
			if prompt.action == "EQUIP" then
				if not showPrompt then
					ContinueEquip(false, prompt.slot)
				else
					RegisterReaction("PLAYER_EQUIPMENT_CHANGED"):WithPredicate(EquipmentChangedContinueEquipPredicate):WithAction(EquipmentChangedContinueEquipAction, prompt.slot):Once()
				end
				Equip(prompt.slot, prompt.recommended.item)

				return
			end
			if prompt.action == "SKIP" then
				tinsert(tempIgnoreCache, prompt.recommended.item..prompt.slot)
			end
			if not quit then
				ContinueEquip(showPrompt, prompt.slot)
			end
		end

		function GA:OnGearOptionClicked(button)
			if button==DugisEquipPromptFrame.recommended then
				DugisEquipPromptFrame.action="EQUIP"
			else
				DugisEquipPromptFrame.action="SKIP"
			end
		end

		local function AutoEquipEnabled()
			return DGV:UserSetting(DGV_AUTOEQUIPSMARTSET)
		end

		function GA.AutoEquipSmartSet(reaction, clearTempIgnore, button)
			if not InCombatLockdown() then
				if DGV:UserSetting(DGV_GASMARTSETTARGET)==WIN_CRITERIA_NONE or (not AutoEquipEnabled() and not button) then return end
				if clearTempIgnore then
					wipe(tempIgnoreCache)
				end
				ContinueEquip(true, nil)
			else
				DoOutOfCombat(GA.AutoEquipSmartSet, reaction, clearTempIgnore)
			end
		end

		local function NoEquipInProgress()
			return not DugisEquipPromptFrame:IsShown() and (not equipExecutedReaction or equipExecutedReaction.invoked)
		end

		local lastBUFire
		local function BagUpdatePredicate()
			local elapsed = GetTime()
			if lastBUFire==elapsed then return end
			lastBUFire = elapsed
			return AutoEquipEnabled() and NoEquipInProgress()
		end

		bagUpdateReaction = RegisterReaction("BAG_UPDATE", BagUpdatePredicate, function()

        --Outfitter bugfix
        if GA.lastOutfitterClickedTime == nil or (GetTime() - GA.lastOutfitterClickedTime) > 4 then
            GA.AutoEquipSmartSet()
        end

        end):Defer()
		--levelUpReaction = RegisterReaction("PLAYER_LEVEL_UP", AutoEquipEnabled, GA.AutoEquipSmartSet, true)
		activeSpecReaction = RegisterReaction("ACTIVE_TALENT_GROUP_CHANGED", AutoEquipEnabled, GA.AutoEquipSmartSet, true)

		local function SmartSetPredicate(reaction, clearTempIgnore, setName)
			return setName==L["Dugi Smart Set"]
		end
		smartSetReaction = RegisterFunctionReaction("EquipmentManager_EquipSet", SmartSetPredicate, GA.AutoEquipSmartSet, true)
		if AutoEquipEnabled() then QueueInvocation(GA.AutoEquipSmartSet) end

		local function HideRewardGuidance()
			DugisCoinRewardAdornment:Hide()
			DugisGreenArrowRewardAdornment:Hide()
			DugisYellowArrowRewardAdornment:Hide()
		end

		local function EvaluateRewards()
			if QuestFrameRewardPanel:IsShown() then
				local questId = GetQuestID()
				local questLogIndex
				if questId then
					questLogIndex = GetQuestLogIndexByID(questId);
					SelectQuestLogEntry(questLogIndex); --Blizzard bug? need this since GetNumQuestLogChoices or GetNumQuestChoices always returns 0 otherwise.
				end
			end

			HideRewardGuidance()
			local selectionMade
			for _, option,criterion in GA:IterateWinCriteria() do
				criterion:BindToAutoroutineLifetime(tPool)
				local winScore, winLink, winFrame, unresolvableTie
				for _, link, frame in GA.ItemChoiceIterator do
					local score = EvaluateWinCriteron(criterion, link)
					if score and (not winScore or winScore<score) then
						winScore = score
						winLink = link
						winFrame = frame
					elseif criterion.SettleTie and score and winScore==score then
						winLink = criterion:SettleTie(winLink, link)
						winFrame = winLink==link and frame or winFrame
						winScore = winLink==link and score or winScore
					elseif not criterion.SettleTie and score and winScore==score then
						unresolvableTie = true
					end
				end
				if winLink then
					if not selectionMade then

                        if Storyline_NPCFrame and Storyline_NPCFrame:IsShown() then
                            local buttonToBeHighlighted = GetStorylineButtonByRewardName(winFrame.Name:GetText())

                            if buttonToBeHighlighted then
                                if glowRewardFrame == nil then
                                    CreateFrame("Frame", "glowRewardFrame", buttonToBeHighlighted)
                                    texture = glowRewardFrame:CreateTexture()
                                    texture:SetAllPoints()
                                    texture:SetBlendMode("ADD")
                                    texture:SetTexture("Interface\\QuestFrame\\UI-QuestItemHighlight")
                                end

                                glowRewardFrame:SetWidth(256)
                                glowRewardFrame:SetHeight(64)
                                glowRewardFrame:SetFrameStrata("DIALOG")
                                buttonToBeHighlighted:SetFrameLevel(500)

                                glowRewardFrame:SetPoint("TOPLEFT", buttonToBeHighlighted, "TOPLEFT", -8, 7);

                                glowRewardFrame:Show()
                            else
                                if glowRewardFrame then
                                    glowRewardFrame:Hide()
                                end
                            end
                        end

						QueueInvocation(winFrame:GetScript("OnClick"), winFrame)
						selectionMade = true
					end
					criterion:AdornReward(winLink, winFrame)
				end
				criterion:Pool()
			end
		end

        GA.EvaluateRewards = EvaluateRewards

		local function ShouldShowQuestItems()
			local getNumChoices = QuestInfoFrame.questLog and GetNumQuestLogChoices or GetNumQuestChoices
			return getNumChoices()>1 --one choice is no choice at all
		end

		local lastRewardFrameShow
		local function DeferredRewardFrameShow(frame, elapsed) --first QuestInfoRewardsFrame:Show gets spammed
			if lastRewardFrameShow~=elapsed then
				lastRewardFrameShow = elapsed
				InterruptAutoroutine("EvaluateRewards")
				QueueAutoroutine("EvaluateRewards", EvaluateRewards)
			end
		end
		rewardShowReaction = RegisterMemberFunctionReaction(QuestInfoRewardsFrame, "Show", ShouldShowQuestItems):
			Or(RegisterMemberFunctionReaction(MapQuestInfoRewardsFrame, "Show", ShouldShowQuestItems)):
			WithAction(DeferredRewardFrameShow):Defer():InvokePassively()
		questHideReaction = RegisterMemberFunctionReaction(QuestInfoRewardsFrame, "Hide"):WithAction(HideRewardGuidance)
		adornerParentShowReaction = RegisterMemberFunctionReaction(QuestInfoRewardsFrame, "Show"):WithAction(HideRewardGuidance)

		local function HideLootRollGuidance()
			for i=1,NUM_GROUP_LOOT_FRAMES do
				local lootFrame = _G["GroupLootFrame"..i]
				if lootFrame and lootFrame.dugisGreenArrow then
					lootFrame.dugisGreenArrow:Hide()
				end
			end
		end

		local function EvaluateLootRollRoutine()
			HideLootRollGuidance()
			for _, option,criterion in GA:IterateWinCriteria() do
				criterion:BindToAutoroutineLifetime(tPool)
				for _, link, frame in GA.LootRollIterator do
					local score = EvaluateWinCriteron(criterion, link)
					if score and criterion.AdornLootRoll then
						criterion:AdornLootRoll(link, frame)
					end
				end
				criterion:Pool()
			end
		end

		local function EvaluateLootRoll()
			InterruptAutoroutine("EvaluateLootRollRoutine")
			QueueAutoroutine("EvaluateLootRollRoutine", EvaluateLootRollRoutine)
		end

		lootRollShowReaction = RegisterFunctionReaction("GroupLootFrame_OpenNewFrame"):WithAction(EvaluateLootRoll)

		local orig_GetLootRollTimeLeft, orig_GetLootRollItemLink, orig_GetLootRollItemInfo
		local function UndoMockGroupLoot()
			DGV:DebugFormat("UndoMockGroupLoot")
			GroupLootContainer_RemoveFrame(GroupLootContainer, GroupLootFrame1)
			GetLootRollTimeLeft = orig_GetLootRollTimeLeft
			GetLootRollItemLink = orig_GetLootRollItemLink
			GetLootRollItemInfo = orig_GetLootRollItemInfo
			orig_GetLootRollTimeLeft, orig_GetLootRollItemLink, orig_GetLootRollItemInfo = nil, nil, nil
		end

		local function OverrideGetLootRollTimeLeft(durationMillis)
			orig_GetLootRollTimeLeft = GetLootRollTimeLeft
			local nowMillis = GetTime()*100
			GetLootRollTimeLeft = function()
				return durationMillis - (GetTime()*100-nowMillis)
			end
		end

		local function OverrideGetLootRollItemLink(item)
			orig_GetLootRollItemLink = GetLootRollItemLink
			GetLootRollItemLink = function() return item end
		end

		local function OverrideGetLootRollItemInfo(item)
			orig_GetLootRollItemInfo = GetLootRollItemInfo
			GetLootRollItemInfo = function()
				local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType,

					itemStackCount, itemEquipLoc, itemTexture, itemSellPrice =
					GetItemInfo(item)
				return itemTexture, itemName, 1, itemRarity, 1, 1, 1, 1
			end
		end

		function MockGroupLoot(item, duration)
			DGV:DebugFormat("MockGroupLoot", "item",item,"duration", duration)
			if orig_GetLootRollTimeLeft then return end
			local durationMillis = duration*100
			OverrideGetLootRollTimeLeft(durationMillis)
			OverrideGetLootRollItemLink(item)
			OverrideGetLootRollItemInfo(item)
			GroupLootFrame_OpenNewFrame(1, durationMillis)
			RegisterStopwatchReaction(duration):Once():WithAction(UndoMockGroupLoot)
		end

		local function HideEncounterJournalGuidance()
			local scrollFrame = EncounterJournal.encounter.info.lootScroll;
			local items = scrollFrame.buttons;

			for _,button in ipairs(items) do
				if button.dugisGreenArrow then
					button.dugisGreenArrow:Hide()
				end
				if button.dugisYellowArrow then
					button.dugisYellowArrow:Hide()
				end
			end
		end

		local function EvaluateEncounterJournalRoutine()
			HideEncounterJournalGuidance()
-- if DGV.currentAutoroutine then
-- DGV:DebugFormat("EvaluateEncounterJournalRoutine 1", "time", DGV.GetTicks()-DGV.currentAutoroutine.startTime)
-- end
			if
				EncounterJournal and
				EncounterJournal:IsShown() and
				EncounterJournal.encounter.info.lootScroll:IsShown()
			then
				local greenedSlots = GetCreateTable():BindToAutoroutineLifetime(tPool)
				for slot,inv1,inv2 in NextUniqueInventorySlot do
					for criterionIndex, option,criterion in GA:IterateWinCriteria() do
						criterion:BindToAutoroutineLifetime(tPool)
						if criterion.AdornEncounterJournal then
-- if DGV.currentAutoroutine then
-- DGV:DebugFormat("EvaluateEncounterJournalRoutine 2", "time", DGV.GetTicks()-DGV.currentAutoroutine.startTime, "slot", slot, "criterionIndex", criterionIndex)
-- end
							local winningItem, winningScore, altWinner, altScore = GetCurrentBestInSlot(slot, criterion.specNum, criterion.pvp, nil, nil, nil, nil, true)
-- if DGV.currentAutoroutine then
-- DGV:DebugFormat("EvaluateEncounterJournalRoutine 3", "time", DGV.GetTicks()-DGV.currentAutoroutine.startTime, "slot", slot, "criterionIndex", criterionIndex)
-- end

							for _, link, frame in GA.EncounterJournalIterator do
								if criterion:Predicate(link, unpack(criterion)) then
									if slot==INVSLOT_MAINHAND and link==altWinner then
										slot = INVSLOT_OFFHAND
										winningItem = link
									end

									if link==winningItem then
										if frame then
											criterion:AdornEncounterJournal(link, frame, (greenedSlots[slot] and "yellow") or "green")
										end
										greenedSlots[slot] = true
									elseif link==altWinner then
										if frame then
											criterion:AdornEncounterJournal(link, frame, "yellow")
										end
									end
								end
								YieldAutoroutine()
							end
						end
						criterion:Pool()
					end
				end
				greenedSlots:Pool()
			end
		end

		local function EvaluateEncounterJournal()
			InterruptAutoroutine("EvaluateEncounterJournal")
			BeginAutoroutine("EvaluateEncounterJournal", EvaluateEncounterJournalRoutine)
		end


        local EncounterJournal_advising_initialized = false
        C_Timer.NewTicker(2, function()
            if EncounterJournal and EncounterJournal_advising_initialized == false then
              encounterJournalUpdateReaction = RegisterMemberFunctionReaction("EncounterJournal.encounter.info.lootScroll", "update")
				:Or(RegisterFunctionReaction("EncounterJournal_LootUpdate")):WithAction(EvaluateEncounterJournal)

                  hooksecurefunc("EncounterJournal_LootUpdate", function()
                    -- EvaluateEncounterJournal()
                end)

                EncounterJournal_advising_initialized = true
            end
        end)


		local function HideVendorGuidance()
			for i=1, MERCHANT_ITEMS_PER_PAGE, 1 do
				local itemButton = _G["MerchantItem"..i.."ItemButton"]
				if itemButton then
					if itemButton.dugisGreenArrow then
						itemButton.dugisGreenArrow:Hide()
					end
					if itemButton.dugisYellowArrow then
						itemButton.dugisYellowArrow:Hide()
					end
				end
			end
		end

		local function EvaluateVendorRoutine()
			HideVendorGuidance()
			if MerchantFrame:IsShown() then
				local greenedSlots = GetCreateTable():BindToAutoroutineLifetime(tPool)
				for slot,inv1,inv2 in NextUniqueInventorySlot do
					for criterionIndex, option, criterion in GA:IterateWinCriteria() do
						criterion:BindToAutoroutineLifetime(tPool)
						if criterion.AdornVendorItem then
							local winningItem, winningScore, altWinner, altScore = GetCurrentBestInSlot(slot, criterion.specNum, criterion.pvp, nil, nil, nil, nil, true)
							for _, link, frame in GA.VendorIterator do
								if criterion:Predicate(link, unpack(criterion)) then
									if slot==INVSLOT_MAINHAND and link==altWinner then
										slot = INVSLOT_OFFHAND
										winningItem = link
									end

									if link==winningItem then
										if frame then
											criterion:AdornVendorItem(link, frame, (greenedSlots[slot] and "yellow") or "green")
										end
										greenedSlots[slot] = true
									elseif link==altWinner then
										if frame then
											criterion:AdornVendorItem(link, frame, "yellow")
										end
									end
								end
								YieldAutoroutine()
							end
						end
						criterion:Pool()
					end
				end
				greenedSlots:Pool()
			end
		end

		local function EvaluateVendor()
			InterruptAutoroutine("EvaluateVendor")
			QueueAutoroutine("EvaluateVendor", EvaluateVendorRoutine)
		end

		vendorUpdateReaction =  RegisterFunctionReaction("MerchantFrame_UpdateMerchantInfo"):WithAction(EvaluateVendor)

		local function HideLootGuidance()
			for i=1, LOOTFRAME_NUMBUTTONS  do
				local itemButton = _G["LootButton"..i];
				if itemButton then
					if itemButton.dugisGreenArrow then
						itemButton.dugisGreenArrow:Hide()
					end
					if itemButton.dugisYellowArrow then
						itemButton.dugisYellowArrow:Hide()
					end
				end
			end
		end

		local function EvaluateLootRoutine()
			HideLootGuidance()
			if LootFrame:IsShown() then
				local greenedSlots = GetCreateTable():BindToAutoroutineLifetime(tPool)
				for _, option,criterion in GA:IterateWinCriteria() do
					criterion:BindToAutoroutineLifetime(tPool)
					local winScore, winLink, winFrame, winSlot
					for _, link, frame in GA.LootIterator do
						local score,_,_,uniqueInventorySlot = EvaluateWinCriteron(criterion, link)
						if score and (not winScore or winScore<score) then
							winScore = score
							winLink = link
							winFrame = frame
							winSlot = uniqueInventorySlot
						elseif criterion.SettleTie and score and winScore==score then
							winLink = criterion:SettleTie(winLink, link)
							winFrame = winLink==link and frame or winFrame
							winScore = winLink==link and score or winScore
							winSlot = uniqueInventorySlot
						end
					end
					if winLink and winFrame then
						criterion:AdornLoot(winLink, winFrame, (greenedSlots[winSlot] and "yellow") or "green")
					end
					if winSlot then
						greenedSlots[winSlot] = true
					end
					criterion:Pool()
				end
				greenedSlots:Pool()
			end
		end

		local function EvaluateLoot()
			InterruptAutoroutine("EvaluateLoot")
			QueueAutoroutine("EvaluateLoot", EvaluateLootRoutine)
		end

		lootUpdateReaction =  RegisterFunctionReaction("LootFrame_Update"):WithAction(EvaluateLoot)

		if DGV.Debug then
			DGV:RegisterTest(
				function(suite)
					--suite.mocks
					--suite.setUp
					--suite.tearDown
					local function justReturn(arg1, arg2, arg3, arg4, arg5, arg6, ...)
						return ...
					end
					suite.testVisitCSV = function(state)
						local joined = strjoin("test", VisitCSV(justReturn, nil, nil, nil, nil, nil, nil, "1,two,3", "four,5,six"))
						local expected = "1testtwotest3testfourtest5testsix"
						DGV:ShouldEqual(expected, joined)
					end
					suite.testMathSum = function(state)
						DGV:ShouldEqual(6, math.sum(1,2,3))
					end
					suite.testItemIteratorWithArgITEM_ITERATOR_SKIP_EQUIPPEDShouldNotGivePlayerInventory = function(state)
						local playerInventoryFound = false
						for control,iteratedItemLink in ItemIterator,ITEM_ITERATOR_SKIP_EQUIPPED do
							playerInventoryFound = playerInventoryFound or (control.player and not control.bags)
						end
						DGV:Shouldnt(playerInventoryFound)
					end
					return "GearAdvisorConditionlessTests"
				end)
		end
	end

	function GA:Unload()
		TipHooker:Unhook(ProcessTooltip, "item")
		bagUpdateReaction:Dispose()
		--levelUpReaction:Dispose()
		activeSpecReaction:Dispose()
		smartSetReaction:Dispose()
		rewardShowReaction:Dispose()
		questHideReaction:Dispose()
		adornerParentShowReaction:Dispose()
		lootRollShowReaction:Dispose()
		vendorUpdateReaction:Dispose()
		lootUpdateReaction:Dispose()
		if encounterJournalUpdateReaction then
			encounterJournalUpdateReaction:Dispose()
		end
		if equipExecutedReaction then
			if equipExecutedReaction.requestedSlots then
				equipExecutedReaction.requestedSlots:Pool()
				equipExecutedReaction.requestedSlots = nil
			end
			equipExecutedReaction:Dispose()
			equipExecutedReaction = nil
		end
		GetNumEquipmentSets = orig_GetNumEquipmentSets
		GetEquipmentSetInfo = orig_GetEquipmentSetInfo
	end
end
--[[
GetItemStats("itemLink" [, returnTable])
id, texture, checkRelic = GetInventorySlotInfo("slotName")
Arguments:

slotName - Name of an inventory slot to query (string)
AmmoSlot - Ranged ammunition slot
BackSlot - Back (cloak) slot
Bag0Slot - Backpack slot
Bag1Slot - First bag slot
Bag2Slot - Second bag slot
Bag3Slot - Third bag slot
ChestSlot - Chest slot
FeetSlot - Feet (boots) slot
Finger0Slot - First finger (ring) slot
Finger1Slot - Second finger (ring) slot
HandsSlot - Hand (gloves) slot
HeadSlot - Head (helmet) slot
LegsSlot - Legs (pants) slot
MainHandSlot - Main hand weapon slot
NeckSlot - Necklace slot
RangedSlot - Ranged weapon or relic slot
SecondaryHandSlot - Off-hand (weapon, shield, or held item) slot
ShirtSlot - Shirt slot
ShoulderSlot - Shoulder slot
TabardSlot - Tabard slot
Trinket0Slot - First trinket slot
Trinket1Slot - Second trinket slot
WaistSlot - Waist (belt) slot
WristSlot - Wrist (bracers) slot]]

--StatLogic:GetEffectFromRating same as GetCombatRatingBonus
--GetExpertise includes racial bonuses, StatLogic:GetEffectFromRating does not.
--LibStatLogic does not mod spell hit/expertise based upon racial weapon specializations.
--GetHitModifier, GetSpellHitModifier, and GetExpertise perform this sort of function, but only apply to equipped items, current level, etc.

--[[function GetMeleeMissChance(levelOffset, special)
    if (levelOffset < 0 or levelOffset > 3) then
        return 0;
    end
    local chance = BASE_MISS_CHANCE_PHYSICAL[levelOffset];
    chance = chance - GetCombatRatingBonus(CR_HIT_MELEE) - GetHitModifier();
    if (IsDualWielding() and not special) then
        chance = chance + DUAL_WIELD_HIT_PENALTY;
    end
    if (chance < 0) then
        chance = 0;
    elseif (chance > 100) then
        chance = 100;
    end
    return chance;
end

function GetRangedMissChance(levelOffset, special)
    if (levelOffset < 0 or levelOffset > 3) then
        return 0;
    end
    local chance = BASE_MISS_CHANCE_PHYSICAL[levelOffset];
    chance = chance - GetCombatRatingBonus(CR_HIT_RANGED) - GetHitModifier();
    if (chance < 0) then
        chance = 0;
    elseif (chance > 100) then
        chance = 100;
    end
    return chance;
end

function GetSpellMissChance(levelOffset, special)
    if (levelOffset < 0 or levelOffset > 3) then
        return 0;
    end
    local chance = BASE_MISS_CHANCE_SPELL[levelOffset];
    chance = chance - GetCombatRatingBonus(CR_HIT_SPELL) - GetSpellHitModifier();
    if (chance < 0) then
        chance = 0;
    elseif (chance > 100) then
        chance = 100;
    end
    return chance;
end

function GetEnemyDodgeChance(levelOffset)
    if (levelOffset < 0 or levelOffset > 3) then
        return 0;
    end
    local chance = BASE_ENEMY_DODGE_CHANCE[levelOffset];
    local offhandChance = BASE_ENEMY_DODGE_CHANCE[levelOffset];
    local rangedChance = BASE_ENEMY_DODGE_CHANCE[levelOffset];
    local expertisePct, offhandExpertisePct, rangedExpertisePct = GetExpertise();
    chance = chance - expertisePct;
    offhandChance = offhandChance - offhandExpertisePct;
    rangedChance = rangedChance - rangedExpertisePct;
    if (chance < 0) then
        chance = 0;
    elseif (chance > 100) then
        chance = 100;
    end
    if (offhandChance < 0) then
        offhandChance = 0;
    elseif (offhandChance > 100) then
        offhandChance = 100;
    end
    if (rangedChance < 0) then
        rangedChance = 0;
    elseif (rangedChance > 100) then
        rangedChance = 100;
    end
    return chance, offhandChance, rangedChance;
end

function GetEnemyParryChance(levelOffset)
    if (levelOffset < 0 or levelOffset > 3) then
        return 0;
    end
    local chance = BASE_ENEMY_PARRY_CHANCE[levelOffset];
    local offhandChance = BASE_ENEMY_PARRY_CHANCE[levelOffset];
    local expertisePct, offhandExpertisePct = GetExpertise();
    local mainhandDodge = BASE_ENEMY_DODGE_CHANCE[levelOffset];
    local offhandDodge = BASE_ENEMY_DODGE_CHANCE[levelOffset];

    expertisePct = expertisePct - mainhandDodge;
    if ( expertisePct < 0 ) then
        expertisePct = 0;
    end
    chance = chance - expertisePct;
    if (chance < 0) then
        chance = 0;
    elseif (chance > 100) then
        chance = 100;
    end

    offhandExpertisePct = offhandExpertisePct - offhandDodge;
    if ( offhandExpertisePct < 0 ) then
        offhandExpertisePct = 0;
    end
    offhandChance = offhandChance - offhandExpertisePct;
    if (offhandChance < 0) then
        offhandChance = 0;
    elseif (offhandChance > 100) then
        offhandChance = 100;
    end

    return chance, offhandChance;
end]]


--[[
from http://www.wowace.com/paste/832.txt
ITEM_MOD_AGILITY = "%c%d Agility"
ITEM_MOD_AGILITY_SHORT = "Agility"
ITEM_MOD_ARMOR_PENETRATION_RATING = "Increases your armor penetration rating by %d."
ITEM_MOD_ARMOR_PENETRATION_RATING_SHORT = "Armor Penetration Rating"
ITEM_MOD_ATTACK_POWER = "Increases attack power by %d."
ITEM_MOD_ATTACK_POWER_SHORT = "Attack Power"
ITEM_MOD_BLOCK_RATING = "Increases your shield block rating by %d."
ITEM_MOD_BLOCK_RATING_SHORT = "Block Rating"
ITEM_MOD_BLOCK_VALUE = "Increases your shield value by %d."
ITEM_MOD_BLOCK_VALUE_SHORT = "Block Value"
ITEM_MOD_CRIT_MELEE_RATING = "Improves melee critical strike rating by %d."
ITEM_MOD_CRIT_MELEE_RATING_SHORT = "Critical Strike Rating (Melee)"
ITEM_MOD_CRIT_RANGED_RATING = "Improves ranged critical strike rating by %d."
ITEM_MOD_CRIT_RANGED_RATING_SHORT = "Critical Strike Rating (Ranged)"
ITEM_MOD_CRIT_RATING = "Improves critical strike rating by %d."
ITEM_MOD_CRIT_RATING_SHORT = "Critical Strike Rating"
ITEM_MOD_CRIT_SPELL_RATING = "Improves spell critical strike rating by %d."
ITEM_MOD_CRIT_SPELL_RATING_SHORT = "Critical Strike Rating (Spell)"
ITEM_MOD_CRIT_TAKEN_MELEE_RATING = "Improves melee critical avoidance rating by %d."
ITEM_MOD_CRIT_TAKEN_MELEE_RATING_SHORT = "Critical Strike Avoidance Rating (Melee)"
ITEM_MOD_CRIT_TAKEN_RANGED_RATING = "Improves ranged critical avoidance rating by %d."
ITEM_MOD_CRIT_TAKEN_RANGED_RATING_SHORT = "Critical Strike Avoidance Rating (Ranged)"
ITEM_MOD_CRIT_TAKEN_RATING = "Improves critical avoidance rating by %d."
ITEM_MOD_CRIT_TAKEN_RATING_SHORT = "Critical Strike Avoidance Rating"
ITEM_MOD_CRIT_TAKEN_SPELL_RATING = "Improves spell critical avoidance rating by %d."
ITEM_MOD_CRIT_TAKEN_SPELL_RATING_SHORT = "Critical Strike Avoidance Rating (Spell)"
ITEM_MOD_DAMAGE_PER_SECOND_SHORT = "Damage Per Second"
ITEM_MOD_DEFENSE_SKILL_RATING = "Increases defense rating by %d."
ITEM_MOD_DEFENSE_SKILL_RATING_SHORT = "Defense Rating"
ITEM_MOD_DODGE_RATING = "Increases your dodge rating by %d."
ITEM_MOD_DODGE_RATING_SHORT = "Dodge Rating"
ITEM_MOD_EXPERTISE_RATING = "Increases your expertise rating by %d."
ITEM_MOD_EXPERTISE_RATING_SHORT = "Expertise Rating"
ITEM_MOD_FERAL_ATTACK_POWER = "Increases attack power by %d in Cat, Bear, Dire Bear, and Moonkin forms only."
ITEM_MOD_FERAL_ATTACK_POWER_SHORT = "Attack Power In Forms"
ITEM_MOD_HASTE_MELEE_RATING = "Improves melee haste rating by %d."
ITEM_MOD_HASTE_MELEE_RATING_SHORT = "Haste Rating (Melee)"
ITEM_MOD_HASTE_RANGED_RATING = "Improves ranged haste rating by %d."
ITEM_MOD_HASTE_RANGED_RATING_SHORT = "Haste Rating (Ranged)"
ITEM_MOD_HASTE_RATING = "Improves haste rating by %d."
ITEM_MOD_HASTE_RATING_SHORT = "Haste Rating"
ITEM_MOD_HASTE_SPELL_RATING = "Improves spell haste rating by %d."
ITEM_MOD_HASTE_SPELL_RATING_SHORT = "Haste Rating (Spell)"
ITEM_MOD_HEALTH = "%c%d Health"
ITEM_MOD_HEALTH_REGEN_SHORT = "Health Per 5 Sec."
ITEM_MOD_HEALTH_REGENERATION = "Restores %d health per 5 sec."
ITEM_MOD_HEALTH_REGENERATION_SHORT = "Health Regeneration"
ITEM_MOD_HEALTH_SHORT = "Health"
ITEM_MOD_HIT_MELEE_RATING = "Improves melee hit rating by %d."
ITEM_MOD_HIT_MELEE_RATING_SHORT = "Hit Rating (Melee)"
ITEM_MOD_HIT_RANGED_RATING = "Improves ranged hit rating by %d."
ITEM_MOD_HIT_RANGED_RATING_SHORT = "Hit Rating (Ranged)"
ITEM_MOD_HIT_RATING = "Improves hit rating by %d."
ITEM_MOD_HIT_RATING_SHORT = "Hit Rating"
ITEM_MOD_HIT_SPELL_RATING = "Improves spell hit rating by %d."
ITEM_MOD_HIT_SPELL_RATING_SHORT = "Hit Rating (Spell)"
ITEM_MOD_HIT_TAKEN_MELEE_RATING = "Improves melee hit avoidance rating by %d."
ITEM_MOD_HIT_TAKEN_MELEE_RATING_SHORT = "Hit Avoidance Rating (Melee)"
ITEM_MOD_HIT_TAKEN_RANGED_RATING = "Improves ranged hit avoidance rating by %d."
ITEM_MOD_HIT_TAKEN_RANGED_RATING_SHORT = "Hit Avoidance Rating (Ranged)"
ITEM_MOD_HIT_TAKEN_RATING = "Improves hit avoidance rating by %d."
ITEM_MOD_HIT_TAKEN_RATING_SHORT = "Hit Avoidance Rating"
ITEM_MOD_HIT_TAKEN_SPELL_RATING = "Improves spell hit avoidance rating by %d."
ITEM_MOD_HIT_TAKEN_SPELL_RATING_SHORT = "Hit Avoidance Rating (Spell)"
ITEM_MOD_INTELLECT = "%c%d Intellect"
ITEM_MOD_INTELLECT_SHORT = "Intellect"
ITEM_MOD_MANA = "%c%d Mana"
ITEM_MOD_MANA_REGENERATION = "Restores %d mana per 5 sec."
ITEM_MOD_MANA_REGENERATION_SHORT = "Mana Regeneration"
ITEM_MOD_MANA_SHORT = "Mana"
ITEM_MOD_MELEE_ATTACK_POWER_SHORT = "Melee Attack Power"
ITEM_MOD_PARRY_RATING = "Increases your parry rating by %d."
ITEM_MOD_PARRY_RATING_SHORT = "Parry Rating"
ITEM_MOD_POWER_REGEN0_SHORT = "Mana Per 5 Sec."
ITEM_MOD_POWER_REGEN1_SHORT = "Rage Per 5 Sec."
ITEM_MOD_POWER_REGEN2_SHORT = "Focus Per 5 Sec."
ITEM_MOD_POWER_REGEN3_SHORT = "Energy Per 5 Sec."
ITEM_MOD_POWER_REGEN4_SHORT = "Happiness Per 5 Sec."
ITEM_MOD_POWER_REGEN5_SHORT = "Runes Per 5 Sec."
ITEM_MOD_POWER_REGEN6_SHORT = "Runic Power Per 5 Sec."
ITEM_MOD_RANGED_ATTACK_POWER = "Increases ranged attack power by %d."
ITEM_MOD_RANGED_ATTACK_POWER_SHORT = "Ranged Attack Power"
ITEM_MOD_RESILIENCE_RATING = "Improves your resilience rating by %d."
ITEM_MOD_RESILIENCE_RATING_SHORT = "Resilience Rating"
ITEM_MOD_SPELL_DAMAGE_DONE = "Increases damage done by magical spells and effects by up to %d."
ITEM_MOD_SPELL_DAMAGE_DONE_SHORT = "Bonus Damage"
ITEM_MOD_SPELL_HEALING_DONE = "Increases healing done by magical spells and effects by up to %d."
ITEM_MOD_SPELL_HEALING_DONE_SHORT = "Bonus Healing"
ITEM_MOD_SPELL_PENETRATION = "Increases spell penetration by %d."
ITEM_MOD_SPELL_PENETRATION_SHORT = "Spell Penetration"
ITEM_MOD_SPELL_POWER = "Increases spell power by %d."
ITEM_MOD_SPELL_POWER_SHORT = "Spell Power"
ITEM_MOD_SPIRIT = "%c%d Spirit"
ITEM_MOD_SPIRIT_SHORT = "Spirit"
ITEM_MOD_STAMINA = "%c%d Stamina"
ITEM_MOD_STAMINA_SHORT = "Stamina"
ITEM_MOD_STRENGTH = "%c%d Strength"
ITEM_MOD_STRENGTH_SHORT = "Strength"
EMPTY_SOCKET_META = "Meta Socket"
EMPTY_SOCKET = "Level %d Socket"
EMPTY_SOCKET_RED = "Red Socket"
EMPTY_SOCKET_BLUE = "Blue Socket"
EMPTY_SOCKET_YELLOW = "Yellow Socket"
EMPTY_SOCKET_NO_COLOR = "Prismatic Socket"

RESISTANCE_NONE = "None"
RESISTANCE_TYPE0 = "armor"
RESISTANCE_TYPE1 = "holy"
RESISTANCE_TYPE2 = "fire"
RESISTANCE_TYPE3 = "nature"
RESISTANCE_TYPE4 = "frost"
RESISTANCE_TYPE5 = "shadow"
RESISTANCE_TYPE6 = "arcane"

RESISTANCE0_NAME = "Armor"
RESISTANCE1_NAME = "Holy Resistance"
RESISTANCE2_NAME = "Fire Resistance"
RESISTANCE3_NAME = "Nature Resistance"
RESISTANCE4_NAME = "Frost Resistance"
RESISTANCE5_NAME = "Shadow Resistance"
RESISTANCE6_NAME = "Arcane Resistance"]]

--[[
from a LibStatLogic L.StatIDLookup dump
["StatIDLookup"] = {
		["Critical Strike (Spell)"] = {
			"SPELL_CRIT_RATING", -- [1]

		},
		["Scope (Damage)"] = {
			"RANGED_DMG", -- [1]
		},
		["% Shield Block Value"] = {
			"MOD_BLOCK_VALUE", -- [1]
		},
		["Increases the damage done by Fire spells and effects"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["Improves ranged critical strike"] = {
			"RANGED_CRIT_RATING", -- [1]
		},
		["Increases your critical strike"] = {
			"MELEE_CRIT_RATING", -- [1]
			"RANGED_CRIT_RATING", -- [2]
			"SPELL_CRIT_RATING", -- [3]
		},
		["Shadow and Frost Spell Power"] = {
			"SHADOW_SPELL_DMG", -- [1]
			"FROST_SPELL_DMG", -- [2]
		},
		["Critical Strike (Ranged)"] = {
			"RANGED_CRIT_RATING", -- [1]
		},
		["increases your hit"] = {
			"MELEE_HIT_RATING", -- [1]
			"RANGED_HIT_RATING", -- [2]
			"SPELL_HIT_RATING", -- [3]
		},
		["Hit Avoidance (Melee)"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
		},
		["Damage"] = {
			"SPELL_DMG", -- [1]
		},
		["improves spell hit"] = {
			"SPELL_HIT_RATING", -- [1]
		},
		["increases damage done to undead by magical spells and effects.  it also allows the acquisition of scourgestones on behalf of the argent dawn"] = {
			"SPELL_DMG_UNDEAD", -- [1]
		},
		["Frost Resistance"] = {
			"FROST_RES", -- [1]
		},
		["Improves melee hit"] = {
			"MELEE_HIT_RATING", -- [1]
		},
		["mining"] = {
			"MINING", -- [1]
		},
		["Hit (Ranged)"] = {
			"RANGED_HIT_RATING", -- [1]
		},
		["increases damage done to undead by magical spells and effects"] = {
			"SPELL_DMG_UNDEAD", -- [1]
		},
		["Improves spell critical avoidance"] = {
			"SPELL_CRIT_AVOID_RATING", -- [1]
		},
		["critical strike (melee)"] = {
			"MELEE_CRIT_RATING", -- [1]
		},
		["cooking skill increased"] = {
			"COOKING", -- [1]
		},
		["shadow spell damage"] = {
			"SHADOW_SPELL_DMG", -- [1]
		},
		["improves melee critical strike"] = {
			"MELEE_CRIT_RATING", -- [1]
		},
		["MP"] = {
			"MANA", -- [1]
		},
		["improves ranged critical strike"] = {
			"RANGED_CRIT_RATING", -- [1]
		},
		["Increases your armor penetration"] = {
			"ARMOR_PENETRATION_RATING", -- [1]
		},
		["skinning; does not need to be equipped"] = {
			"SKINNING", -- [1]
		},
		["experience gained from killing monsters and completing quests increased%"] = false,
		["mana regeneration"] = {
			"COMBAT_MANA_REGEN", -- [1]
		},
		["Healing and Spell Damage"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["increases attack powerwhen fighting undead.  it also allows the acquisition of scourgestones on behalf of the argent dawn"] = {
			"AP_UNDEAD", -- [1]
		},
		["Holy Damage"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["Stamina"] = {
			"STA", -- [1]
		},
		["HP"] = {
			"HEALTH", -- [1]
		},
		["Attack Power versus Undead"] = {
			"AP_UNDEAD", -- [1]
		},
		["hit avoidance (spell)"] = {
			"SPELL_HIT_AVOID_RATING", -- [1]
		},
		["Shadow Resistance"] = {
			"SHADOW_RES", -- [1]
		},
		["Weapon Damage"] = {
			"MELEE_DMG", -- [1]
		},
		["Spirit"] = {
			"SPI", -- [1]
		},
		["increases damage done by arcane spells and effects"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["Increases the damage done by Frost spells and effects"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["Haste (Ranged)"] = {
			"RANGED_HASTE_RATING", -- [1]
		},
		["improves spell hit avoidance"] = {
			"SPELL_HIT_AVOID_RATING", -- [1]
		},
		["Health"] = {
			"HEALTH", -- [1]
		},
		["Shadow Spell Damage"] = {
			"SHADOW_SPELL_DMG", -- [1]
		},
		["stamina"] = {
			"STA", -- [1]
		},
		["increases the damage dealt by your crusader strike ability%"] = false,
		["fire spell damage"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["hit (spell)"] = {
			"SPELL_HIT_RATING", -- [1]
		},
		["Spell Damage"] = {
			"SPELL_DMG", -- [1]
		},
		["Attack Power In Forms"] = {
			"FERAL_AP", -- [1]
		},
		["mp"] = {
			"MANA", -- [1]
		},
		["Block"] = {
			"BLOCK_RATING", -- [1]
		},
		["Spell Power"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Attack Power when fighting Undead"] = {
			"AP_UNDEAD", -- [1]
		},
		["Improves spell hit"] = {
			"SPELL_HIT_RATING", -- [1]
		},
		["Nature Spell Damage"] = {
			"NATURE_SPELL_DMG", -- [1]
		},
		["Mining; does not need to be equipped"] = {
			"MINING", -- [1]
		},
		["Mining"] = {
			"MINING", -- [1]
		},
		["shadow resist"] = {
			"SHADOW_RES", -- [1]
		},
		["Improves spell hit avoidance"] = {
			"SPELL_HIT_AVOID_RATING", -- [1]
		},
		["health"] = {
			"HEALTH", -- [1]
		},
		["Cooking skill increased"] = {
			"COOKING", -- [1]
		},
		[" to All Resistances"] = {
			"ARCANE_RES", -- [1]
			"FIRE_RES", -- [2]
			"FROST_RES", -- [3]
			"NATURE_RES", -- [4]
			"SHADOW_RES", -- [5]
		},
		["Increases damage done to Undead by magical spells and effects.  It also allows the acquisition of Scourgestones on behalf of the Argent Dawn"] = {
			"SPELL_DMG_UNDEAD", -- [1]
		},
		["Mana"] = {
			"MANA", -- [1]
		},
		["hit (ranged)"] = {
			"RANGED_HIT_RATING", -- [1]
		},
		["Hit (Melee)"] = {
			"MELEE_HIT_RATING", -- [1]
		},
		["Expertise"] = {
			"EXPERTISE_RATING", -- [1]
		},
		["Improves critical avoidance"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
			"RANGED_CRIT_AVOID_RATING", -- [2]
			"SPELL_CRIT_AVOID_RATING", -- [3]
		},
		["dodge"] = {
			"DODGE_RATING", -- [1]
		},
		["block"] = {
			"BLOCK_RATING", -- [1]
		},
		["to"] = false,
		["Improves ranged haste"] = {
			"RANGED_HASTE_RATING", -- [1]
		},
		["fire resist"] = {
			"FIRE_RES", -- [1]
		},
		["increases attack powerin cat, bear, dire bear, and moonkin forms only"] = {
			"FERAL_AP", -- [1]
		},
		["increases your pvp power"] = {
			"PVP_POWER", -- [1]
		},
		["frost resistance"] = {
			"FROST_RES", -- [1]
		},
		["skinning skill increased"] = {
			"SKINNING", -- [1]
		},
		["improves critical avoidance"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
			"RANGED_CRIT_AVOID_RATING", -- [2]
			"SPELL_CRIT_AVOID_RATING", -- [3]
		},
		["Dodge"] = {
			"DODGE_RATING", -- [1]
		},
		["Fire Resist"] = {
			"FIRE_RES", -- [1]
		},
		["spell damage"] = {
			"SPELL_DMG", -- [1]
		},
		["Arcane Resist"] = {
			"ARCANE_RES", -- [1]
		},
		["increases the damage done by arcane spells and effects"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["Intellect"] = {
			"INT", -- [1]
		},
		["increases your pvp resilience"] = {
			"RESILIENCE_RATING", -- [1]
		},
		["Haste (Spell)"] = {
			"SPELL_HASTE_RATING", -- [1]
		},
		["increases ranged attack power"] = {
			"RANGED_AP", -- [1]
		},
		["Nature Damage"] = {
			"NATURE_SPELL_DMG", -- [1]
		},
		["Skinning skill increased"] = {
			"SKINNING", -- [1]
		},
		["Increases attack powerwhen fighting Demons"] = {
			"AP_DEMON", -- [1]
		},
		["Increases attack powerwhen fighting Undead"] = {
			"AP_UNDEAD", -- [1]
		},
		["shadow and frost spell power"] = {
			"SHADOW_SPELL_DMG", -- [1]
			"FROST_SPELL_DMG", -- [2]
		},
		["Increases damage done by Fire spells and effects"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["Shadow resistance"] = {
			"SHADOW_RES", -- [1]
		},
		["all resistances"] = {
			"ARCANE_RES", -- [1]
			"FIRE_RES", -- [2]
			"FROST_RES", -- [3]
			"NATURE_RES", -- [4]
			"SHADOW_RES", -- [5]
		},
		["Improves ranged critical avoidance"] = {
			"RANGED_CRIT_AVOID_RATING", -- [1]
		},
		["increases your expertise"] = {
			"EXPERTISE_RATING", -- [1]
		},
		["Critical Strike"] = {
			"MELEE_CRIT_RATING", -- [1]
			"RANGED_CRIT_RATING", -- [2]
			"SPELL_CRIT_RATING", -- [3]
		},
		["intellect"] = {
			"INT", -- [1]
		},
		["Herbalism"] = {
			"HERBALISM", -- [1]
		},
		["Increases ranged attack power"] = {
			"RANGED_AP", -- [1]
		},
		["nature damage"] = {

			"NATURE_SPELL_DMG", -- [1]
		},
		["scope (critical strike rating)"] = {
			"RANGED_CRIT_RATING", -- [1]
		},
		["attack power"] = {
			"AP", -- [1]
		},
		["Armor"] = {
			"BONUS_ARMOR", -- [1]
		},
		["increases damage done by holy spells and effects"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["Increases your PvP power"] = {
			"PVP_POWER", -- [1]
		},
		["Skinning"] = {
			"SKINNING", -- [1]
		},
		["Fire Damage"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["Improves ranged hit avoidance"] = {
			"RANGED_HIT_AVOID_RATING", -- [1]
		},
		["Attack Power"] = {
			"AP", -- [1]
		},
		["Arcane and Fire Spell Power"] = {
			"ARCANE_SPELL_DMG", -- [1]
			"FIRE_SPELL_DMG", -- [2]
		},
		["frost damage"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["Arcane Damage"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["Hit Avoidance (Spell)"] = {
			"SPELL_HIT_AVOID_RATING", -- [1]
		},
		["Critical Strike (Melee)"] = {
			"MELEE_CRIT_RATING", -- [1]
		},
		["Increases the damage dealt by your Crusader Strike ability%"] = false,
		["holy resistance"] = {
			"HOLY_RES", -- [1]
		},
		["Scope (Critical Strike Rating)"] = {
			"RANGED_CRIT_RATING", -- [1]
		},
		["Critical Strike Avoidance"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
			"RANGED_CRIT_AVOID_RATING", -- [2]
			"SPELL_CRIT_AVOID_RATING", -- [3]
		},
		["haste"] = {
			"MELEE_HASTE_RATING", -- [1]
			"RANGED_HASTE_RATING", -- [2]
			"SPELL_HASTE_RATING", -- [3]
		},
		["attack power in forms"] = {
			"FERAL_AP", -- [1]
		},
		["holy damage"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["Frost Resist"] = {
			"FROST_RES", -- [1]
		},
		["Increases your mastery"] = {
			"MASTERY_RATING", -- [1]
		},
		["Improves hit avoidance"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
			"RANGED_HIT_AVOID_RATING", -- [2]
			"SPELL_HIT_AVOID_RATING", -- [3]
		},
		["increases your haste"] = {
			"MELEE_HASTE_RATING", -- [1]
			"RANGED_HASTE_RATING", -- [2]
			"SPELL_HASTE_RATING", -- [3]
		},
		[" to all resistances"] = {
			"ARCANE_RES", -- [1]
			"FIRE_RES", -- [2]
			"FROST_RES", -- [3]
			"NATURE_RES", -- [4]
			"SHADOW_RES", -- [5]
		},
		["nature resistance"] = {
			"NATURE_RES", -- [1]
		},
		["pvp resilience"] = {
			"RESILIENCE_RATING", -- [1]
		},
		["Nature Resist"] = {
			"NATURE_RES", -- [1]
		},
		["Increases ranged attack speed"] = false,
		["pvp power"] = {
			"PVP_POWER", -- [1]
		},
		["Damage Spells"] = {
			"SPELL_DMG", -- [1]
		},
		["increases your mastery"] = {
			"MASTERY_RATING", -- [1]
		},
		["% threat"] = {
			"MOD_THREAT", -- [1]
		},
		["Increases your PvP resilience"] = {
			"RESILIENCE_RATING", -- [1]
		},
		["increases healing"] = {
			"HEAL", -- [1]
		},
		["increases defense"] = {
			"DEFENSE_RATING", -- [1]
		},
		["Increases the Holy damage of your Judgments"] = false,
		["Increases your haste"] = {
			"MELEE_HASTE_RATING", -- [1]
			"RANGED_HASTE_RATING", -- [2]
			"SPELL_HASTE_RATING", -- [3]
		},
		["Healing"] = {
			"HEAL", -- [1]
		},
		["Increases damage done by Holy spells and effects"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["Increases damage done by Frost spells and effects"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["increases the damage done by fire spells and effects"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["shadow resistance"] = {
			"SHADOW_RES", -- [1]
		},
		["weapon damage"] = {
			"MELEE_DMG", -- [1]
		},
		["attack power when fighting undead"] = {
			"AP_UNDEAD", -- [1]
		},
		["Mastery"] = {
			"MASTERY_RATING", -- [1]
		},
		["Improves ranged hit"] = {
			"RANGED_HIT_RATING", -- [1]
		},
		["Increases your expertise"] = {
			"EXPERTISE_RATING", -- [1]
		},
		["Increases spell power"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["critical strike avoidance (ranged)"] = {
			"RANGED_CRIT_AVOID_RATING", -- [1]
		},
		["hit avoidance (ranged)"] = {
			"RANGED_HIT_AVOID_RATING", -- [1]
		},
		["Improves spell haste"] = {
			"SPELL_HASTE_RATING", -- [1]
		},
		["increases ranged attack speed"] = false,
		["increases your healing"] = {
			"HEAL", -- [1]
		},
		["hp"] = {
			"HEALTH", -- [1]
		},
		["expertise"] = {
			"EXPERTISE_RATING", -- [1]
		},
		["spell healing"] = {
			"HEAL", -- [1]
		},
		["ranged attack power"] = {
			"RANGED_AP", -- [1]
		},
		["Agility"] = {
			"AGI", -- [1]
		},
		["Increases your parry"] = {
			"PARRY_RATING", -- [1]
		},
		["damage"] = {
			"SPELL_DMG", -- [1]
		},
		["fishing skill increased"] = {
			"FISHING", -- [1]
		},
		["damage spells"] = {
			"SPELL_DMG", -- [1]
		},
		["attack power versus undead"] = {
			"AP_UNDEAD", -- [1]
		},
		["critical strike avoidance"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
			"RANGED_CRIT_AVOID_RATING", -- [2]
			"SPELL_CRIT_AVOID_RATING", -- [3]
		},
		["improves melee hit"] = {
			"MELEE_HIT_RATING", -- [1]
		},
		["critical strike (ranged)"] = {
			"RANGED_CRIT_RATING", -- [1]
		},
		["Increases attack power"] = {
			"AP", -- [1]
		},
		["hit avoidance (melee)"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
		},
		["agility"] = {
			"AGI", -- [1]
		},
		["healing"] = {
			"HEAL", -- [1]
		},
		["improves spell critical avoidance"] = {
			"SPELL_CRIT_AVOID_RATING", -- [1]
		},
		["experience gained is increased%"] = false,
		["increases the damage done by shadow spells and effects"] = {
			"SHADOW_SPELL_DMG", -- [1]
		},
		["Increases your dodge"] = {
			"DODGE_RATING", -- [1]
		},
		["Increases spell penetration"] = {
			"SPELLPEN", -- [1]
		},
		["improves hit avoidance"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
			"RANGED_HIT_AVOID_RATING", -- [2]
			"SPELL_HIT_AVOID_RATING", -- [3]
		},
		["mastery"] = {
			"MASTERY_RATING", -- [1]
		},
		["Spell Damage and Healing"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Hit (Spell)"] = {
			"SPELL_HIT_RATING", -- [1]
		},
		["Increases the damage done by Shadow spells and effects"] = {
			"SHADOW_SPELL_DMG", -- [1]
		},
		["haste (ranged)"] = {
			"RANGED_HASTE_RATING", -- [1]
		},
		["nature spell damage"] = {
			"NATURE_SPELL_DMG", -- [1]
		},
		["defense"] = {
			"DEFENSE_RATING", -- [1]
		},
		["increases spell power"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Increases your healing"] = {
			"HEAL", -- [1]
		},
		["Haste"] = {
			"MELEE_HASTE_RATING", -- [1]
			"RANGED_HASTE_RATING", -- [2]
			"SPELL_HASTE_RATING", -- [3]
		},
		["Increases damage done by Arcane spells and effects"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["Hit Avoidance"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
			"RANGED_HIT_AVOID_RATING", -- [2]
			"SPELL_HIT_AVOID_RATING", -- [3]
		},
		["improves ranged critical avoidance"] = {
			"RANGED_CRIT_AVOID_RATING", -- [1]
		},
		["arcane resist"] = {
			"ARCANE_RES", -- [1]
		},
		["Increases damage done by Shadow spells and effects"] = {
			"SHADOW_SPELL_DMG", -- [1]
		},
		["hit"] = {
			"MELEE_HIT_RATING", -- [1]
			"RANGED_HIT_RATING", -- [2]
			"SPELL_HIT_RATING", -- [3]
		},
		["Fishing skill increased"] = {
			"FISHING", -- [1]
		},
		["increases your parry"] = {
			"PARRY_RATING", -- [1]
		},
		["increases your shield block"] = {
			"BLOCK_RATING", -- [1]
		},
		["fire resistance"] = {
			"FIRE_RES", -- [1]
		},
		["spell power"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Fire Spell Damage"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["Improves spell critical strike"] = {
			"SPELL_CRIT_RATING", -- [1]
		},
		["Haste (Melee)"] = {
			"MELEE_HASTE_RATING", -- [1]
		},
		["Fire Resistance"] = {
			"FIRE_RES", -- [1]
		},
		["Increases damage done by Nature spells and effects"] = {
			"NATURE_SPELL_DMG", -- [1]
		},
		["Frost Spell Damage"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["Critical Strike Avoidance (Ranged)"] = {
			"RANGED_CRIT_AVOID_RATING", -- [1]
		},
		["hit (melee)"] = {
			"MELEE_HIT_RATING", -- [1]
		},
		["increases your dodge"] = {
			"DODGE_RATING", -- [1]
		},
		["increases spell penetration"] = {
			"SPELLPEN", -- [1]
		},
		["Damage per Second"] = {

			"DPS", -- [1]
		},
		["critical strike avoidance (melee)"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
		},
		["sec"] = false,
		["increases attack powerwhen fighting undead"] = {
			"AP_UNDEAD", -- [1]
		},
		["increases attack powerwhen fighting demons"] = {
			"AP_DEMON", -- [1]
		},
		["increases damage and healing done by magical spells and effects of all party members within 30 yards"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Herbalism; does not need to be equipped"] = {
			"HERBALISM", -- [1]
		},
		["increases damage done by frost spells and effects"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["frost resist"] = {
			"FROST_RES", -- [1]
		},
		["Mana Regeneration"] = {
			"COMBAT_MANA_REGEN", -- [1]
		},
		["critical strike avoidance (spell)"] = {
			"SPELL_CRIT_AVOID_RATING", -- [1]
		},
		["Hit Avoidance (Ranged)"] = {
			"RANGED_HIT_AVOID_RATING", -- [1]
		},
		["Healing Spells"] = {
			"HEAL", -- [1]
		},
		["Frost Damage"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["arcane resistance"] = {
			"ARCANE_RES", -- [1]
		},
		["increases the damage done by holy spells and effects"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["restores health per 5 sec"] = {
			"COMBAT_HEALTH_REGEN", -- [1]
		},
		["increases damage and healing done by magical spells and effects"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["arcane spell damage"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["Improves melee haste"] = {
			"MELEE_HASTE_RATING", -- [1]
		},
		["improves melee hit avoidance"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
		},
		["herbalism"] = {
			"HERBALISM", -- [1]
		},
		["damage and healing spells"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Increases damage done to Undead and Demons by magical spells and effects"] = {
			"SPELL_DMG_UNDEAD", -- [1]
			"SPELL_DMG_DEMON", -- [2]
		},
		["increases healing donemagical spells and effects"] = {
			"HEAL", -- [1]
		},
		["Fishing"] = {
			"FISHING", -- [1]
		},
		["% shield block value"] = {
			"MOD_BLOCK_VALUE", -- [1]
		},
		["Strength"] = {
			"STR", -- [1]
		},
		["parry"] = {
			"PARRY_RATING", -- [1]
		},
		["nature resist"] = {
			"NATURE_RES", -- [1]
		},
		["Improves melee critical avoidance"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
		},
		["armor"] = {
			"BONUS_ARMOR", -- [1]
		},
		["increases the damage done by nature spells and effects"] = {
			"NATURE_SPELL_DMG", -- [1]
		},
		["increases attack powerwhen fighting undead and demons"] = {
			"AP_UNDEAD", -- [1]
			"AP_DEMON", -- [2]
		},
		["health regeneration"] = {
			"COMBAT_HEALTH_REGEN", -- [1]
		},
		["healing and spell damage"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["fishing"] = {
			"FISHING", -- [1]
		},
		["increases your effective stealth level"] = {
			"STEALTH_LEVEL", -- [1]
		},
		["Increases the damage done by Nature spells and effects"] = {
			"NATURE_SPELL_DMG", -- [1]
		},
		["spell penetration"] = {
			"SPELLPEN", -- [1]
		},
		["Holy Resistance"] = {
			"HOLY_RES", -- [1]
		},
		["Restores mana per 5 sec"] = {
			"COMBAT_MANA_REGEN", -- [1]
		},
		["Holy Spell Damage"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["skinning"] = {
			"SKINNING", -- [1]
		},
		["fire damage"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["Your attacks ignoreof your opponent's armor"] = {
			"IGNORE_ARMOR", -- [1]
		},
		["Increases healing done by magical spells and effects of all party members within 30 yards"] = {
			"HEAL", -- [1]
		},
		["increases damage done to undead and demons by magical spells and effects"] = {
			"SPELL_DMG_UNDEAD", -- [1]
			"SPELL_DMG_DEMON", -- [2]
		},
		["improves melee haste"] = {
			"MELEE_HASTE_RATING", -- [1]
		},
		["mana"] = {
			"MANA", -- [1]

		},

		["arcane damage"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["Shadow Damage"] = {
			"SHADOW_SPELL_DMG", -- [1]
		},
		["increases the holy damage of your judgments"] = false,
		["increases healing done by magical spells and effects of all party members within 30 yards"] = {
			"HEAL", -- [1]
		},
		["your attacks ignoreof your opponent's armor"] = {
			"IGNORE_ARMOR", -- [1]

		},
		["damage donefor all magical spells"] = {

			"SPELL_DMG", -- [1]
		},
		["Increases your effective stealth level"] = {
			"STEALTH_LEVEL", -- [1]
		},
		["Spell Healing"] = {
			"HEAL", -- [1]
		},
		["critical strike"] = {
			"MELEE_CRIT_RATING", -- [1]
			"RANGED_CRIT_RATING", -- [2]
			"SPELL_CRIT_RATING", -- [3]
		},
		["Critical Strike Avoidance (Melee)"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
		},
		["Nature Resistance"] = {
			"NATURE_RES", -- [1]
		},
		["improves ranged hit avoidance"] = {
			"RANGED_HIT_AVOID_RATING", -- [1]
		},
		["hit avoidance"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
			"RANGED_HIT_AVOID_RATING", -- [2]
			"SPELL_HIT_AVOID_RATING", -- [3]
		},
		["increases the critical effect chance of your flash of light%"] = false,
		["armor penetration"] = {
			"ARMOR_PENETRATION_RATING", -- [1]
		},
		["Increases healing done"] = {
			"HEAL", -- [1]
		},
		["shadow damage"] = {
			"SHADOW_SPELL_DMG", -- [1]
		},
		["improves melee critical avoidance"] = {
			"MELEE_CRIT_AVOID_RATING", -- [1]
		},
		["Arcane Spell Damage"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["improves spell haste"] = {
			"SPELL_HASTE_RATING", -- [1]
		},
		["Improves melee critical strike"] = {
			"MELEE_CRIT_RATING", -- [1]
		},
		["Increases damage and healing done by magical spells and effects"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Increases damage done to Undead by magical spells and effects"] = {
			"SPELL_DMG_UNDEAD", -- [1]
		},
		["restores mana per 5 sec"] = {
			"COMBAT_MANA_REGEN", -- [1]
		},
		["healing spells"] = {
			"HEAL", -- [1]
		},
		["increases your armor penetration"] = {
			"ARMOR_PENETRATION_RATING", -- [1]
		},
		["haste (melee)"] = {
			"MELEE_HASTE_RATING", -- [1]
		},
		["spell damage and healing"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Arcane Resistance"] = {
			"ARCANE_RES", -- [1]
		},
		["Skinning; does not need to be equipped"] = {
			"SKINNING", -- [1]
		},
		["Increases attack powerwhen fighting Undead and Demons"] = {
			"AP_UNDEAD", -- [1]
			"AP_DEMON", -- [2]
		},
		["Increases attack powerin Cat, Bear, Dire Bear, and Moonkin forms only"] = {
			"FERAL_AP", -- [1]
		},
		["herbalism; does not need to be equipped"] = {
			"HERBALISM", -- [1]
		},
		["% Threat"] = {
			"MOD_THREAT", -- [1]
		},
		["Defense"] = {
			"DEFENSE_RATING", -- [1]
		},
		["Increases healing donemagical spells and effects"] = {
			"HEAL", -- [1]
		},
		["Experience gained from killing monsters and completing quests increased%"] = false,
		["increases damage done by fire spells and effects"] = {
			"FIRE_SPELL_DMG", -- [1]
		},
		["increases damage done by nature spells and effects"] = {
			"NATURE_SPELL_DMG", -- [1]
		},
		["Ranged Attack Power"] = {
			"RANGED_AP", -- [1]
		},
		["scope (damage)"] = {
			"RANGED_DMG", -- [1]
		},
		["increases the damage done by frost spells and effects"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["Shadow Resist"] = {
			"SHADOW_RES", -- [1]
		},
		["damage per second"] = {
			"DPS", -- [1]
		},
		["Increases the critical effect chance of your Flash of Light%"] = false,
		["improves ranged haste"] = {
			"RANGED_HASTE_RATING", -- [1]
		},
		["Increases attack powerwhen fighting Undead.  It also allows the acquisition of Scourgestones on behalf of the Argent Dawn"] = {
			"AP_UNDEAD", -- [1]
		},
		["PvP Power"] = {
			"PVP_POWER", -- [1]
		},
		["haste (spell)"] = {
			"SPELL_HASTE_RATING", -- [1]
		},
		["mining; does not need to be equipped"] = {
			"MINING", -- [1]
		},
		["Increases damage and healing done by magical spells and effects of all party members within 30 yards"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["Spell Penetration"] = {
			"SPELLPEN", -- [1]

		},
		["Improves melee hit avoidance"] = {
			"MELEE_HIT_AVOID_RATING", -- [1]
		},
		["increases attack power"] = {
			"AP", -- [1]
		},
		["Increases defense"] = {
			"DEFENSE_RATING", -- [1]
		},
		["increases healing done"] = {
			"HEAL", -- [1]
		},
		["spirit"] = {
			"SPI", -- [1]
		},
		["Increases the damage done by Arcane spells and effects"] = {
			"ARCANE_SPELL_DMG", -- [1]
		},
		["improves spell critical strike"] = {
			"SPELL_CRIT_RATING", -- [1]
		},
		["PvP Resilience"] = {
			"RESILIENCE_RATING", -- [1]
		},
		["Damage and Healing Spells"] = {
			"SPELL_DMG", -- [1]
			"HEAL", -- [2]
		},
		["holy spell damage"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["increases your critical strike"] = {
			"MELEE_CRIT_RATING", -- [1]
			"RANGED_CRIT_RATING", -- [2]
			"SPELL_CRIT_RATING", -- [3]
		},
		["healing Spells"] = {
			"HEAL", -- [1]
		},
		["Experience gained is increased%"] = false,
		["critical strike (spell)"] = {
			"SPELL_CRIT_RATING", -- [1]
		},
		["Increases Healing"] = {
			"HEAL", -- [1]
		},
		["Armor Penetration"] = {
			"ARMOR_PENETRATION_RATING", -- [1]
		},
		["arcane and fire spell power"] = {
			"ARCANE_SPELL_DMG", -- [1]
			"FIRE_SPELL_DMG", -- [2]
		},
		["Health Regeneration"] = {
			"COMBAT_HEALTH_REGEN", -- [1]
		},
		["all stats"] = {
			"STR", -- [1]
			"AGI", -- [2]
			"STA", -- [3]
			"INT", -- [4]
			"SPI", -- [5]
		},
		["Increases your hit"] = {
			"MELEE_HIT_RATING", -- [1]
			"RANGED_HIT_RATING", -- [2]
			"SPELL_HIT_RATING", -- [3]
		},
		["Critical Strike Avoidance (Spell)"] = {
			"SPELL_CRIT_AVOID_RATING", -- [1]
		},
		["strength"] = {
			"STR", -- [1]
		},
		["Parry"] = {
			"PARRY_RATING", -- [1]
		},
		["Increases the damage done by Holy spells and effects"] = {
			"HOLY_SPELL_DMG", -- [1]
		},
		["Restores health per 5 sec"] = {
			"COMBAT_HEALTH_REGEN", -- [1]
		},
		["Increases your shield block"] = {
			"BLOCK_RATING", -- [1]
		},
		["Hit"] = {
			"MELEE_HIT_RATING", -- [1]
			"RANGED_HIT_RATING", -- [2]
			"SPELL_HIT_RATING", -- [3]
		},
		["increases damage done by shadow spells and effects"] = {
			"SHADOW_SPELL_DMG", -- [1]

		},
		["All Resistances"] = {
			"ARCANE_RES", -- [1]
			"FIRE_RES", -- [2]
			"FROST_RES", -- [3]
			"NATURE_RES", -- [4]
			"SHADOW_RES", -- [5]
		},
		["frost spell damage"] = {
			"FROST_SPELL_DMG", -- [1]
		},
		["improves ranged hit"] = {
			"RANGED_HIT_RATING", -- [1]
		},
	},
}
]]
