local tablex = require("core.tablex")
local consts = require("ui.consts")
local hudConsts = require("ui.hudConsts")
local meterPreview = require("ui.meterPreview")
local pickupConsts = require("ui.pickupConsts")
local pickupUtil = require("ui.pickupUtil")
local resourceConsts = require("ui.resourceConsts")
local resourceUtil = require("ui.resourceUtil")
local runeUtil = require("ui.runeUtil")
local util = require("ui.util")
local UI = game.UI
local Pickup = game.Pickup
local currentlyEquippedAttributes = {}
local GetDisplayName = function(attributeName)
  local displayName = ""
  if attributeName ~= nil then
    displayName = util.GetLAMSMsg(pickupConsts.AttributeDisplayNameIDs[attributeName], attributeName)
  end
  return displayName
end
local GetShortDisplayName = function(attributeName)
  local displayName = ""
  if attributeName ~= nil then
    displayName = util.GetLAMSMsg(pickupConsts.AttributeShortNames[attributeName], attributeName)
  end
  return displayName
end
local GetStatDisplayName = function(attributeName)
  local displayName = ""
  if attributeName ~= nil then
    displayName = util.GetLAMSMsg(pickupConsts.AttributeDisplayNameWithStats[attributeName], attributeName)
  end
  return displayName
end
local GetDescription = function(attributeName)
  local description = ""
  if attributeName ~= nil then
    description = util.GetLAMSMsg(pickupConsts.AttributeDescriptionIDs[attributeName])
  end
  return description
end
local GetMax = function(attributeName)
  local max = pickupConsts.MaxAttributeValues[attributeName]
  assert(max ~= nil, "Max value for attribute was nil")
  return max
end
local GetAttributesTable = function(pickupName, socketIndexToExclude)
  local attributes = {}
  local socketCount = pickupUtil.GetRuneSocketCount(pickupName)
  for index, attr in ipairs(pickupConsts.Attributes) do
    local value = pickupUtil.GetAttribute(pickupName, attr)
    for socketIndex = 0, socketCount - 1 do
      if socketIndex ~= socketIndexToExclude then
        local runeInfo = runeUtil.GetRuneInfoInSocketFromPickup(pickupName, socketIndex)
        local attributeValue = runeUtil.GetRuneInfoImprovementAttributeValue(runeInfo, attr)
        value = value + attributeValue
      end
    end
    local percent = value / GetMax(attr)
    tablex.FastInsert(attributes, percent, index)
  end
  return attributes
end
local GetRuneInfoAttributesTable = function(runeInfo)
  local attributes = {}
  for index, attr in ipairs(pickupConsts.Attributes) do
    local value = runeUtil.GetRuneInfoImprovementAttributeValue(runeInfo, attr)
    if value == nil then
      value = 0
    end
    local percent = value / GetMax(attr)
    tablex.FastInsert(attributes, percent, index)
  end
  return attributes
end
local GetCreaturesAttributes = function(creature)
  local attributes = {}
  assert(creature ~= nil, "Requested attributes for nil creature.")
  for index, attr in ipairs(pickupConsts.Attributes) do
    local value = creature:AttributeGetValue(attr)
    tablex.FastInsert(attributes, value, index)
  end
  return attributes
end
local GetCharactersAttributes = function(character)
  local creature = pickupUtil.GetCreatureFromCharacter(character)
  assert(creature ~= nil, "Requested attributes for nil creature.")
  local attributes = GetCreaturesAttributes(creature)
  attributes[pickupConsts.PowerLevelIndex] = creature:AttributeGetValue("PowerLevel")
  attributes[pickupConsts.MaxHealthIndex] = util.Round(creature:MeterGetMax(hudConsts.METER_HEALTH))
  return attributes
end
local g_attributesInitialized = false
local MarkAttributesInitialized = function()
  g_attributesInitialized = true
end
local MarkAttributesUninitialized = function()
  g_attributesInitialized = false
end
local AttributesInitialized = function()
  return g_attributesInitialized
end
local SetCurrentlyEquippedAttributes = function(character, attributes, uiCreaturesInitialized)
  currentlyEquippedAttributes[character] = attributes
  currentlyEquippedAttributes[character][pickupConsts.PowerLevelIndex] = 0
  local creature = uiCreaturesInitialized and pickupUtil.GetUICreature(character) or pickupUtil.GetCreatureFromCharacter(character)
  if creature ~= nil then
    currentlyEquippedAttributes[character][pickupConsts.PowerLevelIndex] = creature:AttributeGetValue("PowerLevel")
    currentlyEquippedAttributes[character][pickupConsts.MaxHealthIndex] = util.Round(creature:MeterGetMax(hudConsts.METER_HEALTH))
  end
end
local UpdateCurrentlyEquippedAttributes = function(character, delta)
  if currentlyEquippedAttributes[character] ~= nil and delta ~= nil then
    for index, value in ipairs(delta) do
      currentlyEquippedAttributes[character][index] = currentlyEquippedAttributes[character][index] + value
    end
  end
end
local GetCurrentlyEquippedAttributes = function(character)
  if not g_attributesInitialized then
    return GetCharactersAttributes(character)
  else
    return currentlyEquippedAttributes[character]
  end
end
local GetCurrentlyEquippedAttributeOnCharacter = function(character, attribute)
  local attrIndex = -1
  for index, currConstAttr in ipairs(pickupConsts.Attributes) do
    if currConstAttr == attribute then
      attrIndex = index
    end
  end
  assert(attrIndex ~= -1, "Attribute wasn't found in GetCurrentlyEquippedAttributeOnCharacter. Value was: " .. tostring(attribute))
  return GetCurrentlyEquippedAttributes(character)[attrIndex]
end
local SetMeterColor = function(meter, color)
  local goObjToColor = meter:GetInstancedFillObject()
  util.SetGameObjectColor(goObjToColor, color, "statBar_Fill", "Layer0", "cst_EmissiveTint")
end
local SetPreviewMeterColor = function(meter, color)
  local goObjToColor = meter:GetInstancedPreviewFillObject()
  util.SetGameObjectColor(goObjToColor, color, "statBar_Fill", "Layer0", "cst_EmissiveTint")
end
local CalculateAttributeDiff = function(currentAttributes, equippedItemAttributes, selectedItemAttributes)
  local returnTable = {}
  for index, _ in ipairs(currentAttributes) do
    returnTable[index] = currentAttributes[index] + (selectedItemAttributes[index] - equippedItemAttributes[index])
  end
  return returnTable
end
local InternalUpdateAttributeMeters = function(attributeCard, currentCreaturesAttributes, equippedItemMatchesSelected, equippedAttributes, selectedAttributes, character, creature)
  local attributes_current = {}
  local attributes_preview = {}
  for index, attributeValue in ipairs(currentCreaturesAttributes) do
    attributes_current[index] = attributeValue
    attributes_preview[index] = attributeValue
  end
  if not equippedItemMatchesSelected then
    attributes_preview = CalculateAttributeDiff(currentCreaturesAttributes, equippedAttributes, selectedAttributes)
  end
  attributeCard:UpdateMeter_Equipped(attributes_current, character, creature)
  attributeCard:UpdateMeter_Preview(attributes_preview)
end
local UpdateAttributeMeters = function(attributeCard, character, currPickupSlotName, currentlySelectedPickupName)
  assert(character ~= nil, "Nil character was passed into atributeUtil::UpdateAttributeMeters")
  local creature = pickupUtil.GetCreatureFromCharacter(character)
  local equippedPickupName = pickupUtil.GetPickupNameInSlot(creature, currPickupSlotName)
  local currentCreaturesAttributes = GetCharactersAttributes(character)
  InternalUpdateAttributeMeters(attributeCard, currentCreaturesAttributes, equippedPickupName == currentlySelectedPickupName, pickupUtil.GetPickupAttributes(equippedPickupName), pickupUtil.GetPickupAttributes(currentlySelectedPickupName), character, creature)
end
local UpdateRuneInfoAttributeMeters = function(attributeCard, creature, character, equippedPickupName, socketIndex, currRuneInfo)
  assert(creature ~= nil, "Nil creature was passed into atributeUtil::UpdateAttributeMeters")
  local currentCreaturesAttributes = GetCreaturesAttributes(creature)
  local equippedRuneInfo = runeUtil.GetRuneInfoInSocketFromPickup(equippedPickupName, socketIndex)
  local equippedMatchesSelected = false
  if equippedRuneInfo ~= nil and currRuneInfo ~= nil then
    equippedMatchesSelected = equippedRuneInfo.Id == currRuneInfo.Id
  end
  local attributesFromEquippedRune = {}
  local attributesFromSelectedRune = {}
  local nameValuePairAttrTable = runeUtil.GetRuneAttributesFromInfo(equippedRuneInfo)
  for i, attribute in ipairs(nameValuePairAttrTable) do
    attributesFromEquippedRune[i] = attribute.value
  end
  nameValuePairAttrTable = runeUtil.GetRuneAttributesFromInfo(currRuneInfo)
  for i, attribute in ipairs(nameValuePairAttrTable) do
    attributesFromSelectedRune[i] = attribute.value
  end
  InternalUpdateAttributeMeters(attributeCard, currentCreaturesAttributes, equippedMatchesSelected, attributesFromEquippedRune, attributesFromSelectedRune)
end
local UpdateAttributeGroup = function(attributeCard, character, pickupSlotName, pickupName, showAttributes, refNodeRootObj)
  if pickupUtil.IsValidName(pickupName) == true then
    UpdateAttributeMeters(attributeCard, character, pickupSlotName, pickupName)
  end
end
local GetUICreatureAttributes = function(character)
  local uiCreature = pickupUtil.GetUICreature(character)
  return GetCreaturesAttributes(uiCreature)
end
local UpdateAttributePreview = function(currState, character, postTimerCallback)
  assert(currState ~= nil, "No fsm passed into UpdateAttributePreview!")
  assert(postTimerCallback ~= nil, "No callback passed into UpdateAttributePreview!")
  if currState:HaveTimer("UpdateAttributesDelayTimer") then
    currState:StopTimer("UpdateAttributesDelayTimer")
  end
  local needSonCreature = character == pickupConsts.TAG_PICKUP_SON
  local haveSonCreature = util.SonUI_ShouldShow() and not util.SonUI_ShouldDisable()
  local shouldUpdateAttributes = not needSonCreature or needSonCreature and haveSonCreature
  if shouldUpdateAttributes then
    currState:StartTimer("UpdateAttributesDelayTimer", consts.SingleFrameDelayTime, postTimerCallback)
  end
end
local UpdateAttributePreview_PostTimer = function(attributeCard, character, isVendorMenu)
  pickupUtil.CheckCharacterPickupID(character)
  local creature = isVendorMenu == true and pickupUtil.GetCreatureFromCharacter(character) or pickupUtil.GetUICreature(character)
  local attributes = GetCreaturesAttributes(creature)
  attributeCard:UpdateMeter_Preview(attributes, character, creature)
end
local GetPowerLevel = function(pickup, stage)
  local powerLevel
  local attributeTable = Pickup.GetGemAttributes(pickup, stage)
  if attributeTable ~= nil and type(attributeTable) == "table" then
    local temp = attributeTable[1]
    powerLevel = util.Round(temp.Value * 10)
  end
  return powerLevel
end
return {
  GetDisplayName = GetDisplayName,
  GetShortDisplayName = GetShortDisplayName,
  GetStatDisplayName = GetStatDisplayName,
  GetDescription = GetDescription,
  GetMax = GetMax,
  GetCreaturesAttributes = GetCreaturesAttributes,
  GetAttributesTable = GetAttributesTable,
  GetRuneInfoAttributesTable = GetRuneInfoAttributesTable,
  SetCurrentlyEquippedAttributes = SetCurrentlyEquippedAttributes,
  UpdateCurrentlyEquippedAttributes = UpdateCurrentlyEquippedAttributes,
  GetCurrentlyEquippedAttributes = GetCurrentlyEquippedAttributes,
  GetCurrentlyEquippedAttributeOnCharacter = GetCurrentlyEquippedAttributeOnCharacter,
  MarkAttributesInitialized = MarkAttributesInitialized,
  MarkAttributesUninitialized = MarkAttributesUninitialized,
  AttributesInitialized = AttributesInitialized,
  UpdateAttributeMeters = UpdateAttributeMeters,
  UpdateRuneInfoAttributeMeters = UpdateRuneInfoAttributeMeters,
  UpdateAttributeGroup = UpdateAttributeGroup,
  GetUICreatureAttributes = GetUICreatureAttributes,
  UpdateAttributePreview = UpdateAttributePreview,
  UpdateAttributePreview_PostTimer = UpdateAttributePreview_PostTimer,
  GetPowerLevel = GetPowerLevel
}
