local tablex = require("core.tablex")
local colors = require("ui.colors")
local consts = require("ui.consts")
local fsm = require("ui.fsm")
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 logManager = require("ui.logManager")
local UpdateAmountText = function(childGO, amount, isMaxed)
  local sign = 0 <= amount and "+" or "-"
  local textStr = isMaxed and "MAX" or sign .. amount
  local textHdl = util.GetTextHandle(childGO, "count")
  if textHdl ~= nil then
    game.UI.SetText(textHdl, textStr)
    return true
  end
  return false
end
local Anim_ProcessCallback = function(childGO, animTable)
  local displayStr, rarityColor
  local resourceType = animTable.Type
  local iconText = ""
  local materialName
  if resourceType == resourceConsts.RESOURCE_FLAG_RUNE then
    local runeID = animTable.Resource
    displayStr = runeUtil.GetRuneDisplayNameFromID(runeID)
    rarityColor = runeUtil.GetRuneColorFromID(runeID)
    iconText = runeUtil.GetRuneIconFromID(runeID)
  elseif resourceType == resourceConsts.RESOURCE_FLAG_PICKUP then
    local resource = animTable.Resource
    local shouldUseMaterialSwap = pickupUtil.ShouldUseMaterialSwap(resource)
    displayStr = pickupUtil.GetDisplayName(resource)
    rarityColor = resourceUtil.GetRarityColor(resource)
    if shouldUseMaterialSwap then
      materialName = resourceUtil.GetMaterialSwapName(resource)
    else
      iconText = resourceUtil.GetIconText(resource)
    end
  else
    local resource = animTable.Resource
    local _, displayLams = game.Resources.GetLamsNameId(resource)
    local shouldUseMaterialSwap = resource == "Hacksilver" or resource == "EconomyXP"
    if displayLams == 0 then
      engine.Warning("Bad Resource definition: " .. tostring(resource) .. " has lamsID of ZERO!")
    end
    displayStr = util.GetLAMSMsg(displayLams)
    rarityColor = resourceUtil.GetRarityColor(resource)
    if shouldUseMaterialSwap then
      materialName = resource
    else
      iconText = resourceUtil.GetIconText(resource)
    end
  end
  if displayStr ~= nil then
    local textHdl = util.GetTextHandle(childGO, "text")
    if textHdl ~= nil then
      game.UI.SetText(textHdl, displayStr)
    end
  end
  if materialName == nil and iconText == "" then
    engine.Warning(tostring(tostring(resourceType) .. " resource " .. tostring(animTable.Resource) .. " named " .. util.GetLAMSStringFromMsg(displayStr)) .. " does not have an icon!")
  end
  if util.IsStringNilOrEmpty(materialName) then
    materialName = consts.EMPTY_MATERIAL_NAME
  end
  childGO:SetMaterialSwap(materialName)
  local thIconText = util.GetTextHandle(childGO, "icon_text")
  if thIconText ~= nil then
    game.UI.SetText(thIconText, iconText)
  end
  if childGO.FindMaterialEntry then
    local colorEntry = childGO:FindMaterialEntry("body_text", "LayerX.cst_EmissiveTint")
    if colorEntry ~= nil then
      colorEntry.Value = engine.Vector.New(rarityColor[1], rarityColor[2], rarityColor[3])
    end
  end
  local childGOHasText = UpdateAmountText(childGO, animTable.Amount, animTable.IsMaxed)
  if childGOHasText then
    local sequence = animTable.Sequence
    local rowIndex = animTable.RowIndex
    local rowInfo = {ChildGO = childGO, AnimTable = animTable}
    sequence._activeRows[rowIndex] = rowInfo
  end
end
local ResourceRow_GOSets = {
  {
    "blur|log_text_row1_blur",
    "log|log_text_row1"
  },
  {
    "blur|log_text_row2_blur",
    "log|log_text_row2"
  },
  {
    "blur|log_text_row3_blur",
    "log|log_text_row3"
  }
}
local ResourceRow_RowInParms = {
  FadeTime = 1,
  FadeValue = 1,
  ProcessCallback = Anim_ProcessCallback
}
local ResourceRow_FadeOutParms = {
  FadeValue = 0,
  FadeTime = 1,
  AnimStyle = consts.AS_Reverse,
  AnimStart = 1,
  AnimEnd = 0
}
local CONST_Max_Row_Count = #ResourceRow_GOSets
local CONST_Idle_Base_Time = 2
local CONST_Row_Update_Min_Time = 1
local MakeAnimsTable = function(setIndex, parmTable, optParams)
  local anims = {}
  for pathIndex, goPath in ipairs(ResourceRow_GOSets[setIndex]) do
    local newAnim = {}
    newAnim.GOPath = goPath
    for key, value in pairs(parmTable) do
      newAnim[key] = value
    end
    if optParams ~= nil then
      for key, value in pairs(optParams) do
        newAnim[key] = value
      end
      if 1 < pathIndex then
        newAnim.Sound = nil
      end
    end
    tablex.FastInsert(anims, newAnim, #anims + 1)
  end
  return anims
end
local Stage_RowIn_AssignRowToStageData = function(sequence, stageData)
  if stageData.StageName == "RowIn" then
    sequence._availableRowCount = sequence._availableRowCount - 1
    local rowIndex = CONST_Max_Row_Count - sequence._availableRowCount
    local sound = "SND_UX_HUD_Item_Notification_Basic"
    if stageData.Type == resourceConsts.RESOURCE_FLAG_RESOURCE then
      sound = resourceUtil.GetSound(stageData.Resource)
    end
    local additionalParams = {
      Resource = stageData.Resource,
      Type = stageData.Type,
      Amount = stageData.Amount,
      IsMaxed = stageData.IsMaxed,
      Sequence = sequence,
      RowIndex = rowIndex,
      Sound = sound
    }
    local anims = MakeAnimsTable(rowIndex, ResourceRow_RowInParms, additionalParams)
    stageData.Anims = anims
  end
end
local Stage_FinalWait_TimeoutCallback = function(stage, args)
  local sequence = stage._sequence
  sequence._availableRowCount = CONST_Max_Row_Count
  for i = 1, #sequence._activeRows do
    sequence._activeRows[i] = nil
  end
  if #sequence._resourceQueue > 0 then
    for _ = 1, math.min(CONST_Max_Row_Count, #sequence._resourceQueue) do
      local stageData = table.remove(sequence._resourceQueue, 1)
      Stage_RowIn_AssignRowToStageData(sequence, stageData)
      tablex.FastInsert(sequence._stageDataQueue, stageData, #sequence._stageDataQueue + 1)
    end
  end
  return true
end
local Stage_RowIn_TimeoutCallback = function(stage, args)
  local sequence = stage._sequence
  if sequence._activeStageName == "RowIn" and #sequence._stageDataQueue == 0 then
    local idleStage = {StageName = "Idle", Timer = CONST_Idle_Base_Time}
    tablex.FastInsert(sequence._stageDataQueue, idleStage, #sequence._stageDataQueue + 1)
    local fadeoutStage = {
      StageName = "FadeOut",
      Timer = 0,
      Anims = {}
    }
    for i = 1, CONST_Max_Row_Count - sequence._availableRowCount do
      local anims = MakeAnimsTable(i, ResourceRow_FadeOutParms)
      for _, anim in ipairs(anims) do
        tablex.FastInsert(fadeoutStage.Anims, anim, #fadeoutStage.Anims + 1)
      end
    end
    tablex.FastInsert(sequence._stageDataQueue, fadeoutStage, #sequence._stageDataQueue + 1)
    local finalWaitStage = {
      StageName = "FinalWait",
      Timer = 1,
      TimeoutCallback = function()
        return Stage_FinalWait_TimeoutCallback(stage)
      end
    }
    tablex.FastInsert(sequence._stageDataQueue, finalWaitStage, #sequence._stageDataQueue + 1)
  end
  return true
end
local CheckAndUpdateActiveRows = function(sequence, newStageDatas)
  local canExtendStageTimer = sequence._activeStageName == "Idle"
  for i = #newStageDatas, 1, -1 do
    for _, activeRow in ipairs(sequence._activeRows) do
      if newStageDatas[i].Resource == activeRow.AnimTable.Resource then
        local stageData = table.remove(newStageDatas, i)
        local newAmount = activeRow.AnimTable.Amount + stageData.Amount
        activeRow.AnimTable.Amount = newAmount
        UpdateAmountText(activeRow.ChildGO, newAmount, stageData.IsMaxed)
        if canExtendStageTimer then
          local timerID = sequence._activeStage._timerID
          local remainingTime = sequence._fsmState:GetTimerRemainingTime(timerID)
          if remainingTime < CONST_Row_Update_Min_Time then
            local deltaTime = remainingTime - CONST_Row_Update_Min_Time
            sequence._fsmState:AddTimeToTimer(timerID, deltaTime)
          end
        end
        break
      end
    end
  end
end
local CheckDuplicateAndAddAmount = function(resourceQueue, stageData)
  for _, queuedData in ipairs(resourceQueue) do
    if queuedData.Resource == stageData.Resource then
      queuedData.Amount = queuedData.Amount + stageData.Amount
      for _, anim in ipairs(queuedData.Anims) do
        anim.Amount = queuedData.Amount
      end
      return true
    end
  end
end
local AddStageCallback = function(sequence, addStageArgs, newStageDatas)
  local delayStagesIndex = 1
  if sequence._activeStageName == nil or sequence._activeStageName == "Reset" or sequence._activeStageName == "RowIn" or sequence._activeStageName == "Idle" then
    delayStagesIndex = sequence._availableRowCount + 1
    CheckAndUpdateActiveRows(sequence, newStageDatas)
  end
  for i = #newStageDatas, 1, -1 do
    if CheckDuplicateAndAddAmount(sequence._stageDataQueue, newStageDatas[i]) then
      table.remove(newStageDatas, i)
    end
  end
  local index = delayStagesIndex
  while index <= #newStageDatas do
    local stageData = newStageDatas[index]
    if stageData.StageName == "RowIn" then
      table.remove(newStageDatas, index)
      if not CheckDuplicateAndAddAmount(sequence._resourceQueue, stageData) then
        tablex.FastInsert(sequence._resourceQueue, stageData, #sequence._resourceQueue + 1)
      end
      index = index - 1
    end
    index = index + 1
  end
  for _, stageData in ipairs(newStageDatas) do
    Stage_RowIn_AssignRowToStageData(sequence, stageData)
  end
  if sequence._activeStageName == "Reset" or sequence._activeStageName == "RowIn" then
    return true
  elseif sequence._activeStageName == "Idle" then
    if 0 < #newStageDatas then
      local idleStage = sequence._activeStage
      for i in pairs(sequence._stageDataQueue) do
        sequence._stageDataQueue[i] = nil
      end
      idleStage._fsmState:StopTimer(idleStage._timerID)
      sequence._activeStage = nil
      sequence._activeStageName = nil
      sequence._waitingOnStage = false
    end
    return true
  elseif sequence._activeStageName == "FadeOut" or sequence._activeStageName == "FinalWait" then
    return false
  end
  return true
end
local ActivateCallback = function(sequence, activateArgs, newStageDatas)
  sequence._activeRows = {}
  sequence._resourceQueue = {}
  sequence._availableRowCount = CONST_Max_Row_Count
  return true
end
local InitSequenceArgs = {
  ParentGOName = "ResourceLog",
  InitStages = {
    {
      StageName = "Reset",
      Timer = 0.75,
      Anims = {
        {
          GOPath = "log|log_text_row1",
          FadeValue = 0,
          FadeTime = 0,
          AnimRate = 0,
          AnimStart = 0
        },
        {
          GOPath = "log|log_text_row2",
          FadeValue = 0,
          FadeTime = 0,
          AnimRate = 0,
          AnimStart = 0
        },
        {
          GOPath = "log|log_text_row3",
          FadeValue = 0,
          FadeTime = 0,
          AnimRate = 0,
          AnimStart = 0
        },
        {
          GOPath = "blur|log_text_row1_blur",
          FadeValue = 0,
          FadeTime = 0,
          AnimRate = 0,
          AnimStart = 0
        },
        {
          GOPath = "blur|log_text_row2_blur",
          FadeValue = 0,
          FadeTime = 0,
          AnimRate = 0,
          AnimStart = 0
        },
        {
          GOPath = "blur|log_text_row3_blur",
          FadeValue = 0,
          FadeTime = 0,
          AnimRate = 0,
          AnimStart = 0
        }
      }
    }
  },
  ActivateCallback = ActivateCallback,
  AddStageCallback = AddStageCallback
}
local Dispatch_ActivateCallback = function(sequence, args, stages)
  if sequence._resourceDispatch == nil then
    sequence._resourceDispatch = {}
  end
  sequence._fsmState:AddResourceMessageToSubManager(stages)
  sequence._manager:Handle_SequenceFinished(sequence)
  return false
end
local Dispatch_InitSequenceArgs = {
  ParentGOName = "DummyObjectForLogs",
  InitStages = {},
  ActivateCallback = Dispatch_ActivateCallback
}
local Dispatch_CreateSequenceStages = function(args)
  local newStages = {
    {
      StageName = "RowIn",
      Timer = 1,
      Resource = args.ResourceName,
      Type = args.ResourceType,
      Amount = args.Amount,
      IsMaxed = args.IsMaxed,
      Anims = {},
      TimeoutCallback = Stage_RowIn_TimeoutCallback
    }
  }
  return newStages
end
return {
  InitSequenceArgs = InitSequenceArgs,
  Dispatch_InitSequenceArgs = Dispatch_InitSequenceArgs,
  Dispatch_CreateSequenceStages = Dispatch_CreateSequenceStages
}
