local classlib = require("core.class")
local tablex = require("core.tablex")
local animationUtil = require("ui.animationUtil")
local button = require("ui.button")
local consts = require("ui.consts")
local hooks = require("ui.hooks")
local lamsConsts = require("ui.lamsConsts")
local list = require("ui.list")
local pickupConsts = require("ui.pickupConsts")
local resourceConsts = require("ui.resourceConsts")
local tutorialUtil = require("ui.tutorialUtil")
local util = require("ui.util")
local Audio = game.Audio
local UI = game.UI
local Menu = classlib.Class("Menu")
local SetupFooterButtons = function(menu, footerButtonInfo)
  if footerButtonInfo ~= nil then
    for index, info in ipairs(footerButtonInfo) do
      local newButton = button.Button.New(menu, {
        Item = info.Item,
        Text = info.Text,
        EventHandlers = info.EventHandlers
      })
      tablex.FastInsert(menu._footerButtons, newButton, index)
    end
  end
end
function Menu:init(state, args)
  self._state = state
  self._name = util.GetValueWithDefault(args.Name, self._state.name)
  self._active = false
  self._updateFooterText = false
  self._submenuListName = nil
  self._sortListName = nil
  self._hookTokens = {}
  self._lists = {}
  self._footerButtons = {}
  self._instructionEntries = {}
  self._instructionArgs = {}
  SetupFooterButtons(self, args.FooterButtonInfo)
end
function Menu:Activate()
  if not self._active then
    self._active = true
    self:UpdateFooterButtons(false, true)
  end
end
function Menu:ForceDeactivate()
  self._active = false
  local hideList = true
  local clearButtons = true
  for _, list in pairs(self._lists) do
    list:Deactivate(hideList, clearButtons)
  end
  self:UpdateFooterButtons(false, false)
  tablex.Clear(self._hookTokens)
end
function Menu:Deactivate(deactivateLists, hideLists)
  if self._active then
    self._active = false
    if deactivateLists == true then
      local hideList = true
      local clearButtons = true
      if hideLists ~= nil then
        hideList = hideLists
      end
      for _, list in pairs(self._lists) do
        list:Deactivate(hideList, clearButtons)
      end
    end
    self:UpdateFooterButtons(false, false)
    tablex.Clear(self._hookTokens)
  end
end
function Menu:get_active()
  return self._active
end
function Menu:get_name()
  return self._name
end
function Menu:get_submenuListName()
  return self._submenuListName
end
function Menu:get_sortListName()
  return self._sortListName
end
function Menu:get_instructionEntries()
  return self._instructionEntries
end
function Menu:set_instructionEntries(instructionEntries)
  assert(instructionEntries ~= nil, "Menu:set_instructionEntries called without instruction entries table!")
  self._instructionEntries = instructionEntries
end
function Menu:get_instructionArgs()
  return self._instructionArgs
end
function Menu:set_instructionArgs(instructionArgs)
  if self._instructionArgs ~= nil then
    self._instructionArgs = instructionArgs
  end
end
function Menu:GetStateName()
  return self._state.name
end
function Menu:SetList(listName, list)
  self._lists[listName] = list
end
function Menu:GetList(listName)
  assert(not util.IsStringNilOrEmpty(listName), "GetList called with invalid list name!")
  return self._lists[listName]
end
function Menu:UpdateFooterButtonText(spacer)
  if self._updateFooterText then
    local buttonPromptText = ""
    local footer_spacer = spacer or consts.Footer_Spacer
    for index, button in ipairs(self._footerButtons) do
      if button:get_active() then
        buttonPromptText = buttonPromptText .. button:get_text()
        if index < #self._footerButtons then
          buttonPromptText = buttonPromptText .. footer_spacer
        end
      end
    end
    self._updateFooterText = false
    self._state:SendEventToUIFsm("globalMenu", "EVT_SET_FOOTER_BUTTON_PROMPT", buttonPromptText)
  end
end
function Menu:UpdateFooterButton(item, activate, text)
  for _, button in ipairs(self._footerButtons) do
    if button:get_item() == item then
      if button:get_active() ~= activate then
        if activate then
          button:Activate()
        else
          button:Deactivate()
        end
        self._updateFooterText = true
      end
      if text ~= nil then
        button:set_text(text)
        self._updateFooterText = true
      end
      break
    end
  end
end
function Menu:UpdateFooterButtons(activate, updateText)
  for _, button in ipairs(self._footerButtons) do
    self:UpdateFooterButton(button:get_item(), activate)
  end
  if updateText then
    self:UpdateFooterButtonText()
  end
end
function Menu:InstructionEntry_GetStateName(instructionEntry)
  return instructionEntry.StateName
end
function Menu:InstructionEntry_GetListName(instructionEntry)
  return instructionEntry.ListName
end
function Menu:InstructionEntry_GetItem(instructionEntry)
  return instructionEntry.Item
end
function Menu:InstructionEntry_GetEventName(instructionEntry)
  return instructionEntry.EventName
end
function Menu:InstructionEntry_GetEventParams(instructionEntry)
  return instructionEntry.EventParams or {}
end
function Menu:InstructionEntry_SendEvent(instructionEntry)
  local eventName = self:InstructionEntry_GetEventName(instructionEntry)
  local eventParams = self:InstructionEntry_GetEventParams(instructionEntry)
  assert(not util.IsStringNilOrEmpty(eventName), "InstructionEntry_SendEvent attempted to send invalid event!")
  if hooks.HookIsPadEvent(eventName) then
    local rootFsmName = self._state:GetRoot().name
    self._state:SendPadEventHookToUIFsm(rootFsmName, eventName, unpack(eventParams))
  else
    self._state:SendHookToUI(eventName, unpack(eventParams))
  end
end
function Menu:AddInstructionEntry(instructionEntry)
  local instructionEntryIndex = #self._instructionEntries + 1
  tablex.FastInsert(self._instructionEntries, instructionEntry, instructionEntryIndex)
end
function Menu:AddInstructionArg(index, value)
  self._instructionArgs[index] = value
end
function Menu:InitializeInstructionEntries()
  if #self._instructionEntries > 0 then
    self._state:DebugPrint("The last set of menu instruction entries were not completed!")
    util.DebugPrintTable(self._state, self._instructionEntries)
  end
  tablex.Clear(self._instructionEntries)
  tablex.Clear(self._instructionArgs)
end
function Menu:ExecuteInstructions()
  local menuStateName = self:GetStateName()
  local entryIndex = 1
  local currEntry = self._instructionEntries[entryIndex]
  while currEntry ~= nil do
    local entryStateName = self:InstructionEntry_GetStateName(currEntry)
    if entryStateName ~= menuStateName then
      local hasMore = false
      for i = entryIndex + 1, #self._instructionEntries do
        local stateName = self:InstructionEntry_GetStateName(self._instructionEntries[i])
        if stateName == menuStateName then
          hasMore = true
          entryIndex = i
          break
        end
      end
      if not hasMore then
        break
      end
    else
      table.remove(self._instructionEntries, entryIndex)
      local listName = self:InstructionEntry_GetListName(currEntry)
      local list = self:GetList(listName)
      if list ~= nil then
        local itemToSelect = self:InstructionEntry_GetItem(currEntry)
        local useOnGainFocus = true
        list:SelectItem(itemToSelect, useOnGainFocus)
      end
      if self:InstructionEntry_GetEventName(currEntry) ~= nil then
        self:InstructionEntry_SendEvent(currEntry)
      end
    end
    currEntry = self._instructionEntries[entryIndex]
  end
  if #self._instructionEntries == 0 then
    tablex.Clear(self._instructionArgs)
    tutorialUtil.BeginTutorial(self._state)
  end
end
function Menu:HasInstructionEntryForMenuState()
  local hasEntry = false
  local menuStateName = self:GetStateName()
  for _, entry in ipairs(self._instructionEntries) do
    local entryStateName = self:InstructionEntry_GetStateName(entry)
    if entryStateName == menuStateName then
      hasEntry = true
      break
    end
  end
  return hasEntry
end
function Menu:UpdateSubmenuListIndicators(list)
  local goNextIndicator = list:GetDirectionIndicator(consts.STEP_NEXT)
  local buttonCount = list:GetButtonCount()
  local animPercent = buttonCount / consts.DEFAULT_ANIM_FRAME_COUNT
  UI.Anim(goNextIndicator, consts.AS_Forward, "", 0, animPercent)
end
function Menu:SetMenuListLabelText(listName, text)
  local list = self:GetList(listName)
  list:SetLabelText(text)
end
function Menu:SetSubmenuListLabelText(listName, text)
  local submenuList = self:GetList(self._submenuListName)
  submenuList:SetLabelText(text)
end
function Menu:ClearSubmenuListLabelText()
  local submenuList = self:GetList(self._submenuListName)
  submenuList:SetLabelText("")
end
function Menu:SetupSubmenuList(submenuListName, previousEvents, nextEvents, canScroll)
  local listName = self._name .. "_" .. submenuListName
  local submenuList = list.List.New(self._state, {
    Name = listName,
    ListObjectName = submenuListName,
    MaxFocusableObjectCount = resourceConsts.SUBMENU_MAX_INDEX,
    EmptyTextLamsID = lamsConsts.NoSubmenusAvailable,
    PreviousEvents = previousEvents == nil and {
      "EVT_Left_Press",
      "EVT_LeftStick_Left"
    } or previousEvents,
    NextEvents = nextEvents == nil and {
      "EVT_Right_Press",
      "EVT_LeftStick_Right"
    } or nextEvents,
    DelayTime = 0.5,
    EnableLabel = tablex.Contains(consts.inWorldMenu_ShowSubmenuLabels, self._name),
    AfterDelay = function(list, delayStartButtonIndex, currButtonIndex)
      if self._state.SubmenuList_AfterDelay ~= nil then
        self._state:SubmenuList_AfterDelay(list, delayStartButtonIndex, currButtonIndex)
      end
    end,
    Button_Update = function(button)
      if self._state.SubmenuList_Button_Update ~= nil then
        self._state:SubmenuList_Button_Update(button)
      end
    end,
    Button_OnGainFocus = function(button)
      self._state:SendEventUI("EVT_Submenu_OnTransition", button:get_item())
      if self._state.SubmenuList_Button_OnGainFocus ~= nil then
        self._state:SubmenuList_Button_OnGainFocus(button)
        local goLabel = button:get_list():get_LabelGameObject()
        if goLabel ~= nil then
          animationUtil.DoDefaultTransitionAnim(goLabel)
        end
      end
    end,
    Button_OnLoseFocus = function(button)
      if self._state.SubmenuList_Button_OnLoseFocus ~= nil then
        self._state:SubmenuList_Button_OnLoseFocus(button)
      end
    end,
    PadRepeatRate = canScroll == false and 0 or nil
  })
  self:SetList(submenuListName, submenuList)
  self._submenuListName = submenuListName
  tutorialUtil.RegisterDesaturationList(listName, submenuList)
  submenuList:SetSelectedButton(1, false)
end
function Menu:RefreshSubmenuList(list, newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, itemGetTextFunc, skipListCentering)
  list:Refresh(newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, itemGetTextFunc)
  local animRate = 0
  local targetTimeline = skipListCentering and 0 or 0.033333335 * #newItemArray
  local goAnim = list:GetButtonRootGameObject()
  UI.Anim(goAnim, consts.AS_Forward, "", animRate, targetTimeline)
  self:UpdateSubmenuListIndicators(list)
end
function Menu:CreateSublist(listObjectName, sublistName, mainListItem, maxFocusableObjectCount, itemCompareFunc, getSortItemFunc, sortFunc)
  local subList = list.List.New(self._state, {
    MaxFocusableObjectCount = maxFocusableObjectCount,
    Name = sublistName,
    ListObjectName = listObjectName,
    HoverToSelect = true,
    EventHandlers = {
      {
        Events = {
          "EVT_Advance_Release"
        },
        Handler = function()
          if self._state.SubList_AdvanceReleaseHandler then
            self._state:SubList_AdvanceReleaseHandler(mainListItem)
          end
        end
      },
      {
        Events = {
          "EVT_MOUSE_RELEASED"
        },
        Handler = function()
          local selected = UI.GetEventSenderGameObject()
          local list = self._state.menu:GetList(sublistName)
          local button = list:GetSelectedButton()
          if button ~= nil and selected == button:GetInstancedChildObject() and self._state.SubList_AdvanceReleaseHandler then
            self._state:SubList_AdvanceReleaseHandler(mainListItem)
          end
        end
      },
      {
        Events = {
          "EVT_Back_Release",
          "EVT_MOUSE_RIGHT_CLICKED"
        },
        Handler = function()
          if self._state.SubList_BackReleaseHandler then
            self._state:SubList_BackReleaseHandler(mainListItem)
          end
        end
      },
      {
        Events = {
          "EVT_Square_Release"
        },
        Handler = function()
          if self._state.SubList_SquareReleaseHandler then
            self._state:SubList_SquareReleaseHandler(mainListItem)
          end
        end
      },
      {
        Events = {
          "EVT_Triangle_Release"
        },
        Handler = function()
          if self._state.SubList_TriangleReleaseHandler then
            self._state:SubList_TriangleReleaseHandler(mainListItem)
          end
        end
      },
      {
        Events = {
          "EVT_R3_Release"
        },
        Handler = function()
          if self._state.SubList_R3ReleaseHandler then
            self._state:SubList_R3ReleaseHandler(mainListItem)
          end
        end
      },
      {
        Events = {
          "EVT_R2_Release"
        },
        Handler = function()
          if self._state.SubList_R2ReleaseHandler then
            self._state:SubList_R2ReleaseHandler(mainListItem)
          end
        end
      }
    },
    Button_Update = function(button)
      self._state:SubList_Button_Update(button)
    end,
    Button_OnGainFocus = function(button)
      if self._state.SubList_Button_OnGainFocus then
        self._state:SubList_Button_OnGainFocus(button)
      end
    end,
    Button_OnLoseFocus = function(button)
      if self._state.SubList_Button_OnLoseFocus then
        self._state:SubList_Button_OnLoseFocus(button)
      end
    end,
    Button_ItemCompare = itemCompareFunc,
    Button_GetSortItem = getSortItemFunc,
    Sort = sortFunc
  })
  self:SetList(sublistName, subList)
  tutorialUtil.RegisterDesaturationList(self._name .. "_" .. sublistName, subList)
end
function Menu:SetupSubLists(listObjectName, mainListItems, itemCompareFunc, getSortItemFunc, sortFunc)
  for _, mainListItem in ipairs(mainListItems) do
    local subList_maxFocusableObjectCount = 8
    self:CreateSublist(listObjectName, mainListItem, mainListItem, subList_maxFocusableObjectCount, itemCompareFunc, getSortItemFunc, sortFunc)
  end
end
function Menu:EquippedItemSublist_UpdateEquippedItemHeader(subList, equippedItem, itemGetTextFunc, isOnEquip)
  local equippedItemDisplayName
  local newItem = self._previousEquippedItem ~= equippedItem
  self._previousEquippedItem = equippedItem
  if util.IsStringNilOrEmpty(equippedItem) or equippedItem == pickupConsts.INVALID_RUNE_ID then
    equippedItemDisplayName = util.GetLAMSMsg(lamsConsts.NothingEquipped)
  else
    equippedItemDisplayName = itemGetTextFunc(equippedItem)
  end
  local goListButtonRoot = subList:GetButtonRootGameObject()
  local goEquippedItemHeader = goListButtonRoot:FindSingleGOByName("EquippedItemHeader")
  local thEquippedItemHeaderText = util.GetTextHandle(goEquippedItemHeader, "EquippedItemHeaderText")
  if newItem and isOnEquip then
    local animRate = 1
    local pStart = 0
    local pEnd = 1
    UI.Anim(goEquippedItemHeader, consts.AS_Forward, "", animRate, pStart, pEnd)
    local goOnEquipItemFlourish = goListButtonRoot:FindSingleGOByName("ListItemEquipFlourish")
    animRate = 1
    pStart = 0
    pEnd = 1
    UI.Anim(goOnEquipItemFlourish, consts.AS_Forward, "", animRate, pStart, pEnd)
  end
  goEquippedItemHeader:Show()
  UI.SetText(thEquippedItemHeaderText, equippedItemDisplayName)
end
function Menu:RefreshEquippedItemSublist(subListName, headerText, getSubListItemsFunc, equippedItem, itemDetermineFocusabilityFunc, itemGetTextFunc, select_useOnGainFocus, selectEquippedItem, isOnEquip, deactivateSublist)
  local subList = self:GetList(subListName)
  local currSelectedItem = subList:GetSelectedItem()
  subList:SetHeaderText(headerText)
  self:EquippedItemSublist_UpdateEquippedItemHeader(subList, equippedItem, itemGetTextFunc, isOnEquip)
  local newItemArray = getSubListItemsFunc(subListName)
  local refresh_showList = false
  local refresh_useOnGainFocus = false
  subList:Refresh(newItemArray, refresh_showList, refresh_useOnGainFocus, itemDetermineFocusabilityFunc, itemGetTextFunc)
  if deactivateSublist == true then
    local shouldSelectEquippedItem = selectEquippedItem == true and equippedItem ~= nil
    local itemToSelect = shouldSelectEquippedItem and equippedItem or currSelectedItem
    subList:SelectItem(itemToSelect, select_useOnGainFocus)
    local hide = false
    local clearButtons = false
    local allowLoseFocus = false
    subList:Deactivate(hide, clearButtons, allowLoseFocus)
  else
    local activate_showList = true
    local activate_useOnGainFocus = false
    subList:Activate(activate_showList, activate_useOnGainFocus)
    local shouldSelectEquippedItem = selectEquippedItem == true and equippedItem ~= nil
    local itemToSelect = shouldSelectEquippedItem and equippedItem or currSelectedItem
    subList:SelectItem(itemToSelect, select_useOnGainFocus)
  end
end
local sortListFocusabeObjectCount = 9
function Menu:SetupSortList(sortListName, goSortList)
  local listName = self._name .. "_" .. sortListName
  local listObjectName = sortListName
  if goSortList ~= nil then
    listObjectName = ""
  end
  local sortList = list.List.New(self._state, {
    Name = listName,
    ListObjectName = listObjectName,
    MaxFocusableObjectCount = sortListFocusabeObjectCount,
    SelectionChangeSound = "SND_UX_Menu_Tick_Horizontal",
    PreviousEvents = {
      "EVT_L2_Press"
    },
    NextEvents = {
      "EVT_R2_Press"
    },
    DelayTime = 0.5,
    Button_OnGainFocus = function(button)
      if self._state.SortList_Button_OnGainFocus ~= nil then
        self._state:SortList_Button_OnGainFocus(button)
      end
    end
  })
  if goSortList ~= nil then
    sortList:SetGameObject(goSortList)
  end
  self:SetList(sortListName, sortList)
  self._sortListName = sortListName
  tutorialUtil.RegisterDesaturationList(listName, sortList)
  sortList:SetSelectedButton(1, false)
end
function Menu:AnimateSortList()
  local sortList = self:GetList(self._sortListName)
  if sortList ~= nil then
    local animRate = 0
    local endPos = 0
    if sortList:GetButtonCount() == sortListFocusabeObjectCount then
      endPos = 1
    end
    UI.Anim(sortList:GetButtonRootGameObject(), consts.AS_Forward, "", animRate, endPos)
  end
end
return {Menu = Menu}
