local classlib = require("core.class")
local tablex = require("core.tablex")
local consts = require("ui.consts")
local hooks = require("ui.hooks")
local message = require("ui.message")
local messageRow = require("ui.messageRow")
local resourceUtil = require("ui.resourceUtil")
local util = require("ui.util")
local UI = game.UI
local Log = classlib.Class("Log")
function Log:init(state, args)
  assert(state ~= nil, "Attempted to create new Log without state!")
  assert(args ~= nil, "Attempted to create new Log without args!")
  assert(args.LogObjectName ~= nil, "Attempted to create new Log without LogObjectName!")
  self._state = state
  self._active = false
  self._name = util.GetValueWithDefault(args.Name, "LogNotNamed")
  self._logObjectName = args.LogObjectName
  self._goLog = util.GetUiObjByName(self._logObjectName)
  assert(self._goLog ~= nil, "Log " .. self._logObjectName .. " game object not found!")
  self._timerName = util.GetValueWithDefault(args.TimerName, self._name .. "_Displayed")
  self._maxDisplayRowCount = util.GetValueWithDefault(args.MaxDisplayRowCount, 0)
  self._maxMessageCount = util.GetValueWithDefault(args.MaxMessageCount, 20)
  self._messageTotalDisplayTime = args.MessageTotalDisplayTime
  self._fadeOutTime = util.GetValueWithDefault(args.FadeOutTime, 0.2)
  self._headerIconFadeTime = util.GetValueWithDefault(args.HeaderIconFadeTime, 0.25)
  self._headerTextFadeTime = util.GetValueWithDefault(args.HeaderTextFadeTime, self._headerIconFadeTime)
  self._animInRate = util.GetValueWithDefault(args.AnimInRate, 2)
  self._animInEndTime = util.GetValueWithDefault(args.AnimInEndTime, 1)
  self._animOutRate = util.GetValueWithDefault(args.AnimOutRate, 2)
  self._material = util.GetValueWithDefault(args.Material, "body_text")
  self._layer = util.GetValueWithDefault(args.Layer, "LayerX")
  self._attribute = util.GetValueWithDefault(args.Attribute, "cst_EmissiveTint")
  self._buttonPromptText = util.GetValueWithDefault(args.ButtonPromptText, nil)
  self._OnStart = args.OnStart
  self._OnFinish = args.OnFinish
  self._hookTokens = {}
  self._messageArray = {}
  self._eventHandlers = args.EventHandlers
  self._textIndexNames = {}
  self:SetupTextIndexNames()
  self:SetupText()
end
function Log:Activate()
  if not self._active then
    if self._eventHandlers ~= nil and type(self._eventHandlers) == "table" then
      for _, item in ipairs(self._eventHandlers) do
        util.InstallEventHooks(hooks, self, item.Events, item.Handler)
      end
    end
    self._active = true
  end
end
function Log:Deactivate()
  if self._active then
    self._active = false
    if self._eventHandlers ~= nil and type(self._eventHandlers) == "table" then
      for _, item in ipairs(self._eventHandlers) do
        util.UninstallEventHooks(hooks, self, item.Events)
      end
    end
    tablex.Clear(self._hookTokens)
  end
end
function Log:SetupTextIndexNames()
  self._textIndexNames.Header = self._name .. "_HeaderText"
  self._textIndexNames.RowText = self._name .. "_RowText"
  self._textIndexNames.RowCount = self._name .. "_RowCount"
end
function Log:ShowAllInstancedObjects()
  self._goLog:Show()
  self:ShowInstancedChildObject(self._goLog, "log")
  self:ShowInstancedChildObject(self._goLog, "blur")
  self:ShowInstancedChildObject(self._goLog, "glow")
  self:ShowInstancedChildObject(self._goLog, "log_icon")
  self:ShowInstancedChildObject(self._goLog, "log_text_category")
  self:ShowInstancedChildObject(self._goLog, "log_text_header")
  for rowIndex = 0, self._maxDisplayRowCount do
    self:ShowInstancedChildObject(self._goLog, "log_text_row" .. rowIndex)
  end
end
function Log:FadeOutAll(fadeOutTime)
  if fadeOutTime == nil then
    fadeOutTime = 0
  end
  self:SetAlphaFade("log_icon", 0, fadeOutTime)
  self:SetAlphaFade("log_text_category", 0, fadeOutTime)
  self:SetAlphaFade("log_text_header", 0, fadeOutTime)
  for rowIndex = 0, self._maxDisplayRowCount do
    self:SetAlphaFade("log_text_row" .. rowIndex, 0, fadeOutTime)
  end
end
function Log:InitAllInstancedObjectsAnimation()
  self:InitChildObjectAnimation(self._goLog, "log")
  self:InitChildObjectAnimation(self._goLog, "blur")
  self:InitChildObjectAnimation(self._goLog, "glow")
  for rowIndex = 0, self._maxDisplayRowCount do
    self:InitChildObjectAnimation(self._goLog, "log_text_row" .. rowIndex)
  end
end
function Log:UpdateDisplayedRowCounts()
  if self:GetMessageCount() > 0 then
    local firstMessage = self:GetMessage(1)
    firstMessage:UpdateDisplayedRowCounts()
  end
end
function Log:Initialize()
  self:ClearMessages()
  self:ResetAllText()
  self:ShowAllInstancedObjects()
  self:InitAllInstancedObjectsAnimation()
  self:FadeOutAll()
  self._goLog:Hide()
end
function Log:Start(message)
  if message ~= nil then
    self._goLog:Show()
    self:ResetAllText()
    if self._OnStart ~= nil then
      self._OnStart(self, message)
    end
    self:AnimateIn(message)
  end
end
function Log:AnimateIn(message)
  self:AnimateInChildObject(self._goLog, "log", nil)
  self:AnimateInChildObject(self._goLog, "blur", nil)
  self:AnimateInChildObject(self._goLog, "glow", nil)
  message:UpdateTotalDisplayTime(consts.LOG_MODE_ANIM_IN)
  self._state:StartTimer(self._timerName, message:GetDisplayTime(consts.LOG_MODE_ANIM_IN), function()
    message:Start(function(message)
      self:AnimateOut(message)
    end)
  end)
end
function Log:AnimateOut(message)
  local animOutTime = message:GetDisplayTime(consts.LOG_MODE_ANIM_OUT)
  self:AnimateOutChildObject(self._goLog, "log", nil)
  self:AnimateOutChildObject(self._goLog, "blur", nil)
  self:AnimateOutChildObject(self._goLog, "glow", nil)
  self:FadeOutAll(animOutTime)
  message:UpdateTotalDisplayTime(consts.LOG_MODE_ANIM_OUT)
  self._state:StartTimer(self._timerName, animOutTime, function()
    self:Finish()
  end)
end
function Log:Finish()
  self:DequeueMessage()
  self:Deactivate()
  self:ResetAllText()
  if self._OnFinish ~= nil then
    self._OnFinish(self, message)
  end
  if self:GetMessageCount() >= 1 then
    self._state:StartTimer(self._timerName, self._fadeOutTime, function()
      self:Start(self:GetMessage(1))
    end)
  else
    self._goLog:Hide()
  end
end
function Log:SetTextColor(objName, color)
  util.SetColor(objName, color, self._material, self._layer, self._attribute)
end
function Log:SetAlphaFade(objectName, alphaAmount, fadeOutTime)
  self:AlphaFadeInstancedChildObject(self._goLog, objectName, alphaAmount, fadeOutTime)
end
function Log:ShowButtonPrompt(buttonPromptText)
  if buttonPromptText == nil then
    buttonPromptText = self._buttonPromptText
  end
  local goChild = self:GetInstancedChildObject(self._goLog, "log")
  local goButtonPrompt = self:GetInstancedChildObject(self._goLog, "log_text_button_prompt")
  local thButtonPrompt = util.GetTextHandle(goButtonPrompt)
  goButtonPrompt:Show()
  UI.SetText(thButtonPrompt, buttonPromptText)
end
function Log:HideButtonPrompt()
  local goChild = self:GetInstancedChildObject(self._goLog, "log")
  local goButtonPrompt = self:GetInstancedChildObject(self._goLog, "log_text_button_prompt")
  local thButtonPrompt = util.GetTextHandle(goButtonPrompt)
  goButtonPrompt:Hide()
  UI.SetText(thButtonPrompt, "")
end
function Log:get_gameObject()
  return self._goLog
end
function Log:get_timerName()
  return self._timerName
end
function Log:get_animInRate()
  return self._animInRate
end
function Log:get_animInEndTime()
  return self._animInEndTime
end
function Log:EnqueueMessage(message)
  local currMessageCount = self:GetMessageCount()
  if currMessageCount < self:get_maxMessageCount() then
    self._messageArray[currMessageCount + 1] = message
  else
    self._state:DebugPrint("Text Message Queue Max Count Reached!")
  end
end
function Log:DequeueMessage()
  table.remove(self._messageArray, 1)
end
function Log:ClearMessages()
  tablex.Clear(self._messageArray)
end
function Log:CreateMessage(args, rowArray)
  return message.Message.New(self._state, {
    Log = self,
    Data = args.Data,
    Icon = args.Icon,
    Category = args.Category,
    Header = args.Header,
    Body = args.Body,
    Sound = args.Sound,
    MaxDisplayRowCount = self._maxDisplayRowCount,
    HeaderColor = args.HeaderColor,
    BodyColor = args.BodyColor,
    TotalDisplayTime = args.TotalDisplayTime,
    HeaderDisplayTime = args.HeaderDisplayTime,
    BodyDisplayTime = args.BodyDisplayTime,
    AnimInDisplayTime = args.AnimInDisplayTime,
    AnimOutDisplayTime = args.AnimOutDisplayTime,
    HeaderIconFadeTime = self._headerIconFadeTime,
    HeaderTextFadeTime = self._headerTextFadeTime,
    BodyTextFadeTime = self._fadeOutTime,
    Material = self._material,
    Layer = self._layer,
    Attribute = self._attribute,
    TextIndexNames = self._textIndexNames,
    RowArray = rowArray
  })
end
function Log:CreateMessageRow(args)
  return messageRow.MessageRow.New(self._state, {
    ResourceName = args.ResourceName,
    ResourceType = args.ResourceType,
    Count = args.Amount,
    IsMaxed = args.IsMaxed,
    AnimInRate = self._animInRate,
    AnimOutRate = self._animOutRate,
    RowTextFadeTime = self._fadeOutTime,
    Material = self._material,
    Layer = self._layer,
    Attribute = self._attribute,
    TextIndexNames = self._textIndexNames
  })
end
function Log:AddMessage(args)
  if args ~= nil then
    if self._messageTotalDisplayTime ~= nil then
      args.TotalDisplayTime = self._messageTotalDisplayTime
    end
    if args.RowArray == nil then
      local newMessage = self:CreateMessage(args, nil)
      self:HandleNewMessage(newMessage)
    else
      local regularMessageRows = {}
      local regularMessageRowIndex = 1
      for _, row in ipairs(args.RowArray) do
        if row:ShouldUseLargeRow() == true then
          local newMessage = self:CreateMessage(args, {row})
          self:HandleNewMessage(newMessage)
        else
          tablex.FastInsert(regularMessageRows, row, regularMessageRowIndex)
          regularMessageRowIndex = regularMessageRowIndex + 1
        end
      end
      if 0 < #regularMessageRows then
        local newMessage = self:CreateMessage(args, regularMessageRows)
        self:HandleNewMessage(newMessage)
      end
    end
  end
end
function Log:HandleNewMessage(newMessage)
  if self._state:HaveTimer(self._timerName) == false then
    self:EnqueueMessage(newMessage)
    self:Start(newMessage)
  elseif newMessage:GetRowCount() == 0 then
    self:EnqueueMessage(newMessage)
  else
    self:MoveItemsIntoMessageArray(newMessage)
    self:UpdateDisplayedRowCounts()
  end
end
function Log:MoveItemsIntoMessageArray(newMessage)
  for _, messageRow in ipairs(newMessage:get_rows()) do
    for messageIndex, message in ipairs(self._messageArray) do
      local rowThatTookCount = message:TakeCountFromRow(messageRow)
      if 0 < rowThatTookCount then
        local onCurrentlyDisplayingMessage = messageIndex == 1
        local rowThatTookCountIsVisible = message:ShouldUseLargeRow() == true or rowThatTookCount <= message:GetDisplayRowIndex()
        if onCurrentlyDisplayingMessage and rowThatTookCountIsVisible then
          self._state:RestartTimer(self._timerName)
        end
        break
      end
    end
  end
  newMessage:RemoveInvalidItems()
  for _, messageRow in ipairs(newMessage:get_rows()) do
    for _, existingMessage in ipairs(self._messageArray) do
      if existingMessage:ShouldUseLargeRow() == false and newMessage:ShouldUseLargeRow() == false and existingMessage:GetRowCount() < existingMessage:GetMaxDisplayRowCount() then
        existingMessage:TakeItem(messageRow)
      end
    end
  end
  newMessage:RemoveInvalidItems()
  if 0 < newMessage:GetRowCount() then
    self:EnqueueMessage(newMessage)
  end
end
function Log:GetMessageCount()
  return #self._messageArray
end
function Log:GetMessage(index)
  if index ~= nil and 0 < index and index <= self:GetMessageCount() then
    return self._messageArray[index]
  end
  return nil
end
function Log:get_maxMessageCount()
  return self._maxMessageCount
end
function Log:SetupText()
  if self._name ~= nil then
    self._state.shared[self._textIndexNames.Header] = ""
    self._state.shared[self._textIndexNames.RowText] = {
      n = self._maxDisplayRowCount
    }
    self._state.shared[self._textIndexNames.RowCount] = {
      n = self._maxDisplayRowCount
    }
  end
end
function Log:ResetAllText()
  if self._name ~= nil then
    self._state.shared[self._textIndexNames.Header] = ""
    for i = 0, self._maxDisplayRowCount do
      self._state.shared[self._textIndexNames.RowText][i] = ""
      self._state.shared[self._textIndexNames.RowCount][i] = ""
    end
  end
end
function Log:SetOneItemVariable(var, index, value)
  if index == nil then
    self._state.shared[var] = value
  else
    self._state.shared[var][index] = value
  end
end
function Log:GetInstancedChildObject(parentGO, instanceChildName)
  local childGO
  if parentGO ~= nil and not util.IsStringNilOrEmpty(instanceChildName) then
    childGO = parentGO:FindSingleGOByName(instanceChildName)
  end
  return childGO
end
function Log:InitChildObjectAnimation(parentGO, instanceChildName)
  local childGO = self:GetInstancedChildObject(parentGO, instanceChildName)
  if childGO ~= nil then
    UI.Anim(childGO, consts.AS_Forward, "", 0, 0)
  end
end
function Log:AnimateInChildObject(parentGO, instanceChildName, animRate)
  local childGO = self:GetInstancedChildObject(parentGO, instanceChildName)
  if childGO ~= nil then
    if animRate == nil then
      animRate = self._animInRate
    end
    UI.Anim(childGO, consts.AS_Forward, "", animRate, 0, self._animInEndTime)
  end
end
function Log:AnimateOutChildObject(parentGO, instanceChildName, animRate)
  local childGO = self:GetInstancedChildObject(parentGO, instanceChildName)
  if childGO ~= nil then
    if animRate == nil then
      animRate = self._animOutRate
    end
    if animRate == 0 then
      UI.Anim(childGO, consts.AS_Forward, "", 0, 0)
    else
      UI.Anim(childGO, consts.AS_Reverse, "", animRate, 1, 0)
    end
  end
end
function Log:ShowInstancedChildObject(parentGO, instanceChildName)
  local childGO = self:GetInstancedChildObject(parentGO, instanceChildName)
  if childGO ~= nil then
    childGO:Show()
  end
end
function Log:AlphaFadeInstancedChildObject(parentGO, instanceChildName, fadeAlpha, fadeTime)
  local childGO = self:GetInstancedChildObject(parentGO, instanceChildName)
  if childGO ~= nil then
    UI.AlphaFade(childGO, fadeAlpha, fadeTime)
  end
end
return {Log = Log}
