local tablex = require("core.tablex")
local animationUtil = require("ui.animationUtil")
local attributeUtil = require("ui.attributeUtil")
local buttonUtil = require("ui.buttonUtil")
local card = require("ui.card")
local characterUtil = require("ui.characterUtil")
local colors = require("ui.colors")
local colorUtil = require("ui.colorUtil")
local consts = require("ui.consts")
local frame = require("ui.frame")
local frameGroup = require("ui.frameGroup")
local lamsConsts = require("ui.lamsConsts")
local pickupUtil = require("ui.pickupUtil")
local pickupConsts = require("ui.pickupConsts")
local resourceConsts = require("ui.resourceConsts")
local resourceUtil = require("ui.resourceUtil")
local recipeUtil = require("ui.recipeUtil")
local runeUtil = require("ui.runeUtil")
local util = require("ui.util")
local UI = game.UI
local SetText = game.UI.SetText
local GetTextHandle = util.GetTextHandle
local Pickup = game.Pickup
local FRAMEINDEX = {
  header = 0,
  description = 1,
  separator1 = 2,
  attributes = 3,
  bottomBorder_attributes = 4,
  perks = 5,
  socketHeader = 6,
  runeslot1 = 7,
  separator2 = 8,
  runeslot2 = 9,
  separator3 = 10,
  runeslot3 = 11,
  separatorBottom = 12,
  equipBarBottom = 13,
  buttons = 14
}
local MAX_UPGRADE_PIPS = 4
local rightHandCardOffset = engine.Vector.New(21.5, 0, 0)
local upgradeSlotFadeTransitionTime = 0
local StatsCard = {}
setmetatable(StatsCard, {
  __index = card.Card
})
function CreateSlotObj(goRefnode)
  local table = {}
  table.gameObjects = {}
  table.textHandles = {}
  table.stats = {}
  local gameObjects = table.gameObjects
  local textHandles = table.textHandles
  local stats = table.stats
  gameObjects.goRoot = goRefnode:FindSingleGOByName("Root_RuneSlot")
  gameObjects.goDescriptionText = goRefnode:FindSingleGOByName("description_text")
  gameObjects.goDescriptionText_Center = goRefnode:FindSingleGOByName("description_text_center")
  gameObjects.iconRefnode = {}
  gameObjects.iconRefnode.goRefnode = goRefnode:FindSingleGOByName("upgradeSlotIcon")
  gameObjects.iconRefnode.goRoot = gameObjects.iconRefnode.goRefnode.Child
  gameObjects.iconRefnode.goPerkIndicator = gameObjects.iconRefnode.goRoot:FindSingleGOByName("perkIndicator")
  gameObjects.goUpgradeSlotArrow_Right = gameObjects.goRoot:FindSingleGOByName("slotUpgradeArrow_Right")
  gameObjects.goUpgradeSlotArrow = gameObjects.goRoot:FindSingleGOByName("slotUpgradeArrow")
  textHandles.thDescription = GetTextHandle(gameObjects.goDescriptionText)
  textHandles.thDescription_Center = GetTextHandle(gameObjects.goDescriptionText_Center)
  textHandles.thSlotUpgrade_Left = GetTextHandle(gameObjects.goRoot, "slotUpgrade_left")
  textHandles.thSlotUpgrade_Right = GetTextHandle(gameObjects.goRoot, "slotUpgrade_right")
  for i = 1, consts.MAX_STATS_PER_SLOT do
    stats[i] = {}
    stats[i].goRefnode = gameObjects.goRoot:FindSingleGOByName("Stat" .. tostring(i))
    stats[i].goRoot = stats[i].goRefnode:FindSingleGOByName("Root")
    stats[i].textHandles = {}
    stats[i].textHandles.name = GetTextHandle(stats[i].goRoot, "AttributeName")
    stats[i].textHandles.number = GetTextHandle(stats[i].goRoot, "AttributeNumber")
    stats[i].goRefnode:Show()
    stats[i].goRoot:Show()
    local jid_weaponSpecial = stats[i].goRoot:GetJointIndex("WeaponSpecial")
    stats[i].goRoot:HideJoint(jid_weaponSpecial)
  end
  gameObjects.goRoot:Show()
  gameObjects.goDescriptionText:Show()
  gameObjects.iconRefnode.goRefnode:Show()
  gameObjects.iconRefnode.goRoot:Show()
  gameObjects.iconRefnode.goPerkIndicator:Show()
  return table
end
function StatsCard.New(gameObjectReference)
  assert(gameObjectReference ~= nil, "GameObject reference entered into StatsCard constructor was nil.")
  local newTable = {
    _gameObject = gameObjectReference,
    _currentItemName = nil,
    _activeMinorAttributeCount = 0,
    _position = nil,
    _isVendorStatsCard = false,
    _frameGroup = nil,
    _frameGroupSockets = nil,
    children = {},
    upgradeSlots = {},
    textHandles = {}
  }
  local mt = {__index = StatsCard}
  setmetatable(newTable, mt)
  return newTable
end
function StatsCard:Init(isVendorStatsCard, isComparisonCard, args)
  self._isVendorStatsCard = isVendorStatsCard
  self._isComparisonCard = isComparisonCard
  if args ~= nil then
    self._positionOffset = util.GetValueWithDefault(args.positionOffset, nil)
    self._goHomeLocalPosition = util.GetValueWithDefault(args.goHomeLocalPosition, nil)
    self._hideUpgradeSlots = util.GetValueWithDefault(args.HideUpgradeSlots, false)
    self._comparisonCard = util.GetValueWithDefault(args.ComparisonCard, nil)
    self._nonComparisonCard = util.GetValueWithDefault(args.NonComparisonCard, nil)
  end
  if self._isComparisonCard then
    assert(self._comparisonCard == nil, "Comparison card should not be assigned for a comparison card.")
  else
    assert(self._nonComparisonCard == nil, "Non comparison card should not be assigned for a non comparison card.")
  end
  local children = self.children
  local GO = self._gameObject
  local textHandles = self.textHandles
  children.goRoot = GO:FindSingleGOByName("selectedItem_details")
  children.goButtonPromptBlur = GO:FindSingleGOByName("ButtonPromptBlur")
  children.goBackframe = GO:FindSingleGOByName("BackFrameGroup")
  children.goBodyBacking = GO:FindSingleGOByName("body_backing")
  children.goHeader = GO:FindSingleGOByName("CardComponent_Header")
  children.goHeaderRoot = children.goHeader.Child
  children.goHeaderBacking = children.goHeader:FindSingleGOByName("mapMenu_Header")
  children.goNonEquippedBacking = GO:FindSingleGOByName("nonEquippedBacking")
  children.goEquippedBacking = GO:FindSingleGOByName("equippedBacking")
  children.goRarity = GO:FindSingleGOByName("rarity")
  children.goTopBorder = GO:FindSingleGOByName("statscard_topborder")
  children.goTopBorderWings = GO:FindSingleGOByName("statscard_topborderwings")
  children.goDescriptionGroup = GO:FindSingleGOByName("descriptionGroup")
  children.goDescriptionText = children.goDescriptionGroup:FindSingleGOByName("DescriptionText")
  children.goDescriptionBackingAnimateMe = GO:FindSingleGOByName("designBacking_Attributes")
  children.goAttributesGroup = GO:FindSingleGOByName("attributesGroup")
  children.goReinforcePreviewGroup = GO:FindSingleGOByName("ReinforcementPreview_Group")
  children.goReinforcePreviewArrow = GO:FindSingleGOByName("ReinforcementUpgradeArrow")
  children.goIconGroup = GO:FindSingleGOByName("IconHousingGroup")
  children.goLevel_BigPlus = GO:FindSingleGOByName("Level_BigPlus")
  children.goLevelNum = GO:FindSingleGOByName("LevelNum")
  children.goUpgradePipsGroup = GO:FindSingleGOByName("UpgradePips")
  children.upgradePips = {}
  for i = 1, MAX_UPGRADE_PIPS do
    children.upgradePips[i] = children.goUpgradePipsGroup:FindSingleGOByName("UpgradePip_" .. tostring(i))
  end
  children.goIconArmorHousing = GO:FindSingleGOByName("Armor_IconHousing")
  children.goIconTalismanHousing = GO:FindSingleGOByName("Talisman_IconHousing")
  children.goIconRuneHousing = GO:FindSingleGOByName("Rune_IconHousing")
  children.goPerksGroup = GO:FindSingleGOByName("perksGroup")
  children.goPerksStandardText = GO:FindSingleGOByName("PerkItemInfo")
  textHandles.thTalismanPerkHeader = util.GetTextHandle(children.goPerksGroup, "PerkTalismanHeader")
  textHandles.thTalismanPerkDesc = util.GetTextHandle(children.goPerksGroup, "PerkTalismanDesc")
  children.goButtonPromptGrp = GO:FindSingleGOByName("buttonPrompts")
  children.goButtonPrompt_Left = GO:FindSingleGOByName("ButtonPrompt_Left")
  children.goButtonPrompt_Middle = GO:FindSingleGOByName("ButtonPrompt_Middle")
  children.goButtonPrompt_Right = GO:FindSingleGOByName("ButtonPrompt_Right")
  children.goEquipBarBottom = GO:FindSingleGOByName("equipBar_Bottom")
  children.goGlows = {}
  children.goGlows[1] = GO:FindSingleGOByName("glow_attributesBottom")
  children.goSocketHeader = GO:FindSingleGOByName("SocketHeader_Separator")
  children.goDescriptionAttributeSeparator = GO:FindSingleGOByName("separator_descriptionBottom")
  children.goSeparatorRunes = {}
  children.goSeparatorRunes[1] = GO:FindSingleGOByName("separator_Slot1")
  children.goSeparatorRunes[2] = GO:FindSingleGOByName("separator_Slot2")
  children.goSeparatorBottom = GO:FindSingleGOByName("separator_bottom")
  for slotIndex = 1, pickupConsts.MAX_SOCKET_COUNT do
    self.upgradeSlots[slotIndex] = CreateSlotObj(self._gameObject:FindSingleGOByName("RuneSlot" .. tostring(slotIndex)))
  end
  for _, gameObj in pairs(self.children) do
    if type(gameObj) == "table" then
      for _, childObj in pairs(gameObj) do
        assert(childObj ~= nil, "Game object in stats card was not found.")
      end
    end
    assert(gameObj ~= nil, "Game object in stats card was not found.")
  end
  textHandles.header = GetTextHandle(children.goHeaderRoot, "Text")
  textHandles.description = GetTextHandle(children.goDescriptionText)
  textHandles.rarity = GetTextHandle(children.goRarity)
  textHandles.buttonPrompt_Left = GetTextHandle(children.goButtonPrompt_Left)
  UI.SetTextIsClickable(textHandles.buttonPrompt_Left)
  textHandles.buttonPrompt_Middle = GetTextHandle(children.goButtonPrompt_Middle)
  UI.SetTextIsClickable(textHandles.buttonPrompt_Middle)
  textHandles.buttonPrompt_Right = GetTextHandle(children.goButtonPrompt_Right)
  UI.SetTextIsClickable(textHandles.buttonPrompt_Right)
  textHandles.perks = GetTextHandle(children.goPerksStandardText)
  textHandles.mainAttribute = GetTextHandle(children.goLevelNum)
  textHandles.mainAttributePreview = GetTextHandle(children.goReinforcePreviewGroup, "UpgradeNum")
  textHandles.weaponBigAttr = GetTextHandle(children.goAttributesGroup, "WeaponStatText")
  children.goStats = {}
  children.goStats_upgradeArrow = {}
  children.goUpgradeGroup = {}
  textHandles.attributes = {}
  for i = 1, #pickupConsts.Attributes do
    local goStat = children.goAttributesGroup:FindSingleGOByName("Stat" .. i)
    local goStatRoot = goStat:FindSingleGOByName("Root")
    goStat:Show()
    goStatRoot:Show()
    local goStat_UpgradeArrow = goStatRoot:FindSingleGOByName("slotupgradeArrow")
    local goStat_UpgradeGroup = goStatRoot:FindSingleGOByName("UpgradeStat")
    textHandles.attributes[i] = {}
    local attrGroup = textHandles.attributes[i]
    attrGroup.name = GetTextHandle(goStatRoot, "AttributeName")
    attrGroup.leftNumber = GetTextHandle(goStatRoot, "LeftNumber")
    attrGroup.rightNumber = GetTextHandle(goStatRoot, "RightNumber")
    children.goStats[i] = goStat
    children.goStats_upgradeArrow[i] = goStat_UpgradeArrow
    children.goUpgradeGroup[i] = goStat_UpgradeGroup
  end
  local showAll = true
  self:ShowCard(showAll)
  self:HideCard()
  children.goEquipBarBottom:Hide()
  self.backFrame = frame.Frame.New(self.children.goBackframe)
  self.bodyBackingFrame = frame.Frame.New(self.children.goBodyBacking)
  self.attributeBackingFrame = frame.Frame.New(self.children.goAttributesGroup)
  self.perkBackingFrame = frame.Frame.New(self.children.goPerksGroup)
  self._frameGroup = frameGroup.FrameGroup.New()
  self._frameGroup:SetPadding(0)
  self._frameGroup:SetBorderPadding(0)
  self._frameGroup:AddRoot(frame.Frame.New(children.goHeader))
  self._frameGroup:AddNode(frame.Frame.New(children.goDescriptionGroup), FRAMEINDEX.description)
  self._frameGroup:AddNode(frame.Frame.New(children.goDescriptionAttributeSeparator), FRAMEINDEX.separator1)
  self._frameGroup:AddNode(frame.Frame.New(children.goAttributesGroup), FRAMEINDEX.attributes)
  self._frameGroup:AddNode(frame.Frame.New(children.goGlows[1]), FRAMEINDEX.bottomBorder_attributes)
  self._frameGroup:AddNode(frame.Frame.New(children.goPerksGroup), FRAMEINDEX.perks)
  self._frameGroup:AddNode(frame.Frame.New(children.goSocketHeader), FRAMEINDEX.socketHeader)
  self._frameGroup:AddNode(frame.Frame.New(self.upgradeSlots[1].gameObjects.goRoot), FRAMEINDEX.runeslot1)
  self._frameGroup:AddNode(frame.Frame.New(children.goSeparatorRunes[1]), FRAMEINDEX.separator2)
  self._frameGroup:AddNode(frame.Frame.New(self.upgradeSlots[2].gameObjects.goRoot), FRAMEINDEX.runeslot2)
  self._frameGroup:AddNode(frame.Frame.New(children.goSeparatorRunes[2]), FRAMEINDEX.separator3)
  self._frameGroup:AddNode(frame.Frame.New(self.upgradeSlots[3].gameObjects.goRoot), FRAMEINDEX.runeslot3)
  self._frameGroup:AddNode(frame.Frame.New(children.goSeparatorBottom), FRAMEINDEX.separatorBottom)
  self._frameGroup:AddNode(frame.Frame.New(children.goEquipBarBottom), FRAMEINDEX.equipBarBottom)
  self._frameGroup:AddNode(frame.Frame.New(children.goButtonPromptGrp), FRAMEINDEX.buttons)
  self.backFrame:SetInitialHeight(28.25)
  self.bodyBackingFrame:SetInitialHeight(1)
  self.attributeBackingFrame:SetInitialHeight(6)
  self.perkBackingFrame:SetInitialHeight(4)
  self._frameGroup:GetRoot():SetHeight(3)
  self._frameGroup:GetNode(FRAMEINDEX.description):SetHeight(2)
  self._frameGroup:GetNode(FRAMEINDEX.separator1):SetHeight(0)
  self._frameGroup:GetNode(FRAMEINDEX.attributes):SetHeight(6)
  self._frameGroup:GetNode(FRAMEINDEX.bottomBorder_attributes):SetHeight(0)
  self._frameGroup:GetNode(FRAMEINDEX.perks):SetHeight(4)
  self._frameGroup:GetNode(FRAMEINDEX.socketHeader):SetHeight(0.5)
  local runeSlotHeight = 5.25
  self._frameGroup:GetNode(FRAMEINDEX.runeslot1):SetInitialHeight(runeSlotHeight)
  self._frameGroup:GetNode(FRAMEINDEX.separator2):SetHeight(0)
  self._frameGroup:GetNode(FRAMEINDEX.runeslot2):SetInitialHeight(runeSlotHeight)
  self._frameGroup:GetNode(FRAMEINDEX.separator3):SetHeight(0)
  self._frameGroup:GetNode(FRAMEINDEX.runeslot3):SetInitialHeight(runeSlotHeight)
  self._frameGroup:GetNode(FRAMEINDEX.separatorBottom):SetHeight(0)
  self._frameGroup:GetNode(FRAMEINDEX.equipBarBottom):SetHeight(0)
  self._frameGroup:GetNode(FRAMEINDEX.buttons):SetHeight(2)
end
function StatsCard:PlayFlourishOnEquip()
  self._playFlourishOnEquip = true
end
function StatsCard:ValidateArgs(itemName, itemType, character, itemIsEquipped, args)
  assert(itemType ~= nil, "You need to pass in a resourceConsts.RESOURCE_FLAG to StatsCard:SetItem for parameter: 'itemType'")
  if itemType ~= resourceConsts.RESOURCE_FLAG_PICKUP and itemType ~= resourceConsts.RESOURCE_FLAG_RECIPE and itemType ~= resourceConsts.RESOURCE_FLAG_RUNE and itemType ~= resourceConsts.RESOURCE_FLAG_RESOURCE then
    assert(false, "Unsupported item type passed into StatsCard: " .. tostring(itemType))
  end
  assert(character ~= nil or itemType == resourceConsts.RESOURCE_FLAG_RESOURCE, "No character passed into SetItem")
  if character ~= nil then
    assert(character == pickupConsts.TAG_PICKUP_SON or character == pickupConsts.TAG_PICKUP_KRATOS, "Character passed into StatsCard.lua must be TAG_PICKUP_SON or TAG_PICKUP_KRATOS.")
  end
end
local g_refreshArgs
function StatsCard:SetItem(itemName, itemType, character, itemIsEquipped, args)
  if g_refreshArgs == nil then
    g_refreshArgs = {
      ItemName = itemName,
      ItemType = itemType,
      Character = character,
      ItemIsEquipped = itemIsEquipped,
      Args = args
    }
  else
    g_refreshArgs.ItemName = itemName
    g_refreshArgs.ItemType = itemType
    g_refreshArgs.Character = character
    g_refreshArgs.ItemIsEquipped = itemIsEquipped
    g_refreshArgs.Args = args
  end
  self:ValidateArgs(itemName, itemType, character, itemIsEquipped, args)
  self:ShowCard()
  if itemName == nil then
    self:ClearCard()
    return
  end
  local children = self.children
  local newItemName = self:GetItemName(itemName, itemType)
  self._itemType = itemType
  if args == nil then
    args = {}
  end
  self:AnimateTransitionOnNewItem(newItemName, itemType, args.DoDefaultTransitionAnim)
  local stage, maxStage
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP then
    stage = math.max(pickupUtil.GetProfileStage(itemName), 0)
    maxStage = pickupUtil.GetMaxStage(itemName)
  end
  self.forceDisplayCurrentStage = util.GetValueWithDefault(args.ForceDisplayCurrentStage, false)
  local isReinforcementRecipe = false
  local reinforcementRecipePickup
  if itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    local recipeName = recipeUtil.RecipeListItem_GetRecipeName(self._originalItemName)
    stage = 0
    reinforcementRecipePickup = recipeUtil.RecipeListItem_GetReinforcementPickupName(self._originalItemName)
    isReinforcementRecipe = reinforcementRecipePickup ~= nil
    if isReinforcementRecipe then
      stage = math.max(pickupUtil.GetProfileStage(reinforcementRecipePickup), 0)
      maxStage = pickupUtil.GetMaxStage(reinforcementRecipePickup)
      self._recipeOutputType = resourceConsts.RESOURCE_FLAG_PICKUP
      assert(reinforcementRecipePickup ~= nil, "No associated pickup was found for a reinforcement recipe: " .. tostring(recipeName) .. " Ensure that one is defined by marking the recipe input containing the pickup as non-consumable.")
      assert(-1 < stage, "Player doesn't possess pickup: " .. tostring(self._currentItemName) .. ",  for reinforcement recipe: " .. tostring(recipeName))
    else
      maxStage = pickupUtil.GetMaxStage(newItemName)
      if recipeUtil.RecipeIsForBuy(recipeName) or recipeUtil.RecipeIsForSell(recipeName) then
        stage = math.max(pickupUtil.GetProfileStage(newItemName), 0)
      end
    end
  end
  local displayData = {}
  self._currentDisplayData = displayData
  self._currentItemName = newItemName
  displayData.RecipeOutputItem = itemType == resourceConsts.RESOURCE_FLAG_RECIPE and self._recipeOutputName or nil
  displayData.RecipeOutputType = itemType == resourceConsts.RESOURCE_FLAG_RECIPE and self._recipeOutputType or nil
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP or itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    local weaponCheckResource = itemType == resourceConsts.RESOURCE_FLAG_RECIPE and displayData.RecipeOutputItem or recipeUtil.RecipeListItem_GetRecipeName(itemName)
    if isReinforcementRecipe then
      weaponCheckResource = reinforcementRecipePickup
    end
    displayData.ItemIsWeapon = resourceUtil.HasFlag(weaponCheckResource, "PrimaryWeapon")
    if displayData.ItemIsWeapon then
      displayData.WeaponAttrName = self:GetWeaponAttributeName(weaponCheckResource)
      if isReinforcementRecipe then
        displayData.IsWeaponReinforcement = true
      end
    end
  end
  displayData.Header = self:GetHeader(itemName, itemType, stage)
  displayData.Description, displayData.DescriptionHeight = self:GetDescription(itemName, itemType, displayData.RecipeOutputType, reinforcementRecipePickup)
  displayData.Rarity = self:GetRarity(self._currentItemName, itemType)
  displayData.ItemType = itemType
  displayData.ReinforcementRecipePickup = reinforcementRecipePickup
  displayData.Stage = stage
  displayData.MaxStage = maxStage
  displayData.Character = character
  displayData.Attributes = self:GetAttributes(self._currentItemName, itemType, reinforcementRecipePickup, stage, character)
  displayData.Perks = self:GetPerks(reinforcementRecipePickup ~= nil and reinforcementRecipePickup or self._currentItemName, itemType, stage, isReinforcementRecipe)
  displayData.UpgradeSlots = self:GetUpgradeSlots(reinforcementRecipePickup ~= nil and reinforcementRecipePickup or self._currentItemName, itemType, stage, isReinforcementRecipe)
  local itemToCheckForEquipped = reinforcementRecipePickup ~= nil and reinforcementRecipePickup or self._currentItemName
  if itemIsEquipped ~= nil then
    displayData.MarkAsEquipped = itemIsEquipped
  else
    displayData.MarkAsEquipped = characterUtil.ItemIsEquipped(itemToCheckForEquipped)
  end
  displayData.MainStat, displayData.MainStat_Preview = self:GetPowerLevel(self._currentItemName, stage, itemType, character, reinforcementRecipePickup)
  displayData.IsTalisman = self:GetIsTalisman(reinforcementRecipePickup or self._currentItemName, itemType)
  displayData.IsWeaponComponent = self:GetIsWeaponComponent(reinforcementRecipePickup or self._currentItemName, itemType)
  displayData.IsUpgradeCard = isReinforcementRecipe
  displayData.icon = self._currentItemName
  if displayData.IsTalisman then
    local itemToCheck
    if pickupUtil.HasTag(reinforcementRecipePickup or self._currentItemName, pickupConsts.TAG_PICKUP_TALISMAN_PASSIVE) then
      displayData.TalismanTag = lamsConsts.TalismanPassivePrompt
    elseif pickupUtil.HasTag(reinforcementRecipePickup or self._currentItemName, pickupConsts.TAG_PICKUP_TALISMAN_ACTIVE) then
      displayData.TalismanTag = lamsConsts.TalismanActivePrompt
    elseif pickupUtil.HasTag(reinforcementRecipePickup or self._currentItemName, pickupConsts.TAG_PICKUP_TALISMAN_ACTIVATION_FOCUSMODE) then
      displayData.TalismanTag = lamsConsts.TalismanActivationPrompt_FocusMode
    elseif pickupUtil.HasTag(reinforcementRecipePickup or self._currentItemName, pickupConsts.TAG_PICKUP_TALISMAN_ACTIVATION_REACTRECOVER) then
      displayData.TalismanTag = lamsConsts.TalismanActivationPrompt_ReactRecover
    end
  end
  if displayData.IsWeaponComponent then
    displayData.WeaponComponentDescription = Pickup.GetSecondDisplayDescriptionId(reinforcementRecipePickup or self._currentItemName)
  end
  self:SetDisplayData(displayData)
  if self._comparisonCard then
    self._comparisonCard:RefreshDisplayData()
  end
  if self._nonComparisonCard then
    self._nonComparisonCard:RefreshDisplayData()
  end
end
function StatsCard:RefreshCard()
  if g_refreshArgs then
    self:SetItem(g_refreshArgs.ItemName, g_refreshArgs.ItemType, g_refreshArgs.Character, g_refreshArgs.ItemIsEquipped, g_refreshArgs.Args)
  end
end
function StatsCard:RefreshDisplayData()
  if self:IsActive() and self._currentDisplayData ~= nil then
    self._refreshingStats = true
    self:SetDisplayData(self._currentDisplayData)
  end
end
function StatsCard:GetDisplayData()
  return self._currentDisplayData
end
function StatsCard:SetDisplayData(displayData)
  assert(type(displayData == "table"))
  local textHandles = self.textHandles
  local children = self.children
  self:SetHeader(displayData.Header)
  self:SetEquipped(displayData.MarkAsEquipped, displayData.ItemType)
  self:SetRarity(displayData.Rarity, textHandles.rarity)
  self:SetDescription(displayData, displayData.Description, textHandles.description, displayData.ItemType, displayData.DescriptionHeight)
  self:SetAttributes(displayData)
  self:SetUpgradePips(displayData)
  self:ClearPerks()
  if displayData.IsTalisman then
    self:SetTalismanPerks(displayData)
  elseif displayData.IsWeaponComponent then
    self:SetWeaponComponentPerks(displayData)
  elseif displayData.Character == pickupConsts.TAG_PICKUP_KRATOS then
    self:SetStandardPerks(displayData)
  else
    self:SetPerks(displayData, textHandles.perks, self._frameGroup:GetNode(FRAMEINDEX.perks))
  end
  self:SetUpgradeSlots(displayData.UpgradeSlots, textHandles.upgradeSlots, self._frameGroup:GetNode(FRAMEINDEX.socketHeader), {
    self._frameGroup:GetNode(FRAMEINDEX.runeslot1),
    self._frameGroup:GetNode(FRAMEINDEX.runeslot2),
    self._frameGroup:GetNode(FRAMEINDEX.runeslot3)
  }, displayData.ItemType == resourceConsts.RESOURCE_FLAG_RECIPE, displayData.IsUpgradeCard)
  if self._isVendorStatsCard == false or self._isVendorStatsCard == nil then
    self._meetsMoveRequirements = displayData.perksExist == true and displayData.UpgradeSlots.SlotCount == 3 and displayData.numOfNonzeroAttributes == 6
    local shouldMove = self._meetsMoveRequirements
    local hasActivePartnerCard = self._comparisonCard and self._comparisonCard:IsActive() or self._nonComparisonCard ~= nil and self._nonComparisonCard:IsActive()
    if hasActivePartnerCard then
      local partnerCard = self._comparisonCard or self._nonComparisonCard
      shouldMove = shouldMove or partnerCard._meetsMoveRequirements
      partnerCard:AdjustPositionForHeight(shouldMove)
    end
    self:AdjustPositionForHeight(shouldMove)
  end
  self:SetBodyBackingScaling()
  self._frameGroup:Update()
  self._frameGroup:SetBackframeScale(self.backFrame)
  self._refreshingStats = false
end
function StatsCard:AdjustPositionForHeight(shouldMove)
  local vTargetPos = shouldMove and engine.Vector.New(0, 1.35, 0) or engine.Vector.New(0, 0, 0)
  local translationTime = 0
  local useWorldSpace = false
  local interplationMode = consts.Interp_Smoothstep
  UI.SetGOTransformInterpolated(self.children.goRoot, vTargetPos, translationTime, useWorldSpace, interplationMode)
end
function StatsCard:SetEquipped(markAsEquipped, itemType)
  local oldEquippedValue = self._equippedEnabled
  if itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    if markAsEquipped then
      self._equippedEnabled = true
      self.children.goEquippedBacking:Show()
      self.children.goNonEquippedBacking:Show()
    else
      self._equippedEnabled = false
      self.children.goEquippedBacking:Hide()
      self.children.goNonEquippedBacking:Show()
    end
  elseif markAsEquipped then
    self._equippedEnabled = true
    self.children.goEquippedBacking:Show()
    self.children.goNonEquippedBacking:Show()
  else
    self._equippedEnabled = false
    self.children.goEquippedBacking:Hide()
    self.children.goNonEquippedBacking:Show()
  end
  if self._equippedEnabled and not self._refreshingStats then
    if self._playFlourishOnEquip and oldEquippedValue == false and self._equippedEnabled then
      local animRate = 1
      local pStart = 0
      local pEnd = 1
      UI.Anim(self.children.goEquippedBacking, consts.AS_Forward, "", animRate, pStart, pEnd)
      self._playFlourishOnEquip = false
    else
      local animRate = 1
      local pStart = 1
      local pEnd = 1
      UI.Anim(self.children.goEquippedBacking, consts.AS_Forward, "", animRate, pStart, pEnd)
    end
  end
end
function StatsCard:GetIsTalisman(itemName, itemType)
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP and resourceUtil.IsTrinket(itemName) then
    return true
  elseif itemType == resourceConsts.RESOURCE_FLAG_RECIPE and resourceUtil.IsTrinket(itemName) then
    return true
  end
  return false
end
function StatsCard:GetIsWeaponComponent(itemName, itemType)
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP and pickupUtil.IsWeaponComponent(itemName) then
    return true
  elseif itemType == resourceConsts.RESOURCE_FLAG_RECIPE and pickupUtil.IsWeaponComponent(itemName) then
    return true
  end
  return false
end
function StatsCard:GetItemName(itemName, itemType)
  self._originalItemName = itemName
  self._recipeName = nil
  self._recipeOutputName = nil
  self._recipeOutputType = nil
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP or itemType == resourceConsts.RESOURCE_FLAG_RUNE then
    return itemName
  elseif itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    local primaryRecipeItem = recipeUtil.RecipeListItem_GetPrimaryItem(itemName)
    assert(primaryRecipeItem ~= nil, "Recipe passed into stats card did not contain an item as output")
    local recipeOutputName = recipeUtil.GetRecipeItemName(primaryRecipeItem)
    self._recipeName = recipeUtil.RecipeListItem_GetRecipeName(itemName)
    self._recipeOutputName = recipeOutputName
    if resourceUtil.IsTrinket(recipeOutputName) then
      self._recipeOutputType = resourceConsts.RESOURCE_FLAG_ARMOR_TRINKET
    elseif resourceUtil.IsResourceAPickup(recipeOutputName) then
      self._recipeOutputType = resourceConsts.RESOURCE_FLAG_PICKUP
    end
    return recipeOutputName
  elseif itemType == resourceConsts.RESOURCE_FLAG_RESOURCE then
    return itemName
  else
    assert(false, "Failed to get resource name for stats card")
  end
end
function StatsCard:GetHeader(itemName, itemType, stage)
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP then
    return (pickupUtil.GetDisplayName(itemName))
  elseif itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    return recipeUtil.RecipeListItem_GetDisplayName(itemName)
  elseif itemType == resourceConsts.RESOURCE_FLAG_RUNE then
    return runeUtil.GetRuneInfoDisplayName(itemName)
  elseif itemType == resourceConsts.RESOURCE_FLAG_RESOURCE then
    return resourceUtil.GetDisplayName(itemName)
  end
end
function StatsCard:SetHeader(displayName)
  SetText(self.textHandles.header, displayName)
end
function StatsCard:SetBodyBackingScaling()
  local descriptionFrame = self._frameGroup:GetNode(FRAMEINDEX.description)
  local attributeFrame = self._frameGroup:GetNode(FRAMEINDEX.attributes)
  local perkFrame = self._frameGroup:GetNode(FRAMEINDEX.perks)
  local descriptionHeight = descriptionFrame:IsActive() and descriptionFrame:GetHeight() or 0
  local attributeHeight = attributeFrame:IsActive() and attributeFrame:GetHeight() or 0
  local perkHeight = perkFrame:IsActive() and perkFrame:GetHeight() or 0
  local bodyBackingHeight = descriptionHeight + attributeHeight + perkHeight
  self.bodyBackingFrame:SetScaleY(bodyBackingHeight)
  local UVScale_max = 1
  local UVScale_min = 0.1
  local targetTimelinePos = (descriptionHeight + attributeHeight) * (UVScale_min / UVScale_max)
  local animRate = 0
  UI.Anim(self.children.goDescriptionBackingAnimateMe, consts.AS_Forward, "", animRate, targetTimelinePos)
  local bodyBackingTimelinePos = bodyBackingHeight * (UVScale_min / UVScale_max)
  UI.Anim(self.children.goBodyBacking, consts.AS_Forward, "statDetail_Card_WorldUI_BodyBackingUVscale", animRate, bodyBackingTimelinePos)
end
function StatsCard:GetWeaponAttributeName(itemName)
  if itemName == "Axe" then
    return "Strength"
  elseif itemName == "Blades" then
    return "Runic"
  end
  return nil
end
function StatsCard:AnimateTransitionOnNewItem(newItemName, itemType, doDefaultTransitionAnim)
  if util.GetValueWithDefault(doDefaultTransitionAnim, true) then
    local isDifferentItemThanLast = false
    if itemType == resourceConsts.RESOURCE_FLAG_RUNE then
      isDifferentItemThanLast = self._currentItemName == nil or newItemName.Id ~= self._currentItemName.Id
    else
      isDifferentItemThanLast = self._currentItemName == nil or newItemName ~= self._currentItemName
    end
    if isDifferentItemThanLast then
      local cardOffset = engine.Vector.New(0, 0, 0)
      animationUtil.DoDefaultTransitionAnim(self.children.goRoot, cardOffset)
    end
  end
end
function StatsCard:GetEquippedItemNameAndStage(itemName, itemType, character)
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP or itemType == resourceConsts.RESOURCE_FLAG_RECIPE or self._recipeOutputType ~= nil and self._recipeOutputType == resourceConsts.RESOURCE_FLAG_PICKUP then
    local pickupSlotForItem = pickupUtil.GetSlotName(itemName)
    local equippedItemName = characterUtil.GetPickupInSlot(character, pickupSlotForItem)
    local equippedItemStage = pickupUtil.GetProfileStage(equippedItemName)
    if equippedItemStage == -1 then
      equippedItemStage = nil
    end
    return equippedItemName, equippedItemStage
  elseif itemType == resourceConsts.RESOURCE_FLAG_RUNE then
    return nil
  end
end
function StatsCard:GetPowerLevelPreview(itemName, stage, itemType, character, isReinforcementRecipe)
  if isReinforcementRecipe then
    local maxStage = pickupUtil.GetMaxStage(itemName)
    if stage < maxStage then
      stage = stage + 1
    end
    return self:GetPowerLevelValue(itemName, stage)
  else
    local equippedItem, equippedItemStage = self:GetEquippedItemNameAndStage(itemName, itemType, character)
    return self:GetPowerLevelValue(equippedItem, equippedItemStage)
  end
end
function StatsCard:GetPowerLevel(itemName, stage, itemType, character, reinforcementRecipePickup)
  if itemType ~= resourceConsts.RESOURCE_FLAG_PICKUP and itemType ~= resourceConsts.RESOURCE_FLAG_RECIPE then
    if itemType == resourceConsts.RESOURCE_FLAG_RUNE then
      return runeUtil.GetPowerLevelFromID(itemName.Id)
    else
      return nil
    end
  else
    if character == pickupConsts.TAG_PICKUP_SON and pickupUtil.IsPrimaryWeapon(reinforcementRecipePickup or self._currentItemName) then
      return nil
    end
    local isReinforcementRecipe = reinforcementRecipePickup ~= nil
    local pickup = isReinforcementRecipe and reinforcementRecipePickup or itemName
    local defaultValue = -1
    local powerLevel = pickup ~= nil and self:GetPowerLevelValue(pickup, stage) or 0
    local powerLevelPreview = isReinforcementRecipe and self:GetPowerLevelPreview(reinforcementRecipePickup, stage, itemType, character, isReinforcementRecipe) or nil
    return powerLevel, powerLevelPreview
  end
end
function StatsCard:GetPerks(itemName, itemType, stage, isReinforcementRecipe)
  local perksStringTable = {}
  if itemType == resourceConsts.RESOURCE_FLAG_ARMOR_TRINKET then
    return perksStringTable
  end
  if itemType == resourceConsts.RESOURCE_FLAG_RUNE then
    perksStringTable = runeUtil.GetRuneInfoPerksStringTable(itemName)
  end
  local displayStage = stage
  if itemType == resourceConsts.RESOURCE_FLAG_RECIPE and resourceUtil.IsResourceAPickup(itemName) and not resourceUtil.IsTrinket(itemName) and not pickupUtil.IsWeapon(itemName) then
    perksStringTable = self:GetArmorPerks(itemName, displayStage)
  end
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP and pickupUtil.IsArmor(self._currentItemName) and not resourceUtil.IsTrinket(itemName) then
    perksStringTable = self:GetArmorPerks(itemName, displayStage)
  end
  return perksStringTable
end
function StatsCard:ClearPerks()
  local thTalismanPerkHeader = self.textHandles.thTalismanPerkHeader
  local thTalismanPerkDescription = self.textHandles.thTalismanPerkDesc
  local thOldPerks = self.textHandles.perks
  UI.SetText(thTalismanPerkHeader, "")
  UI.SetText(thTalismanPerkDescription, "")
  UI.SetText(thOldPerks, "")
end
function StatsCard:SetPerks(displayData, textHandle, frame)
  local perksString = ""
  local perksStringTable = displayData.Perks
  if #displayData.Perks > 0 then
    assert(#perksStringTable % 2 == 0, "Perks string table in StatsCard:SetPerks has an odd number to it, it should have header + description per perk")
    local index = 1
    while index < #perksStringTable do
      perksString = perksString .. "<StatsCardGemSlotPerkHeader>[BulletPoint] " .. perksStringTable[index] .. [[
</StatsCardGemSlotPerkHeader>
<StatsCardGemSlotPerkDescription>]] .. perksStringTable[index + 1] .. [[
</StatsCardGemSlotPerkDescription>

]]
      index = index + 2
    end
  end
  displayData.perksExist = not util.IsStringNilOrEmpty(perksString)
  if displayData.perksExist then
    SetText(textHandle, perksString)
    self.children.goPerksGroup:Show()
    frame:SetActive(true)
    self.children.goGlows[1]:Show()
    local padding = 1
    local numPerks = #perksStringTable / 2
    local heightOf1Line = 1.2
    local heightOfSinglePerk = heightOf1Line * 3.4
    local textHeight = heightOfSinglePerk * numPerks
    local finalHeight = padding * 2 + textHeight
    frame:SetHeight(finalHeight)
    self:CenterGOInFrame(self.children.goPerksStandardText, finalHeight)
  else
    self.children.goGlows[1]:Hide()
    self.children.goPerksGroup:Hide()
    frame:SetActive(false)
  end
end
function StatsCard:CenterGOInFrame(gameObj, frameHeight)
  local vCurrentTextPos = UI.GetGOLocalPosition(gameObj)
  local vNewTextPos = engine.Vector.New(vCurrentTextPos.x, -frameHeight * 0.5, vCurrentTextPos.z)
  game.UI.SetGOLocalPosition(gameObj, vNewTextPos)
end
function StatsCard:SetStandardPerks(displayData)
  local perksStringTable = displayData.Perks
  local frame = self._frameGroup:GetNode(FRAMEINDEX.perks)
  local thPerkHeader = self.textHandles.thTalismanPerkHeader
  local thPerkDescription = self.textHandles.thTalismanPerkDesc
  displayData.perksExist = 0 < #perksStringTable
  assert(#perksStringTable % 2 == 0, "Perks string table in StatsCard:SetPerks has an odd number to it, it should have header + description per perk")
  if displayData.perksExist then
    UI.SetText(thPerkHeader, perksStringTable[1])
    UI.SetText(thPerkDescription, perksStringTable[2])
    self.children.goPerksGroup:Show()
    frame:SetActive(true)
    self.children.goGlows[1]:Show()
    local finalHeight = 7
    frame:SetHeight(finalHeight)
  else
    self.children.goGlows[1]:Hide()
    self.children.goPerksGroup:Hide()
    frame:SetActive(false)
  end
end
function StatsCard:SetTalismanPerks(displayData)
  local frame = self._frameGroup:GetNode(FRAMEINDEX.perks)
  local thPerkHeader = self.textHandles.thTalismanPerkHeader
  local thPerkDescription = self.textHandles.thTalismanPerkDesc
  if displayData.TalismanTag ~= nil then
    UI.SetText(thPerkHeader, util.GetLAMSMsg(displayData.TalismanTag))
  end
  UI.SetText(thPerkDescription, displayData.Description)
  self.children.goPerksGroup:Show()
  frame:SetActive(true)
  self.children.goGlows[1]:Show()
  local finalHeight = 7
  frame:SetHeight(finalHeight)
  displayData.perksExist = true
end
function StatsCard:SetWeaponComponentPerks(displayData)
  local frame = self._frameGroup:GetNode(FRAMEINDEX.perks)
  local thPerkHeader = self.textHandles.thTalismanPerkHeader
  local thPerkDescription = self.textHandles.thTalismanPerkDesc
  if displayData.WeaponComponentDescription ~= nil then
    UI.SetText(thPerkHeader, util.GetLAMSMsg(displayData.WeaponComponentDescription))
  else
    UI.SetText(thPerkHeader, "")
  end
  UI.SetText(thPerkDescription, displayData.Description)
  self.children.goPerksGroup:Show()
  frame:SetActive(true)
  self.children.goGlows[1]:Show()
  local finalHeight = 9
  frame:SetHeight(finalHeight)
  displayData.perksExist = true
end
function StatsCard:GetArmorPerks(itemName, stage)
  local perkStringsTable = {}
  local tableIndex = 1
  local pickupTables = {}
  pickupTables = Pickup.GetGivePlayerPickupAtStage(itemName, stage)
  for _, pickupTable in ipairs(pickupTables) do
    local displayName = pickupUtil.GetDisplayName(pickupTable.PickupId)
    local displayDesc = pickupUtil.GetDescription(pickupTable.PickupId)
    tablex.FastInsert(perkStringsTable, displayName, #perkStringsTable + 1)
    tablex.FastInsert(perkStringsTable, displayDesc, #perkStringsTable + 1)
  end
  return perkStringsTable
end
function StatsCard:GetRarity(itemName, itemType)
  local rarity
  if itemType == resourceConsts.RESOURCE_FLAG_RUNE then
    return runeUtil.GetRarityFromID(runeUtil.GetRuneInfoRuneID(itemName))
  else
    return resourceUtil.GetRarity(itemName)
  end
end
function StatsCard:SetRarity(rarity, textHandle)
  local textColorStyle = self._equippedEnabled and "Color_Black" or "Color_White"
  SetText(textHandle, rarity ~= nil and util.StyleText(util.GetLAMSMsg(lamsConsts.NoColon[rarity]), textColorStyle) or "")
  local color, brightColor, darkColor
  if rarity ~= nil then
    color = colors.RARITY[rarity]
    darkColor = colors.RARITY_DARK[rarity]
    brightColor = colors.RARITY_BRIGHT[rarity]
  else
    color = colors.RARITY[resourceConsts.RESOURCE_FLAG_ECONOMY]
    darkColor = colors.RARITY_DARK[resourceConsts.RESOURCE_FLAG_ECONOMY]
    brightColor = colors.RARITY_BRIGHT[resourceConsts.RESOURCE_FLAG_ECONOMY]
  end
  self:SetCardColor(color, darkColor, brightColor)
  local targetTimelinePos = 0
  local animRate = 0
  if rarity == resourceConsts.RESOURCE_FLAG_PERFECT then
    targetTimelinePos = 1
  end
  UI.Anim(self.children.goBodyBacking, consts.AS_Forward, "", animRate, targetTimelinePos)
  UI.Anim(self.children.goDescriptionBackingAnimateMe, consts.AS_Forward, "statsDetail_Card_WorldUI_ToggleNGP", animRate, targetTimelinePos)
end
function StatsCard:GetDescription(itemName, itemType, recipeOutputType, reinforcementRecipePickup)
  local enableFrame = true
  local descriptionText, descriptionHeight
  local largeDescriptionSize = 7
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP then
    descriptionText = pickupUtil.GetDescription(itemName)
    if resourceUtil.IsTrinket(itemName) then
      descriptionHeight = largeDescriptionSize
    elseif pickupUtil.IsWeaponComponent(itemName) then
      descriptionHeight = largeDescriptionSize
    end
  elseif itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    descriptionText = recipeUtil.RecipeListItem_GetDescription(itemName)
    local itemToCheck = reinforcementRecipePickup ~= nil and reinforcementRecipePickup or self._currentItemName
    if resourceUtil.IsTrinket(itemToCheck) then
      descriptionHeight = largeDescriptionSize
    elseif pickupUtil.IsWeaponComponent(itemToCheck) then
      descriptionHeight = largeDescriptionSize
    end
  elseif itemType == resourceConsts.RESOURCE_FLAG_RESOURCE then
    descriptionText = resourceUtil.GetDescription(itemName)
    descriptionHeight = largeDescriptionSize
  end
  return descriptionText, descriptionHeight
end
function StatsCard:SetDescription(displayData, displayText, textHandle, itemType, height)
  local enableFrame = displayText ~= nil and not displayData.IsTalisman and not displayData.IsWeaponComponent
  if height == nil then
    local descriptionHeightStandard = 4
    height = descriptionHeightStandard
  end
  if enableFrame then
    SetText(textHandle, displayText)
    self.children.goDescriptionGroup:Show()
  else
    self.children.goDescriptionGroup:Hide()
  end
  local descriptionFrame = self._frameGroup:GetNode(FRAMEINDEX.description)
  descriptionFrame:SetHeight(height)
  descriptionFrame:SetActive(enableFrame)
  local centerHeightOffset = -0.15
  self:CenterGOInFrame(self.children.goDescriptionText, height + centerHeightOffset)
end
function StatsCard:GetPowerLevelValue(pickup, stage)
  if stage == nil or stage == -1 or pickupUtil.IsWeaponSpecial(pickup) then
    return 0
  end
  local attributeTable
  attributeTable = Pickup.GetGemAttributes(pickup, stage)
  if 1 < #attributeTable then
    print("UI Warning: Multiple power level attributes found in pickup: " .. tostring(pickup) .. ", there should only be one.")
  end
  if #attributeTable == 1 then
    local temp = attributeTable[1]
    return temp.Value * 10
  end
  return 0
end
function StatsCard:GetAttributes(itemName, itemType, reinforcementRecipePickup, stage, character)
  local getAttributeFunc
  if itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    local recipeName = recipeUtil.RecipeListItem_GetRecipeName(self._originalItemName)
    itemType = self._recipeOutputType
    if recipeUtil.IsRuneCreatorRecipe(recipeName) then
      return {}
    end
  end
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP or itemType == resourceConsts.RESOURCE_FLAG_RECIPE or itemType == resourceConsts.RESOURCE_FLAG_ARMOR_TRINKET then
    getAttributeFunc = Pickup.GetAttribute
  elseif itemType == resourceConsts.RESOURCE_FLAG_RUNE then
    getAttributeFunc = runeUtil.GetRuneInfoImprovementAttributeValue
  elseif itemType == resourceConsts.RESOURCE_FLAG_RESOURCE then
    function getAttributeFunc()
      return 0
    end
  end
  assert(getAttributeFunc ~= nil, "No GetAttribute function was defined in stats card item set.")
  local isReinforcementRecipe = reinforcementRecipePickup ~= nil
  if isReinforcementRecipe then
    itemName = reinforcementRecipePickup
  end
  local stageForSocketDisplay = stage
  local attributes = {}
  if character ~= pickupConsts.TAG_PICKUP_SON then
    local comparisonItemName, stageForComparisonPickup
    if itemType ~= resourceConsts.RESOURCE_FLAG_RESOURCE then
      if isReinforcementRecipe then
        comparisonItemName = itemName
        stageForSocketDisplay = stage
        if stage < pickupUtil.GetMaxStage(itemName) then
          stageForComparisonPickup = stage + 1
        end
      else
        comparisonItemName, stageForComparisonPickup = self:GetEquippedItemNameAndStage(itemName, itemType, character)
      end
    end
    for index, attributeName in pairs(pickupConsts.Attributes) do
      attributes[index] = {}
      attributes[index].name = attributeName
      local value_original = getAttributeFunc(itemName, attributeName, stageForSocketDisplay)
      value_original = value_original ~= nil and value_original or 0
      attributes[index].value = math.floor(value_original)
      if comparisonItemName ~= nil then
        local value = getAttributeFunc(comparisonItemName, attributeName, stageForComparisonPickup)
        value = value ~= nil and value or 0
        attributes[index].comparisonValue = math.floor(value)
        local isUpgradeCard = reinforcementRecipePickup ~= nil
        if isUpgradeCard then
          attributes[index].upgradeValue = math.floor(value)
        end
      end
    end
  end
  return attributes
end
function StatsCard:ClearAttributeDisplayFlags()
  if self._currentDisplayData and self._currentDisplayData.Attributes then
    local attrTable = self._currentDisplayData.Attributes
    for _, attr in ipairs(attrTable) do
      attr.displayOverride = attr.value ~= 0
    end
  end
end
local flipComparisonArrows = false
function StatsCard:SetUpgradePips(displayData)
  local filledPips = displayData.Stage ~= nil and displayData.Stage + 1 or 1
  local displayedPips = displayData.MaxStage ~= nil and displayData.MaxStage + 1 or 1
  if displayData.MaxStage ~= nil and displayData.MaxStage > 0 and not displayData.IsWeaponReinforcement then
    for i = 1, MAX_UPGRADE_PIPS do
      local goPip = self.children.upgradePips[i]
      if i <= displayedPips then
        local jid_fill = goPip:GetJointIndex("Fill")
        if i <= filledPips then
          goPip:ShowJoint(jid_fill)
        else
          goPip:HideJoint(jid_fill)
        end
        goPip:Show()
      else
        goPip:Hide()
      end
    end
    self.children.goUpgradePipsGroup:Show()
  else
    self.children.goUpgradePipsGroup:Hide()
  end
end
function StatsCard:SetAttributes(displayData)
  local attributeTable = displayData.Attributes
  local isRecipe = displayData.ItemType == resourceConsts.RESOURCE_FLAG_RECIPE
  local itemType = isRecipe and displayData.RecipeOutputType or displayData.ItemType
  local isTalisman = displayData.IsTalisman
  local isUpgradeCard = displayData.IsUpgradeCard
  local mainStatValue = displayData.MainStat
  local mainStatPreview = displayData.MainStat_Preview
  local itemIsWeapon = displayData.ItemIsWeapon
  local weaponAttributeName = displayData.WeaponAttrName
  local stage = displayData.Stage
  local maxStage = displayData.MaxStage
  if not self._isComparisonCard and self._refreshingStats == false and self._comparisonCard ~= nil then
    self:ClearAttributeDisplayFlags()
    self._comparisonCard:ClearAttributeDisplayFlags()
  end
  local hasActivePartnerCard = self._comparisonCard and self._comparisonCard:IsActive() or self._nonComparisonCard ~= nil and self._nonComparisonCard:IsActive()
  if hasActivePartnerCard then
    local partnerCard = self._comparisonCard or self._nonComparisonCard
    local partnerDisplayData = partnerCard:GetDisplayData()
    local partnerAttrTable = partnerDisplayData.Attributes
    for i, partnerAttr in ipairs(partnerAttrTable) do
      local myAttr = attributeTable[i]
      if not self._isComparisonCard then
        myAttr.displayOverride = partnerAttr.value ~= 0
        myAttr.comparisonValue = partnerAttr.value
      else
        myAttr.displayOverride = partnerAttr.value ~= 0
      end
    end
  end
  assert(attributeTable ~= nil or #attributeTable == 0, "Attribute table passed in to StatsCard:SetAttributes was nil")
  local frame = self._frameGroup:GetNode(FRAMEINDEX.attributes)
  local children = self.children
  self:ClearAttributes()
  children.goIconRuneHousing:Hide()
  if itemType == resourceConsts.RESOURCE_FLAG_PICKUP then
    children.goIconArmorHousing:Show()
    children.goIconTalismanHousing:Hide()
    if isUpgradeCard then
      children.goReinforcePreviewGroup:Show()
    else
      children.goReinforcePreviewGroup:Hide()
    end
  elseif itemType == resourceConsts.RESOURCE_FLAG_RECIPE then
    if isTalisman then
      children.goIconArmorHousing:Hide()
      children.goIconTalismanHousing:Show()
    else
      children.goIconArmorHousing:Show()
      children.goIconTalismanHousing:Hide()
    end
  elseif itemType == resourceConsts.RESOURCE_FLAG_ARMOR_TRINKET then
    children.goIconArmorHousing:Show()
    children.goIconTalismanHousing:Hide()
  elseif itemType == resourceConsts.RESOURCE_FLAG_RUNE then
    children.goIconArmorHousing:Show()
    children.goIconTalismanHousing:Hide()
  end
  local MAX_ATTRIBUTE_COUNT = 6
  local targetTimelinePos
  if mainStatValue ~= nil then
    local displayValue = tostring(mainStatValue)
    self:SetMainStatValue(displayValue)
    if mainStatPreview == nil or mainStatValue == mainStatPreview then
      SetText(self.textHandles.mainAttributePreview, "")
      children.goReinforcePreviewArrow:Hide()
    else
      self:SetMainStatValue_Preview(mainStatPreview)
      children.goReinforcePreviewArrow:Show()
    end
    targetTimelinePos = 0
    self.children.goIconGroup:Show()
  elseif displayData.icon ~= nil then
    targetTimelinePos = 0
  else
    targetTimelinePos = 1
    self.children.goIconGroup:Hide()
  end
  itemIsWeapon = false
  if itemIsWeapon then
    local weaponAttrDisplayText = ""
    if weaponAttributeName ~= nil then
      local weaponAttrValue = 0
      local iAttribute = 1
      for i, attr in ipairs(attributeTable) do
        if attr.name == weaponAttributeName then
          weaponAttrValue = attr.value
          iAttribute = i
          break
        end
      end
      weaponAttrDisplayText = attributeUtil.GetShortDisplayName(attributeTable[iAttribute].name) .. " " .. tostring(weaponAttrValue)
    end
    SetText(self.textHandles.weaponBigAttr, weaponAttrDisplayText)
    for index = 1, #pickupConsts.Attributes do
      self.children.goStats[index]:Hide()
    end
    self:SetAttributesFrameScaling(frame, 0)
    frame:SetActive(true)
    self.children.goAttributesGroup:Show()
  else
    SetText(self.textHandles.weaponBigAttr, "")
    local animRate = 0
    for i = 1, MAX_ATTRIBUTE_COUNT do
      UI.Anim(self.children.goStats[i].Child, consts.AS_ForwardCycle, "", animRate, targetTimelinePos)
    end
    local iCurrentGO = 1
    for _, attr in ipairs(attributeTable) do
      if attr.value ~= 0 or isUpgradeCard and attr.upgradeValue ~= 0 or attr.displayOverride then
        local displayName = attributeUtil.GetDisplayName(attr.name)
        local leftValue
        local rightValue = attr.value
        local valueDiff
        if attr.comparisonValue ~= nil then
          if attr.upgradeValue then
            if attr.upgradeValue == 0 and attr.value ~= 0 then
              valueDiff = attr.value - attr.comparisonValue
            end
            valueDiff = attr.upgradeValue - attr.comparisonValue
          else
            valueDiff = attr.value - attr.comparisonValue
          end
        end
        local upgradeValueDiff
        if isUpgradeCard then
          leftValue = attr.value
          rightValue = attr.upgradeValue
          if attr.comparisonValue ~= nil then
            valueDiff = attr.upgradeValue - attr.comparisonValue
          end
          upgradeValueDiff = attr.upgradeValue - attr.value
          if upgradeValueDiff == nil or upgradeValueDiff == 0 then
            rightValue = nil
          end
          if stage == maxStage then
            rightValue = leftValue
            leftValue = nil
          end
        end
        local forceUpgradeArrowsOff = isUpgradeCard and stage == maxStage or not self._isComparisonCard and self._comparisonCard and not self._comparisonCard:IsActive() or not self._isComparisonCard and flipComparisonArrows or self._comparisonCard and not self._comparisonCard:IsActive() and isUpgradeCard
        local enablePreviewVisibility = isUpgradeCard and upgradeValueDiff ~= 0 and stage ~= maxStage
        self:SetMinorStatValue(displayName, leftValue, rightValue, valueDiff, iCurrentGO, enablePreviewVisibility, forceUpgradeArrowsOff)
        iCurrentGO = iCurrentGO + 1
      end
    end
    displayData.numOfNonzeroAttributes = iCurrentGO - 1
    for index = displayData.numOfNonzeroAttributes + 1, #pickupConsts.Attributes do
      self.children.goStats[index]:Hide()
    end
    self:SetAttributesFrameScaling(frame, displayData.numOfNonzeroAttributes)
    if 0 < displayData.numOfNonzeroAttributes then
      frame:SetActive(true)
      self.children.goAttributesGroup:Show()
    else
      frame:SetActive(false)
      self.children.goAttributesGroup:Hide()
    end
  end
end
function StatsCard:SetAttributesFrameScaling(frame, numberOfStats)
  local backingFrame = self.attributeBackingFrame
  local heightOfSingleRow = 1
  local heightOfBuffer = 0.5
  local heightGapBetweenRows = 0.25
  local maxHeight = backingFrame:GetInitialHeight()
  local statsPerRow = 1
  local numberOfRows = math.ceil(numberOfStats / statsPerRow)
  local newHeight = heightOfSingleRow * numberOfRows + numberOfRows * heightGapBetweenRows + heightOfBuffer * 2
  local minHeight = heightOfSingleRow * 4 + 5 * heightGapBetweenRows + heightOfBuffer * 2
  frame:SetHeight(math.max(minHeight, newHeight))
end
local powerLevelNumberOffsets = {
  [4] = {-0.2, 0},
  [10] = {-0.5, -0.3}
}
local plusOffsets = {
  [4] = {0.1, 0},
  [10] = {0.2, 0}
}
local debugOverride_integer
function StatsCard:SetMainStatValue(value)
  local integer, fraction = math.modf(value)
  local mainStatStyle = "ArmorCardLevel"
  local powerLevelNumberOffsetIndex = integer
  if debugOverride_integer ~= nil then
    integer = debugOverride_integer
    fraction = 0.5
  end
  if 10 <= integer then
    powerLevelNumberOffsetIndex = 10
    mainStatStyle = "ArmorCardLevel_DoubleDigits"
  end
  local printString = util.StyleText(tostring(integer), mainStatStyle)
  local xOffset = powerLevelNumberOffsets[powerLevelNumberOffsetIndex] and powerLevelNumberOffsets[powerLevelNumberOffsetIndex][1] or 0
  local yOffset = powerLevelNumberOffsets[powerLevelNumberOffsetIndex] and powerLevelNumberOffsets[powerLevelNumberOffsetIndex][2] or 0
  local vTargetPos = engine.Vector.New(xOffset, yOffset, 0)
  local useWorldSpace = false
  UI.SetGOTransformInterpolated(self.children.goLevelNum, vTargetPos, 0, useWorldSpace)
  if 0 < fraction then
    xOffset = plusOffsets[powerLevelNumberOffsetIndex] ~= nil and plusOffsets[powerLevelNumberOffsetIndex][1] or 0
    yOffset = plusOffsets[powerLevelNumberOffsetIndex] ~= nil and plusOffsets[powerLevelNumberOffsetIndex][2] or 0
    vTargetPos = engine.Vector.New(xOffset, yOffset, 0)
    UI.SetGOTransformInterpolated(self.children.goLevel_BigPlus, vTargetPos, 0, useWorldSpace)
    self.children.goLevel_BigPlus:Show()
  else
    self.children.goLevel_BigPlus:Hide()
  end
  SetText(self.textHandles.mainAttribute, printString)
end
function StatsCard:SetMainStatValue_Preview(value)
  local integer, fraction = math.modf(value)
  if debugOverride_integer ~= nil then
    integer = debugOverride_integer
    fraction = 0.5
  end
  local printString = tostring(integer)
  if 0 < fraction then
    printString = printString .. "+"
  end
  SetText(self.textHandles.mainAttributePreview, printString)
end
function StatsCard:SetMinorStatValue(displayName, valueLeft, valueRight, valueDiff, index, enablePreviewVisibility, forceUpgradeArrowsOff)
  local displayValueLeft
  if valueLeft == nil then
    displayValueLeft = ""
  else
    local sign = ""
    displayValueLeft = sign .. tostring(math.floor(valueLeft))
  end
  local displayValueRight
  if valueRight == nil then
    displayValueRight = ""
  elseif valueRight == displayValueLeft then
    displayValueRight = ""
  else
    local sign = ""
    displayValueRight = sign .. tostring(math.floor(valueRight))
  end
  local attributeTextHandleGroup = self.textHandles.attributes[index]
  local goAttributeRefnode = self.children.goStats[index]
  local goUpgradeArrow = self.children.goStats_upgradeArrow[index]
  local goUpgradeGroup = self.children.goUpgradeGroup[index]
  if displayName ~= nil then
    SetText(attributeTextHandleGroup.name, displayName)
    SetText(attributeTextHandleGroup.leftNumber, displayValueLeft)
    SetText(attributeTextHandleGroup.rightNumber, displayValueRight)
    goAttributeRefnode:Show()
  else
    goAttributeRefnode:Hide()
    SetText(attributeTextHandleGroup.name, "")
  end
  if enablePreviewVisibility then
    goUpgradeGroup:Show()
  else
    goUpgradeGroup:Hide()
  end
  if valueDiff and not forceUpgradeArrowsOff then
    if 0 < valueDiff then
      goUpgradeArrow:Show()
      goUpgradeArrow:SetMaterialSwap("UpgradeArrow_Up")
    elseif valueDiff < 0 then
      goUpgradeArrow:Show()
      goUpgradeArrow:SetMaterialSwap("UpgradeArrow_Down")
    else
      goUpgradeArrow:Hide()
    end
  else
    goUpgradeArrow:Hide()
  end
end
function StatsCard:SetCardColor(color, color_dark, color_bright)
  if color_dark == nil then
    color_dark = color
  end
  if color_bright == nil then
    color_bright = color
  end
  local childObjTable = self.children
  local material = "glowingLight_anim"
  local layer = "Layer0"
  local attribute = "cst_EmissiveTint"
  util.SetGameObjectColor(childObjTable.goTopBorder, color, "vendor_topBorder", "Layer0", attribute)
  util.SetGameObjectColor(childObjTable.goBodyBacking, color_dark, "itemCard_HeaderBacking", "Layer0", attribute)
  util.SetGameObjectColor(childObjTable.goBodyBacking, color_bright, "itemCard_HeaderBacking", "Layer1", attribute)
end
function StatsCard:GetUpgradeSlots(itemName, itemType, stage, isReinforcementRecipe)
  local upgradeSlotsTable = {}
  if (self._hideUpgradeSlots == nil or self._hideUpgradeSlots == false) and (itemType == resourceConsts.RESOURCE_FLAG_PICKUP or itemType == resourceConsts.RESOURCE_FLAG_RECIPE) and (itemType ~= resourceConsts.RESOURCE_FLAG_RECIPE or itemType == resourceConsts.RESOURCE_FLAG_RECIPE and resourceUtil.IsResourceAPickup(itemName)) and pickupUtil.IsArmor(itemName) then
    upgradeSlotsTable.Type = "Rune"
    local stageForSocketDisplay = isReinforcementRecipe and not self.forceDisplayCurrentStage and stage or stage
    local armorSocketInfo = self:GetArmorUpgradeSocketInfo(itemName, itemType, stageForSocketDisplay, isReinforcementRecipe)
    upgradeSlotsTable.SlotCount = armorSocketInfo.SlotCount
    upgradeSlotsTable.SlotCountPreview = armorSocketInfo.SlotCountPreview
    upgradeSlotsTable.UpgradeCount = armorSocketInfo.UpgradeCount
    upgradeSlotsTable.Slots = armorSocketInfo.Slots
  end
  return upgradeSlotsTable
end
function StatsCard:SetUpgradeSlots(upgradeSlots, textHandles, socketHeaderFrame, upgradeSlotFrameTable, isRecipe, isReinforcementRecipe)
  local upgradeSlotsTableIsEmpty = true
  for _, _ in pairs(upgradeSlots) do
    upgradeSlotsTableIsEmpty = false
  end
  if self._hideUpgradeSlots or upgradeSlots == nil or upgradeSlotsTableIsEmpty then
    self:ClearSocketSlots()
    return
  end
  for slotIndex = 1, pickupConsts.MAX_SOCKET_COUNT do
    local slotExists = slotIndex <= upgradeSlots.SlotCount
    local upgradeExists = upgradeSlots.Slots[slotIndex] ~= nil
    local slotData = slotExists and upgradeSlots.Slots[slotIndex] or nil
    local slotHeaderText = not (not upgradeExists or isRecipe) and slotData.Name or util.GetLAMSMsg(lamsConsts.EmptyRuneSlot)
    local iconMaterial = not (not upgradeExists or isRecipe) and slotData.IconMaterial ~= nil and slotData.IconMaterial or ""
    local iconColor = not (not upgradeExists or isRecipe) and slotData.IconColor ~= nil and slotData.IconColor or {
      0,
      0,
      0
    }
    local hasPerks = false
    local attributeTable = not (not upgradeSlots.Slots[slotIndex] or isRecipe) and upgradeSlots.Slots[slotIndex].Attributes or nil
    if 1 < slotIndex then
      if slotExists then
        self.children.goSeparatorRunes[slotIndex - 1]:Show()
      else
        self.children.goSeparatorRunes[slotIndex - 1]:Hide()
      end
    end
    self:SetUpgradeSlot(slotIndex, slotExists, upgradeExists, slotHeaderText, iconMaterial, iconColor, hasPerks, attributeTable, upgradeSlotFrameTable[slotIndex], isRecipe, isReinforcementRecipe, upgradeSlots.SlotCount, upgradeSlots.SlotCountPreview)
  end
  local anySocketsExist = upgradeSlots.SlotCount > 0 or 0 < upgradeSlots.SlotCountPreview
  if anySocketsExist then
    local fadeAlpha = 1
    UI.AlphaFade(socketHeaderFrame:GetGameObject(), fadeAlpha, upgradeSlotFadeTransitionTime)
    socketHeaderFrame:GetGameObject():Show()
    socketHeaderFrame:SetActive(true)
  else
    local fadeAlpha = 0
    UI.AlphaFade(socketHeaderFrame:GetGameObject(), fadeAlpha, upgradeSlotFadeTransitionTime)
    socketHeaderFrame:GetGameObject():Hide()
    socketHeaderFrame:SetActive(false)
  end
end
function StatsCard:GetArmorUpgradeSocketInfo(itemName, itemType, stage, isReinforcementRecipe)
  local returnTable = {}
  returnTable.SlotCount = pickupUtil.GetRuneSocketCount(itemName, stage)
  returnTable.SlotCountPreview = isReinforcementRecipe and pickupUtil.GetRuneSocketCountPreview(itemName, stage) or 0
  returnTable.UpgradeCount = 0
  returnTable.Slots = {}
  for slotIndex = 1, pickupConsts.MAX_SOCKET_COUNT do
    local socketText = util.GetLAMSMsg(lamsConsts.EmptyRuneSlot)
    local iconMaterial = ""
    local iconColor = colors.BLACK
    local slotExists = slotIndex <= returnTable.SlotCount
    local upgradeExists = false
    local hasPerks = false
    local nextAttributeIndex = 1
    if slotIndex <= returnTable.SlotCount then
      local actualSocketIndex = slotIndex - 1
      local runeInfo = runeUtil.GetRuneInfoInSocketFromPickup(itemName, actualSocketIndex)
      if runeInfo ~= nil then
        returnTable.UpgradeCount = returnTable.UpgradeCount + 1
        local runeID = runeUtil.GetRuneInfoRuneID(runeInfo)
        local runeAttributes = runeUtil.GetRuneAttributesFromInfo(runeInfo)
        returnTable.Slots[slotIndex] = {}
        returnTable.Slots[slotIndex].Name = runeUtil.GetRuneInfoDisplayName(runeInfo)
        returnTable.Slots[slotIndex].IconColor = runeUtil.GetRuneColorFromID(runeID)
        returnTable.Slots[slotIndex].IconMaterial = runeUtil.GetRuneIconFromID(runeID)
        returnTable.Slots[slotIndex].Attributes = runeAttributes
      end
    end
  end
  return returnTable
end
function StatsCard:SetUpgradeSlot(slotIndex, slotExists, upgradeExists, headerText, iconMaterial, iconColor, hasPerks, attributes, frame, isRecipe, isReinforcementRecipe, slotCount, slotCountPreview)
  assert(frame ~= nil, "No frame was passed into StatsCard::SetUpgradeSlot.")
  if isRecipe then
    upgradeExists = false
  end
  local upgradeSlot = self.upgradeSlots[slotIndex]
  local gameObjectsTable = upgradeSlot.gameObjects
  local goRoot = gameObjectsTable.goRoot
  local textHandles = upgradeSlot.textHandles
  gameObjectsTable.goUpgradeSlotArrow_Right:Hide()
  gameObjectsTable.goUpgradeSlotArrow:Hide()
  if slotIndex == 1 then
    if isRecipe then
      if not self._isComparisonCard and self._comparisonCard and self._comparisonCard:IsActive() then
        local compareCardDisplayData = self._comparisonCard:GetDisplayData()
        if compareCardDisplayData then
          local slotCountComparison = compareCardDisplayData.UpgradeSlots.SlotCount
          if slotCountComparison then
            local thisCardsSlotCount = isReinforcementRecipe and slotCountPreview or slotCount
            if slotCountComparison ~= thisCardsSlotCount then
              gameObjectsTable.goUpgradeSlotArrow:Show()
            end
            if slotCountComparison > thisCardsSlotCount then
              gameObjectsTable.goUpgradeSlotArrow:SetMaterialSwap("UpgradeArrow_Down")
            elseif slotCountComparison < thisCardsSlotCount then
              gameObjectsTable.goUpgradeSlotArrow:SetMaterialSwap("UpgradeArrow_Up")
            end
          end
        end
      end
      local recipeUpgradeSlotHeight = 3.75
      frame:SetHeight(recipeUpgradeSlotHeight)
    end
  else
    frame:SetHeight(frame:GetInitialHeight())
  end
  local goSlotIcon = gameObjectsTable.iconRefnode.goRefnode
  local useWorldSpace = false
  if isRecipe then
    local vTargetPos = engine.Vector.New(0, 0.8, 0)
    UI.SetGOTransformInterpolated(goSlotIcon, vTargetPos, 0, useWorldSpace)
  else
    local vIconOffset = engine.Vector.New(0, -0.5, 0)
    UI.SetGOTransformInterpolated(goSlotIcon, vIconOffset, 0, useWorldSpace)
  end
  SetText(textHandles.thDescription, headerText)
  SetText(textHandles.thSlotUpgrade_Left, "")
  SetText(textHandles.thSlotUpgrade_Right, "")
  if isRecipe and (0 < slotCount or 0 < slotCountPreview) then
    local displayText = util.GetLAMSMsg(lamsConsts.RuneSlots)
    if isReinforcementRecipe and slotCount < slotCountPreview then
      SetText(textHandles.thSlotUpgrade_Left, tostring(slotCount))
      SetText(textHandles.thSlotUpgrade_Right, tostring(slotCountPreview))
      gameObjectsTable.goUpgradeSlotArrow_Right:Show()
      slotExists = true
    else
      local textHandle = isReinforcementRecipe and textHandles.thSlotUpgrade_Left or textHandles.thSlotUpgrade_Right
      SetText(textHandle, tostring(slotCount))
    end
    SetText(textHandles.thDescription, "")
    SetText(textHandles.thDescription_Center, displayText)
    gameObjectsTable.goDescriptionText_Center:Show()
    if 1 < slotIndex then
      slotExists = false
    end
  else
    gameObjectsTable.goDescriptionText_Center:Hide()
  end
  if not slotExists then
    goRoot:Hide()
    frame:SetActive(false)
    return
  end
  local iCurrentSlot = 1
  for iAttribute = 1, consts.MAX_STATS_PER_SLOT do
    local currentStat = upgradeSlot.stats[iCurrentSlot]
    local currentTextHandles = currentStat.textHandles
    local attributeDisplayName
    if attributes ~= nil and attributes[iAttribute].value ~= 0 then
      attributeDisplayName = attributeUtil.GetShortDisplayName(attributes[iAttribute].name)
      SetText(currentTextHandles.name, attributeDisplayName)
      SetText(currentTextHandles.number, attributes[iAttribute].value)
      iCurrentSlot = iCurrentSlot + 1
      currentStat.goRefnode:Show()
    else
      currentStat.goRefnode:Hide()
    end
  end
  while iCurrentSlot <= consts.MAX_STATS_PER_SLOT do
    upgradeSlot.stats[iCurrentSlot].goRefnode:Hide()
    iCurrentSlot = iCurrentSlot + 1
  end
  goRoot:Show()
  buttonUtil.UpdateUpgradeSlot(goSlotIcon, iconColor, hasPerks, nil, iconMaterial)
  if upgradeExists then
    gameObjectsTable.iconRefnode.goRoot:Show()
    local fadeOutTargetAlpha = 1
    local fadeOutTime = 0
    UI.AlphaFade(gameObjectsTable.goDescriptionText, fadeOutTargetAlpha, fadeOutTime)
  else
    local fadeOutTargetAlpha = 0.45
    local fadeOutTime = 0
    UI.AlphaFade(gameObjectsTable.goDescriptionText, fadeOutTargetAlpha, fadeOutTime)
  end
  frame:SetActive(true)
end
function StatsCard:AnimateUpgradeSlot(slotIndex)
  if self.upgradeSlots[slotIndex] ~= nil then
    local goUpgradeSlotIcon = self.upgradeSlots[slotIndex].gameObjects.iconRefnode.goRoot
    if goUpgradeSlotIcon ~= nil then
      local animRate = 1
      local startTime = 0
      local endTime = 1
      UI.Anim(goUpgradeSlotIcon, consts.AS_Forward, "upgradeSlot_GlowFlourish", animRate, startTime, endTime)
    end
  end
end
function StatsCard:DebugForceButtonFooterOn()
  self._debugFooter = true
  self:SetButtonStates("left", "Right", true)
end
function StatsCard:SetButtonStates(leftButtonMessage, rightButtonMessage, debugOn)
  self:SetButtonState_Left(leftButtonMessage)
  self:SetButtonState_Right(rightButtonMessage)
  self:UpdateButtonStates()
end
function StatsCard:SetButtonState_Left(message)
  self._buttonMessage_Left = message
  self:UpdateButtonStates()
end
function StatsCard:SetButtonState_Middle(message)
  self._buttonMessage_Middle = message
  self:UpdateButtonStates()
end
function StatsCard:SetButtonState_Right(message)
  self._buttonMessage_Right = message
  self:UpdateButtonStates()
end
function StatsCard:UpdateButtonStates()
  local textHandles = self.textHandles
  local leftButtonExists = not util.IsStringNilOrEmpty(self._buttonMessage_Left)
  local middleButtonExists = not util.IsStringNilOrEmpty(self._buttonMessage_Middle)
  local rightButtonExists = not util.IsStringNilOrEmpty(self._buttonMessage_Right)
  if leftButtonExists then
    SetText(textHandles.buttonPrompt_Left, self._buttonMessage_Left)
    self.children.goButtonPrompt_Left:Show()
  else
    self.children.goButtonPrompt_Left:Hide()
  end
  if middleButtonExists then
    SetText(textHandles.buttonPrompt_Middle, self._buttonMessage_Middle)
    self.children.goButtonPrompt_Middle:Show()
  else
    self.children.goButtonPrompt_Middle:Hide()
  end
  if rightButtonExists then
    SetText(textHandles.buttonPrompt_Right, self._buttonMessage_Right)
    self.children.goButtonPrompt_Right:Show()
  else
    self.children.goButtonPrompt_Right:Hide()
  end
  if leftButtonExists or middleButtonExists or rightButtonExists then
    self.children.goButtonPromptGrp:Show()
    self.children.goEquipBarBottom:Hide()
    self._frameGroup:GetNode(FRAMEINDEX.buttons):SetActive(true)
  else
    self.children.goButtonPromptGrp:Hide()
    self._frameGroup:GetNode(FRAMEINDEX.buttons):SetActive(false)
  end
  self:SetBodyBackingScaling()
  self._frameGroup:Update()
  self._frameGroup:SetBackframeScale(self.backFrame)
end
function StatsCard:UpdateEquipBarBottom(show)
  if show then
    self.children.goEquipBarBottom:Show()
    self._frameGroup:GetNode(FRAMEINDEX.equipBarBottom):SetActive(true)
  else
    self.children.goEquipBarBottom:Hide()
    self._frameGroup:GetNode(FRAMEINDEX.equipBarBottom):SetActive(false)
  end
  self:SetBodyBackingScaling()
  self._frameGroup:Update()
  self._frameGroup:SetBackframeScale(self.backFrame)
end
function StatsCard:ClearCard()
  local textHandles = self.textHandles
  SetText(textHandles.header, "")
  SetText(textHandles.description, "")
  SetText(textHandles.perks, "")
  SetText(textHandles.thTalismanPerkHeader, "")
  SetText(textHandles.thTalismanPerkDesc, "")
  SetText(textHandles.perks, "")
  self:ClearAttributes()
  self:ClearAttributeDisplayFlags()
  self._frameGroup:ResetActiveStates()
end
function StatsCard:ClearAttributes()
  self._activeMinorAttributeCount = 0
  local textHandles = self.textHandles
  SetText(textHandles.mainAttribute, "")
  local attributeTextHandles = self.textHandles.attributes
  for index, attributeName in ipairs(pickupConsts.Attributes) do
    local textHandleGroup = attributeTextHandles[index]
    if index == #pickupConsts.Attributes then
      SetText(textHandleGroup.name, "")
      SetText(textHandleGroup.leftNumber, "")
      self.children.goStats[index]:Hide()
      break
    end
    SetText(textHandleGroup.name, attributeUtil.GetDisplayName(attributeName))
    SetText(textHandleGroup.leftNumber, "--")
  end
end
function StatsCard:ClearSocketSlots()
  local textHandleGroups = self.textHandles.upgradeSlots
  for socketIndex = 1, pickupConsts.MAX_SOCKET_COUNT do
    self:SetUpgradeSlot(socketIndex, false, false, "", consts.EMPTY_MATERIAL_NAME, colors.BLACK, false, {}, self._frameGroup:GetNode(FRAMEINDEX["runeslot" .. tostring(socketIndex)]))
  end
  local socketHeaderFrame = self._frameGroup:GetNode(FRAMEINDEX.socketHeader)
  socketHeaderFrame:SetActive(false)
  local fadeAlpha = 0
  UI.AlphaFade(socketHeaderFrame:GetGameObject(), fadeAlpha, upgradeSlotFadeTransitionTime)
  socketHeaderFrame:GetGameObject():Hide()
end
function StatsCard:IsActive()
  return self._showing
end
function StatsCard:ShowCard(showAll, doDefaultTransition)
  self._gameObject:Show()
  if showAll then
    for _, obj in pairs(self.children) do
      if type(obj) == "table" then
        self:ShowCard_Recursive(obj)
      else
        obj:Show()
      end
    end
  end
  if doDefaultTransition or not self._showing then
    local cardOffset = engine.Vector.New(0, 0, 0)
    if self._positionOffset then
      cardOffset = self._positionOffset
    end
    animationUtil.DoDefaultTransitionAnim(self.children.goRoot, cardOffset)
  end
  self._showing = true
end
function StatsCard:ShowCard_Recursive(obj)
  if type(obj) == "table" then
    for _, childObj in pairs(obj) do
      self:ShowCard_Recursive(childObj)
    end
  else
    obj:Show()
  end
end
function StatsCard:GoHome()
  local goObjToMove = self._gameObject
  local goObjToMoveTo = self._goHomeLocalPosition
  local nameOfJointToMoveTo
  local animTime = 0.5
  local useWorldSpace = false
  animationUtil.SetTransformInterpolated(goObjToMove, goObjToMoveTo, nameOfJointToMoveTo, animTime, useWorldSpace)
end
function StatsCard:GetCardHeight()
  return self.backFrame:GetHeight()
end
function StatsCard:HideCard()
  self._gameObject:Hide()
  self._showing = false
  if self._comparisonCard then
    self._comparisonCard:ClearAttributeDisplayFlags()
    self._comparisonCard:RefreshDisplayData()
  end
  if self._nonComparisonCard then
    self._nonComparisonCard:ClearAttributeDisplayFlags()
    self._nonComparisonCard:RefreshDisplayData()
  end
end
return {StatsCard = StatsCard}
