local classlib = require("core.class")
local tablex = require("core.tablex")
local colors = require("ui.colors")
local consts = require("ui.consts")
local messageRow = require("ui.messageRow")
local resourceUtil = require("ui.resourceUtil")
local util = require("ui.util")
local Audio = game.Audio
local UI = game.UI
local Message = classlib.Class("Message")
function Message:init(state, args)
  assert(state ~= nil, "Attempted to create new Message without state!")
  assert(args ~= nil, "Attempted to create new Message without args!")
  assert(args.Log ~= nil, "Attempted to create new Message without Log!")
  self._state = state
  self._log = args.Log
  self._data = args.Data
  self._icon = util.GetValueWithDefault(args.Icon, consts.EMPTY_MATERIAL_NAME)
  self._category = args.Category
  self._header = args.Header
  self._body = args.Body
  self._sound = args.Sound
  self._maxDisplayRowCount = util.GetValueWithDefault(args.MaxDisplayRowCount, 0)
  self._displayRowIndex = util.GetValueWithDefault(args.DisplayRowIndex, 0)
  self._headerColor = util.GetValueWithDefault(args.HeaderColor, colors.WHITE)
  self._bodyColor = util.GetValueWithDefault(args.BodyColor, self._color)
  self._totalDisplayTime = util.GetValueWithDefault(args.TotalDisplayTime, 4)
  self._headerDisplayTime = util.GetValueWithDefault(args.HeaderDisplayTime, 0.4)
  self._bodyDisplayTime = util.GetValueWithDefault(args.BodyDisplayTime, 0.3)
  self._animInDisplayTime = util.GetValueWithDefault(args.AnimInDisplayTime, 0.5)
  self._animOutDisplayTime = util.GetValueWithDefault(args.AnimOutDisplayTime, 0.1)
  self._headerIconFadeTime = util.GetValueWithDefault(args.HeaderIconFadeTime, 0.25)
  self._headerTextFadeTime = util.GetValueWithDefault(args.HeaderTextFadeTime, 0.25)
  self._bodyTextFadeTime = util.GetValueWithDefault(args.BodyTextFadeTime, 0.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._rows = {}
  self._displayTimes = {}
  self._textIndexNames = args.TextIndexNames
  if args.RowArray ~= nil then
    self:CreateRowArray(args.RowArray)
  end
  self:SetupDisplayTimes()
end
function Message:SetupDisplayTimes()
  self._displayTimes[consts.LOG_MODE_HEADER] = self._headerDisplayTime
  self._displayTimes[consts.LOG_MODE_BODY] = self._bodyDisplayTime
  self._displayTimes[consts.LOG_MODE_ANIM_IN] = self._animInDisplayTime
  self._displayTimes[consts.LOG_MODE_ANIM_OUT] = self._animOutDisplayTime
end
function Message:get_data()
  return self._data
end
function Message:get_icon()
  return self._icon
end
function Message:get_category()
  return self._category
end
function Message:get_header()
  return self._header
end
function Message:get_body()
  return self._body
end
function Message:get_headerColor()
  return self._headerColor
end
function Message:get_bodyColor()
  return self._bodyColor
end
function Message:get_rows()
  return self._rows
end
function Message:GetMaxDisplayRowCount()
  local count = self._maxDisplayRowCount
  if self:ShouldUseLargeRow() == true then
    count = 1
  end
  return count
end
function Message:GetDisplayRowIndex()
  local displayRowIndex = self._displayRowIndex
  if self:ShouldUseLargeRow() == true then
    displayRowIndex = 0
  end
  return displayRowIndex
end
function Message:GetRowCount()
  return #self._rows
end
function Message:GetRowByIndex(index)
  local row
  if index ~= nil then
    row = self._rows[index]
  end
  return row
end
function Message:GetCurrentRow()
  local row
  if self._displayRowIndex > 0 and self._displayRowIndex <= self:GetRowCount() then
    row = self._rows[self._displayRowIndex]
  end
  return row
end
function Message:GetDisplayTime(logMode)
  local displayTime = self._displayTimes[logMode]
  local rowCount = self:GetRowCount()
  if logMode == consts.LOG_MODE_HEADER then
    if util.IsStringNilOrEmpty(self._header) then
      displayTime = 0
    end
  elseif logMode == consts.LOG_MODE_BODY then
    if rowCount == 0 then
      displayTime = self._totalDisplayTime
    elseif self._displayRowIndex == rowCount then
      displayTime = math.max(self._bodyDisplayTime, self._totalDisplayTime)
    end
  end
  return displayTime
end
function Message:GetStartingMode()
  return self._header ~= nil and consts.LOG_MODE_HEADER or consts.LOG_MODE_BODY
end
function Message:GetNextMode()
  local nextMode = consts.LOG_MODE_ANIM_OUT
  local displayRowIndex = self:GetDisplayRowIndex()
  if not self:ShouldUseLargeRow() and displayRowIndex <= self:GetRowCount() and displayRowIndex <= self:GetMaxDisplayRowCount() then
    nextMode = consts.LOG_MODE_BODY
  end
  return nextMode
end
function Message:GetRowGOByIndex(rowIndex)
  local goLog = self._log:get_gameObject()
  local rowObjectName = "log_text_row" .. rowIndex
  local goRow = goLog:FindSingleGOByName(rowObjectName)
  assert(goRow ~= nil, "Message GetDisplayRowGO could not find row object " .. rowObjectName .. " !")
  return goRow
end
function Message:GetTimerName()
  return self._log:get_timerName()
end
function Message:SetDisplayRowIndex(index)
  self._displayRowIndex = index
end
function Message:ShouldUseLargeRow(message)
  local useLargeRow = false
  if self:GetRowCount() == 1 then
    local row = self:GetRowByIndex(1)
    useLargeRow = row:ShouldUseLargeRow()
  end
  return useLargeRow
end
function Message:CreateRowArray(newRowArray)
  tablex.Clear(self._rows)
  if newRowArray ~= nil and type(newRowArray) == "table" and 0 < #newRowArray then
    self._displayRowIndex = 1
    local index = 1
    for _, messageRow in ipairs(newRowArray) do
      if 0 < messageRow:get_count() then
        tablex.FastInsert(self._rows, messageRow, index)
        index = index + 1
      end
    end
  end
end
function Message:RemoveInvalidItems()
  for rowIndex = self:GetRowCount(), 1, -1 do
    if self._rows[rowIndex]:get_count() <= 0 then
      table.remove(self._rows, rowIndex)
    end
  end
end
function Message:TakeCountFromRow(otherRow)
  for rowIndex, row in ipairs(self._rows) do
    if row:TakeCount(otherRow) then
      return rowIndex
    end
  end
  return 0
end
function Message:TakeItem(otherRow)
  local newMessageRow = messageRow.MessageRow.New(self._state, {
    ResourceName = otherRow:get_resourceName(),
    ResourceType = otherRow:get_resourceType(),
    Count = otherRow:get_count(),
    IsMaxed = otherRow:get_isMaxed(),
    TextIndexNames = otherRow:get_textIndexNames()
  })
  tablex.FastInsert(self._rows, newMessageRow, #self._rows + 1)
  otherRow:set_count(-1)
end
function Message:UpdateTotalDisplayTime(logMode)
  local decrement = self:GetDisplayTime(logMode)
  self._totalDisplayTime = math.max(0, self._totalDisplayTime - decrement)
end
function Message:PlaySound()
  if self._sound ~= nil then
    Audio.PlaySound(self._sound)
  end
end
function Message:DisplayHeaderIcon()
  local goLog = self._log:get_gameObject()
  local goHeaderIcon = goLog:FindSingleGOByName("log_icon")
  if goHeaderIcon ~= nil then
    goHeaderIcon:SetMaterialSwap(self:get_icon())
    UI.AlphaFade(goHeaderIcon, 1, self._headerIconFadeTime)
  end
end
function Message:DisplayHeaderText()
  local goLog = self._log:get_gameObject()
  local goCategoryText = goLog:FindSingleGOByName("log_text_category")
  local categoryTextHandle
  local goHeaderText = goLog:FindSingleGOByName("log_text_header")
  if goCategoryText ~= nil then
    categoryTextHandle = util.GetTextHandle(goCategoryText)
    if categoryTextHandle ~= nil then
      local category = self:get_category()
      local categoryText = category == nil and "" or category
      UI.SetText(categoryTextHandle, categoryText)
      UI.AlphaFade(goCategoryText, 1, self._headerTextFadeTime)
    end
  end
  if goHeaderText ~= nil then
    self:SetOneItemVariable(self._textIndexNames.Header, nil, self:get_header())
    UI.AlphaFade(goHeaderText, 1, self._headerTextFadeTime)
  end
end
function Message:DisplayBodyText(goBody, bodyText, rowIndex, color)
  assert(goBody ~= nil, "Message attempted to call DisplayBodyText without a game object")
  if bodyText ~= nil and rowIndex ~= nil then
    util.SetGameObjectColor(goBody, color, self._material, self._layer, self._attribute)
    self:SetOneItemVariable(self._textIndexNames.RowText, rowIndex, bodyText)
    UI.AlphaFade(goBody, 1, self._bodyTextFadeTime)
  end
end
function Message:UpdateDisplayedRowCounts()
  if self:GetRowCount() > 0 then
    if self:ShouldUseLargeRow() == true then
      local row = self:GetRowByIndex(1)
      row:DisplayRowCount(0)
    else
      for index, row in ipairs(self:get_rows()) do
        row:DisplayRowCount(index)
      end
    end
  end
end
function Message:Start(messageFinishedCallback)
  self:PlaySound()
  self._log:Activate()
  if not util.IsStringNilOrEmpty(self:get_header()) then
    self:UpdateHeader(messageFinishedCallback)
  else
    self:UpdateBody(messageFinishedCallback)
  end
end
function Message:UpdateHeader(messageFinishedCallback)
  self:DisplayHeaderIcon()
  self:DisplayHeaderText()
  self:UpdateTotalDisplayTime(consts.LOG_MODE_HEADER)
  self._state:StartTimer(self:GetTimerName(), self:GetDisplayTime(consts.LOG_MODE_HEADER), function()
    self:UpdateBody(messageFinishedCallback)
  end)
end
function Message:UpdateBody(messageFinishedCallback)
  assert(messageFinishedCallback ~= nil, "Message attempted to call Start without a callback!")
  local displayRowIndex = self:GetDisplayRowIndex()
  local currRow = self:GetCurrentRow()
  if currRow ~= nil and displayRowIndex <= self:GetMaxDisplayRowCount() then
    currRow:Start(self, function(messageRow, nextMode)
      if nextMode == consts.LOG_MODE_BODY then
        self:UpdateBody(messageFinishedCallback)
      else
        messageFinishedCallback(self)
      end
    end)
  else
    local bodyText = self:get_body()
    if bodyText ~= nil then
      self:DisplayBodyText(self:GetRowGOByIndex(displayRowIndex), bodyText, displayRowIndex, self:get_bodyColor())
      self._state:StartTimer(self:GetTimerName(), self:GetDisplayTime(consts.LOG_MODE_BODY), function()
        messageFinishedCallback(self)
      end)
    else
      messageFinishedCallback(self)
    end
  end
end
function Message:SetOneItemVariable(var, index, value)
  if index == nil then
    self._state.shared[var] = value
  else
    self._state.shared[var][index] = value
  end
end
return {Message = Message}
