local classlib = require("core.class")
local tablex = require("core.tablex")
local colors = require("ui.colors")
local consts = require("ui.consts")
local fsm = require("ui.fsm")
local lamsIDs = require("ui.lamsConsts")
local list = require("ui.list")
local menu = require("ui.menu")
local saveUtil = require("ui.saveUtil")
local slider = require("ui.slider")
local sliderHoldFill = require("ui.sliderHoldFill")
local sliderTapFill = require("ui.sliderTapFill")
local toggle = require("ui.toggle")
local util = require("ui.util")
local hooks = require("ui.hooks")
local tutorialUtil = require("ui.tutorialUtil")
local Audio = game.Audio
local Camera = game.Camera
local Combat = game.Combat
local UI = game.UI
local DialogMenu = classlib.Class("DialogMenu", fsm.UIState)
local DialogOff = DialogMenu:StateClass("DialogOff", fsm.UIState)
local DialogOn = DialogMenu:StateClass("DialogOn", fsm.UIState)
local DialogIdle = DialogMenu:StateClass("DialogIdle", fsm.UIState)
local ProgressBar = DialogMenu:StateClass("ProgressBar", fsm.UIState)
local ProgressWait = DialogMenu:StateClass("ProgressWait", fsm.UIState)
local ProgressComplete = DialogMenu:StateClass("ProgressComplete", fsm.UIState)
local ProgressLanguage = DialogMenu:StateClass("ProgressLanguage", fsm.UIState)
local dialogMenu = DialogMenu.New("dialogMenu", {
  DialogOff,
  DialogOn,
  {
    DialogIdle,
    ProgressBar,
    {
      ProgressWait,
      ProgressComplete,
      ProgressLanguage
    }
  }
})
function DialogMenu:Setup()
  self.arguments = {}
  self.settings_menu = true
  self.in_game_pause_on_complete = false
  self.in_game_arguments = nil
  self.currently_in_game_playgo_message = false
  self.progress_complete = false
  self.debug_variable_complete = false
end
function DialogMenu:Enter()
  self:turnoff()
  self:WantPadEvents(true)
end
function DialogMenu:turnoff()
  self:Goto("DialogOff")
end
DialogMenu.EVT_GAME_OVER = DialogMenu.turnoff
DialogMenu.EVT_Restart = DialogMenu.turnoff
function DialogMenu:EVT_R1_Release()
  self.debug_variable_complete = true
end
function DialogMenu:EVT_L1_Release()
  self.debug_variable_complete = false
end
function DialogOff:Setup()
  self.dialogMenu = self:GetState("dialogMenu")
  self.goDialog_name = "SettingsDialog"
  self.goDialog = util.GetUiObjByName(self.goDialog_name)
end
function DialogOff:Enter()
  self.goDialog:Hide()
  self.dialogMenu.arguments = {}
  self.dialogMenu.in_game_pause_on_complete = false
  self.dialogMenu.progress_complete = false
  if not self.dialogMenu.currently_in_game_playgo_message and self.dialogMenu.in_game_arguments ~= nil then
    self.dialogMenu.arguments = self.dialogMenu.in_game_arguments
    self:RunInGamePlaygoProcess()
  elseif self.dialogMenu.currently_in_game_playgo_message then
    self.dialogMenu.in_game_arguments = nil
  end
end
function DialogOff:EVT_SHOW_SETTINGS_DIALOG_MESSAGE(args, settingsMenu)
  self.dialogMenu.settings_menu = true
  if settingsMenu ~= nil then
    self.dialogMenu.settings_menu = settingsMenu
  end
  self.dialogMenu.arguments = args
  self.dialogMenu.currently_in_game_playgo_message = false
  self:Goto("DialogOn")
end
function DialogOff:EVT_SHELL_CHECK_CONTINUE_GAME(latestSlot)
  local game_ready = UI.IsSlotPlayGoDownloaded(latestSlot)
  if game_ready then
    UI.SendEvent("*", consts.EVT_CONTINUE_GAME_READY)
  else
    self.dialogMenu.settings_menu = false
    self.dialogMenu.arguments = {
      Header = util.GetLAMSMsg(lamsIDs.DownloadInProgress),
      Events = {
        Back = {
          Text = util.GetLAMSMsg(lamsIDs.Back),
          Handler = function()
            self:Shell_UserBackedOut()
          end
        }
      },
      Progress = {
        Get = function()
          return UI.GetSlotPlayGoDownloadProgress(latestSlot)
        end,
        Complete = function()
          self:CompletedDownload()
        end,
        IsComplete = function()
          return UI.IsSlotPlayGoDownloaded(latestSlot)
        end
      }
    }
    self:Goto("DialogOn")
  end
end
function DialogOff:Shell_UserBackedOut()
  UI.SendEvent("*", consts.EVT_USER_BACKED_OUT_OF_CONTINUE_DIALOG)
end
function DialogOff:CompletedDownload()
  self:DebugPrint("COMPLETED DOWNLOAD SEND TO SMI")
  UI.SendEvent("*", consts.EVT_CONTINUE_GAME_READY)
end
function DialogOff:EVT_SHELL_START_IMPOSSIBLE_CONFIRM()
  self.dialogMenu.settings_menu = false
  local body_text = util.GetLAMSMsg(lamsIDs.ImpossibleDescNote) .. "\n" .. util.GetLAMSMsg(lamsIDs.AreYouSure)
  self.dialogMenu.arguments = {
    Header = util.GetLAMSMsg(lamsIDs.ImpossibleFlavor),
    Body = body_text,
    Events = {
      Advance = {
        Text = util.GetLAMSMsg(lamsIDs.Yes),
        Handler = function()
          self:Shell_UserConfirmingImpossible()
        end
      },
      Back = {
        Text = util.GetLAMSMsg(lamsIDs.No),
        Handler = function()
          self:Shell_UserBackedOutImpossible()
        end
      }
    },
    Delay = 0.75
  }
  self:Goto("DialogOn")
end
function DialogOff:Shell_UserBackedOutImpossible()
  UI.SendEvent("*", consts.EVT_USER_BACKED_OUT_OF_IMPOSSIBLE_CONFIRM)
end
function DialogOff:Shell_UserConfirmingImpossible()
  UI.SendEvent("*", consts.EVT_USER_IMPOSSIBLE_CONFIRMED)
end
function DialogOff:EVT_PAD_UNPLUGGED()
  tutorialUtil.SetControllerUnplugged(true)
  self.dialogMenu.settings_menu = true
  self.dialogMenu.arguments = {
    Header = util.GetLAMSMsg(lamsIDs.DeviceDisconnectedHeader),
    Body = util.GetLAMSMsg(lamsIDs.DeviceDisconnectedBody),
    Events = {
      Advance = {
        Text = util.GetLAMSMsg(lamsIDs.Continue),
        Handler = function()
          self:Shell_UserConfirmedUnpluggedWarning()
        end
      }
    },
    Delay = 0.75
  }
  self:Goto("DialogOn")
end
function DialogOff:Shell_UserConfirmedUnpluggedWarning()
  tutorialUtil.SetControllerUnplugged(false)
end
function DialogOff:EVT_NEW_GAME_WARNING_FROM_SHELL()
  self.dialogMenu.settings_menu = false
  self.dialogMenu.arguments = {
    Header = util.GetLAMSMsg(lamsIDs.StartingNewGame),
    Body = util.GetLAMSMsg(lamsIDs.ManuallySaveWarning),
    Events = {
      Advance = {
        Text = util.GetLAMSMsg(lamsIDs.Confirm),
        Handler = function()
          self:Shell_UserConfirmedSaveWarning()
        end
      },
      Back = {
        Text = util.GetLAMSMsg(lamsIDs.Back),
        Handler = function()
          self:Shell_UserBackedOutOfSaveWarning()
        end
      }
    },
    Delay = 0.75
  }
  self:Goto("DialogOn")
end
function DialogOff:Shell_UserConfirmedSaveWarning()
  UI.SendEvent("*", consts.EVT_USER_CONFIRMED_WARNING_FOR_NEW_GAME)
end
function DialogOff:Shell_UserBackedOutOfSaveWarning()
  UI.SendEvent("*", consts.EVT_USER_BACKED_OUT_OF_WARNING_FOR_NEW_GAME)
end
function DialogOff:EVT_IN_GAME_PLAYGO_PROGESS(playgo, obj)
  self.dialogMenu.arguments = {
    Header = util.GetLAMSMsg(lamsIDs.DownloadInProgress),
    Events = {
      Back = {
        Text = util.GetLAMSMsg(lamsIDs.Back),
        Handler = function()
          if obj ~= nil then
            obj:CallScript("PlaygoDialogBackOut")
          end
        end
      }
    },
    Progress = {
      Get = function()
        return UI.GetPlayGoDownloadProgress(playgo)
      end,
      Complete = function()
        if obj ~= nil then
          obj:CallScript("PlaygoDownloadComplete")
        end
      end,
      IsComplete = function()
        return UI.IsPlayGoDownloaded(playgo)
      end
    }
  }
  self.dialogMenu.in_game_arguments = self.dialogMenu.arguments
  self:RunInGamePlaygoProcess()
end
function DialogOff:RunInGamePlaygoProcess()
  self.dialogMenu.in_game_pause_on_complete = true
  self.dialogMenu.settings_menu = false
  self.dialogMenu.currently_in_game_playgo_message = true
  self:Goto("DialogOn")
end
function DialogOn:Setup()
  self._hookTokens = {}
  self._state = self
  self.dialogMenu = self:GetState("dialogMenu")
  self.goDialog_name = "SettingsDialog"
  self.goDialog = util.GetUiObjByName(self.goDialog_name)
  self._goMessageChild = self.goDialog:FindSingleGOByName("message")
  self._goHousing = self.goDialog:FindSingleGOByName("Housing")
  self._goHeaderHousing = self.goDialog:FindSingleGOByName("HeaderHousing")
  self._goTextObjects = self.goDialog:FindSingleGOByName("TextObjects")
  self.th_dialogHeader = util.GetTextHandle(self._goTextObjects, "Header")
  self.th_dialogBody = util.GetTextHandle(self._goTextObjects, "Body")
  self.th_dialogButtons = util.GetTextHandle(self._goTextObjects, "ButtonPrompts")
  self.th_ProgressButtons = util.GetTextHandle(self._goTextObjects, "ProgressButtons")
  UI.SetTextIsClickable(self.th_dialogButtons)
  UI.SetTextIsDialog(self.th_dialogButtons)
  self:ClearDialog()
end
function DialogOn:Enter()
  if self.dialogMenu.currently_in_game_playgo_message then
    UI.GamePause()
    self:SendEventToUIFsm("globalMenu", "EVT_DISABLE_PAUSE_MENU")
  end
  Audio.PlaySound("SND_UX_Warning_Prompt_Enter")
  self.args = self.dialogMenu.arguments
  self:ClearDialog()
  self:MoveDialogBox()
  self:SetupDialog()
  self:DisableLists()
  self:ShowGOs()
  self:WantPadEvents(true)
  UI.SetInUI(true)
  if self.args.Progress == nil then
    self:Goto("DialogIdle")
  else
    self:Goto("ProgressBar")
  end
end
function DialogOn:Exit()
  UI.SetInUI(false)
  Audio.PlaySound("SND_UX_Warning_Prompt_Exit")
  if self.args.OnDialogExit ~= nil then
    self.args.OnDialogExit()
  end
  self:WantPadEvents(false)
  self._goHousing:Hide()
  self._goHeaderHousing:Hide()
  self.goDialog:Hide()
  if self.dialogMenu.currently_in_game_playgo_message then
    UI.GameUnpause()
    self:SendEventToUIFsm("globalMenu", "EVT_ENABLE_PAUSE_MENU")
  end
end
function DialogOn:StartExit()
  self:StartTimer("HoldExit", 0.1, function()
    self:Goto("DialogOff")
  end)
end
function DialogOn:ShowGOs()
  self.goDialog:Show()
  self._goMessageChild:Show()
  self._goTextObjects:Show()
end
function DialogOn:ClearDialog()
  UI.SetText(self.th_dialogHeader, "")
  UI.SetText(self.th_dialogBody, "")
  UI.SetText(self.th_dialogButtons, "")
  UI.SetText(self.th_ProgressButtons, "")
end
function DialogOn:SetupDialog()
  if self.args.Header ~= nil then
    self:SetHeaderText(self.args.Header)
    self._goHeaderHousing:Show()
  else
    self._goHeaderHousing:Hide()
  end
  if self.args.Body ~= nil and not self.args.Progress then
    UI.SetText(self.th_dialogBody, self.args.Body)
  end
end
function DialogOn:SetHeaderText(text)
  UI.SetText(self.th_dialogHeader, text)
end
function DialogOn:SetupFooterButtonText(button_text_list, progress)
  local button_text = ""
  if 0 < #button_text_list then
    for i, text in ipairs(button_text_list) do
      if i == 1 then
        button_text = text
      else
        button_text = button_text .. consts.Footer_Spacer .. text
      end
    end
  end
  if progress then
    UI.SetText(self.th_ProgressButtons, button_text)
    UI.SetText(self.th_dialogButtons, "")
  else
    UI.SetText(self.th_ProgressButtons, "")
    UI.SetText(self.th_dialogButtons, button_text)
  end
end
function DialogOn:DisableLists()
  if self.args.ActiveMenus ~= nil then
    for _, menu in ipairs(self.args.ActiveMenus) do
      menu:Deactivate(true, false)
    end
  end
  if self.args.ActiveLists ~= nil then
    for _, list in ipairs(self.args.ActiveLists) do
      list:Deactivate(false)
    end
  end
end
function DialogOn:MoveDialogBox(x, y, z)
  local positionTarget = engine.Vector.New(0, -40.222, 0)
  if not self.dialogMenu.settings_menu then
    positionTarget = engine.Vector.New(0, 0, 0)
  end
  if self.args.Language then
    positionTarget = engine.Vector.New(0, -37.222, 0)
  end
  UI.SetGOTransformInterpolated(self.goDialog, positionTarget, 0, true)
end
function DialogIdle:Setup()
  self.dialogMenu = self:GetState("dialogMenu")
  self.dialogOn = self:GetState("DialogOn")
  self.args = nil
  self.back_event = false
  self.advance_event = false
  self.leaving = false
  self._hookedFooterButtons = false
  self.goDialog_name = "SettingsDialog"
  self.goDialog = util.GetUiObjByName(self.goDialog_name)
  self._goHousing = self.goDialog:FindSingleGOByName("Housing")
  self._goDialogTransBG = self.goDialog:FindSingleGOByName("DialogTransBG")
  self._goTextObjects = self.goDialog:FindSingleGOByName("TextObjects")
  self.th_dialogBody = util.GetTextHandle(self._goTextObjects, "Body")
end
function DialogIdle:Enter()
  self.back_event = false
  self.advance_event = false
  self.leaving = false
  self.args = self.dialogMenu.arguments
  self._hookedFooterButtons = false
  self.arg_events = self.args.Events
  self._back = nil
  self._advance = nil
  if self.arg_events ~= nil then
    self._back = self.arg_events.Back
    self._advance = self.arg_events.Advance
  end
  UI.Anim(self._goHousing, consts.AS_Forward, "", 0, 0)
  self._goDialogTransBG:Show()
  local delay = self.args.Delay
  if delay == nil then
    delay = 0.25
  end
  if self.args.PhotoMode then
    self:SetupFooterButtons()
  else
    self:StartTimer("DelayButtons", delay, function()
      self:SetupFooterButtons()
    end)
  end
  self.arg_timer = self.args.Timer
  self.timerBody = nil
  if self.arg_timer ~= nil then
    self.timerBody = self.arg_timer.Body
    local time = self.arg_timer.Time
    if time == nil then
      time = 15
    end
    self:StartTimer("BackTimer", time, function()
      self:EVT_Back_Release()
    end)
  end
end
function DialogIdle:Exit()
  self._goDialogTransBG:Hide()
end
function DialogIdle:EVT_FORCE_EXIT_DIALOG()
  self.leaving = true
  self:Goto("DialogOff")
end
function DialogIdle:Update()
  if not self.leaving then
    if self.back_event then
      self.leaving = true
      self.back_event = false
      self.advance_event = false
      if self._back.Handler ~= nil then
        self._back.Handler()
      end
      self:Goto("DialogOff")
    elseif self.advance_event then
      self.leaving = true
      self.back_event = false
      self.advance_event = false
      self._advance.Handler()
      self:Goto("DialogOff")
    elseif self.timerBody ~= nil then
      local message = string.format(self.timerBody, self:GetTimerRemainingTime("BackTimer") + 1)
      UI.SetText(self.th_dialogBody, message)
    end
  end
end
function DialogIdle:SetupFooterButtons()
  local t = {}
  if self._advance ~= nil and self._advance.Text ~= nil then
    table.insert(t, "[AdvanceButton] " .. self._advance.Text)
  end
  if self._back ~= nil and self._back.Text ~= nil then
    table.insert(t, "[BackButton] " .. self._back.Text)
  end
  self.dialogOn:SetupFooterButtonText(t, false)
  self._hookedFooterButtons = true
end
function DialogIdle:EVT_Back_Release()
  if self._back ~= nil and self._hookedFooterButtons and not self.leaving then
    self.back_event = true
  end
end
function DialogIdle:EVT_Advance_Release()
  if self._advance ~= nil and self._hookedFooterButtons and not self.leaving and self._advance.Handler ~= nil then
    self.advance_event = true
  end
end
function ProgressBar:Setup()
  self.dialogMenu = self:GetState("dialogMenu")
  self.dialogOn = self:GetState("DialogOn")
  self.progressLanguage = self:GetState("ProgressLanguage")
  self.prog_args = nil
  self.dialogMenu.progress_complete = false
  self._get = nil
  self._complete = nil
  self.advance_handler_pressed = false
  self._goProgressBar = self.dialogOn._goMessageChild:FindSingleGOByName("ProgressBar")
  self.th_Progress = util.GetTextHandle(self._goProgressBar, "Progress")
  self.th_Status = util.GetTextHandle(self._goProgressBar, "Status")
  self._goHousing = self.dialogOn.goDialog:FindSingleGOByName("Housing")
end
function ProgressBar:Enter()
  self.advance_handler_pressed = false
  self.dialogMenu.progress_complete = false
  self.args = self.dialogMenu.arguments
  self:ClearText()
  self._goProgressBar:Show()
  if self.args.Language then
    self._goHousing:Hide()
    self:Goto("ProgressLanguage")
  else
    UI.Anim(self._goHousing, consts.AS_Forward, "", 0, 1)
    self:Goto("ProgressWait")
  end
  self.prog_args = self.args.Progress
  self._get = self.prog_args.Get
  self._complete = self.prog_args.Complete
  self._isComplete = self.prog_args.IsComplete
  self:SetStatus()
end
function ProgressBar:Exit()
  self._goProgressBar:Hide()
end
function ProgressBar:EVT_IN_GAME_TURNOFF_PLAYGO_PROGRESS()
  self:Goto("DialogOff")
end
function ProgressBar:EVT_SHOW_SETTINGS_DIALOG_MESSAGE(args)
  self.dialogMenu.settings_menu = true
  self.dialogMenu.arguments = args
  self.dialogMenu.currently_in_game_playgo_message = false
  self.dialogOn:Enter()
end
function ProgressBar:UpdateAllArguments(args)
  self.dialogMenu.arguments = args
  self.args = self.dialogMenu.arguments
  self.prog_args = self.args.Progress
  self._get = self.prog_args.Get
  self._complete = self.prog_args.Complete
  self._isComplete = self.prog_args.IsComplete
end
function ProgressBar:ClearText()
  UI.SetText(self.th_Progress, "")
  UI.SetText(self.th_Status, "")
end
function ProgressBar:CheckComplete()
  if self._isComplete ~= nil then
    return self._isComplete()
  end
end
function ProgressBar:SetStatus(complete)
  local status = self.args.Status
  if complete or status == nil then
    UI.SetText(self.th_Status, "")
  else
    UI.SetText(self.th_Status, status)
  end
end
function ProgressBar:SetProgressValues()
  if self._get ~= nil then
    local curr, max, seconds = self._get()
    self:SetStatus()
    UI.Anim(self._goProgressBar, consts.AS_Forward, "", 0, curr / max)
    local smart_num = self:FormatBytes(curr) .. " / " .. self:FormatBytes(max)
    local second_string = self:GetSecondsString(seconds)
    if second_string ~= nil then
      smart_num = smart_num .. " " .. second_string
    end
    UI.SetText(self.th_Progress, smart_num)
  end
end
function ProgressBar:GetSecondsString(seconds)
  if 0 < seconds and seconds ~= nil then
    local text = ""
    if seconds == 1 then
      text = "1 " .. util.GetLAMSMsg(lamsIDs.SecondLeft)
    elseif seconds < 120 then
      text = tostring(seconds) .. " " .. util.GetLAMSMsg(lamsIDs.SecondsLeft)
    elseif seconds < 7200 then
      text = tostring(math.floor(seconds / 60)) .. " " .. util.GetLAMSMsg(lamsIDs.MinutesLeft)
    else
      text = tostring(math.floor(seconds / 3600)) .. " " .. util.GetLAMSMsg(lamsIDs.HoursLeft)
    end
    return "(" .. text .. ")"
  end
end
function ProgressBar:FormatBytes(bytes)
  local k = 1024
  local gb = k ^ 3
  if bytes < gb then
    local num = bytes / k ^ 2
    return string.format("%.2f", num) .. " MB"
  else
    local num = bytes / k ^ 3
    return string.format("%.2f", num) .. " GB"
  end
end
function ProgressWait:Setup()
  self.dialogMenu = self:GetState("dialogMenu")
  self.progressBar = self:GetState("ProgressBar")
  self.dialogOn = self:GetState("DialogOn")
end
function ProgressWait:Enter()
  self.args = self.dialogMenu.arguments
  self.arg_events = self.args.Events
  self._back = nil
  if self.arg_events ~= nil then
    self._back = self.arg_events.Back
  end
  self:WantPadEvents(true)
  self:SetupFooterButtons()
  self.dialogOn:SetHeaderText(self.args.Header)
end
function ProgressWait:SetupFooterButtons()
  local t = {}
  if self._back ~= nil and self._back.Text ~= nil then
    table.insert(t, "[BackButton] " .. self._back.Text)
  end
  self.dialogOn:SetupFooterButtonText(t, true)
end
function ProgressWait:EVT_Back_Release()
  if self._back ~= nil then
    if self._back.Handler ~= nil then
      self._back.Handler()
    end
    self:StartExit()
  end
end
function ProgressWait:StartExit()
  self:StartTimer("HoldExit", 0.1, function()
    self:Goto("DialogOff")
  end)
end
function ProgressWait:Update()
  local is_completed = self.progressBar:CheckComplete()
  if is_completed then
    self:Goto("ProgressComplete")
  else
    self.progressBar:SetProgressValues()
  end
end
function ProgressComplete:Setup()
  self.dialogMenu = self:GetState("dialogMenu")
  self.progressBar = self:GetState("ProgressBar")
  self.dialogOn = self:GetState("DialogOn")
end
function ProgressComplete:Enter()
  self:WantPadEvents(true)
  self.args = self.dialogMenu.arguments
  self.prog_args = self.args.Progress
  self._complete = self.prog_args.Complete
  self:SetupFooterButtons()
  self.dialogOn:SetHeaderText(util.GetLAMSMsg(lamsIDs.DownloadComplete))
end
function ProgressComplete:Exit()
end
function ProgressComplete:SetupFooterButtons()
  local t = {}
  if self._complete ~= nil then
    table.insert(t, "[AdvanceButton] " .. util.GetLAMSMsg(lamsIDs.Continue))
  end
  self.dialogOn:SetupFooterButtonText(t, true)
end
function ProgressComplete:Update()
  local is_completed = self.progressBar:CheckComplete()
  if not is_completed then
    self:Goto("ProgressWait")
  end
end
function ProgressComplete:EVT_Advance_Release()
  if self._complete ~= nil then
    self._complete()
    self:StartExit()
  end
end
function ProgressComplete:StartExit()
  self:StartTimer("HoldExit", 0.1, function()
    self:Goto("DialogOff")
  end)
end
function ProgressLanguage:Setup()
  self.dialogMenu = self:GetState("dialogMenu")
  self.progressBar = self:GetState("ProgressBar")
  self.dialogOn = self:GetState("DialogOn")
end
function ProgressLanguage:Enter()
end
function ProgressLanguage:Update()
  local completed = self.progressBar:CheckComplete()
  if not completed then
    self.progressBar:SetProgressValues()
  end
end
function ProgressLanguage:EVT_SHOW_SETTINGS_DIALOG_MESSAGE(args)
  self.progressBar:UpdateAllArguments(args)
end
