local classlib = require("core.class")
local tablex = require("core.tablex")
local animationUtil = require("ui.animationUtil")
local colors = require("ui.colors")
local consts = require("ui.consts")
local fsm = require("ui.fsm")
local iconConsts = require("ui.iconConsts")
local lamsIDs = require("ui.lamsConsts")
local list = require("ui.list")
local menu = require("ui.menu")
local pickupConsts = require("ui.pickupConsts")
local pickupUtil = require("ui.pickupUtil")
local recipeUtil = require("ui.recipeUtil")
local resourceConsts = require("ui.resourceConsts")
local resourceUtil = require("ui.resourceUtil")
local skillInfoCard = require("ui.skillInfoCard")
local skillTreeConsts = require("ui.skillTreeConsts")
local skillUtil = require("ui.skillUtil")
local sliderHoldFill = require("ui.sliderHoldFill")
local util = require("ui.util")
local AI = game.AI
local Camera = game.Camera
local Player = game.Player
local UI = game.UI
local Audio = game.Audio
local Map = game.Map
local skillTree_camera = tweaks.tMapCamera.New({
  OnSkillTree = true,
  Name = "SkillTreeMenuCam",
  CollisionRoot = "Collision",
  HorizontalPosition = 0.97,
  VerticalPosition = 0.2,
  ZoomPosition = 6,
  HorizontalControlSpeed = 0,
  VerticalControlSpeed = 0,
  ZoomControlSpeed = 2.2,
  ZoomSpeedScale = 3,
  Pitch = 0,
  Yaw = 0,
  Roll = 0,
  MaxLeft = -2,
  MaxRight = 2.2,
  MaxUp = -0.5,
  MaxDown = 1.3,
  MaxIn = 6,
  MaxOut = 6,
  CursorPosition_X = 0,
  CursorPosition_Y = 0,
  CursorPosition_H_Min = -2,
  CursorPosition_H_Max = 2,
  CursorPosition_V_Min = -0.5,
  CursorPosition_V_Max = 1.5,
  CursorSpeed = 2.2,
  CursorSnap_Enabled = 1,
  CursorSnap_Strength = 15,
  CursorSnap_PredictionStrength = 0.45,
  DetachCursorFromCamera = true,
  CursorGOName = "SkillTreeWorldCursor",
  Tofu = "never"
})
local SkillTreeMenu = classlib.Class("SkillTreeMenu", fsm.UIState)
local SkillTreeOff = SkillTreeMenu:StateClass("SkillTreeOff", fsm.UIState)
local SkillTreeOn = SkillTreeMenu:StateClass("SkillTreeOn", fsm.UIState)
local skillTreeMenu = SkillTreeMenu.New("skillTreeMenu", {SkillTreeOff, SkillTreeOn})
function SkillTreeMenu:Setup()
  self.pauseMenuOpened = false
end
function SkillTreeMenu:Enter()
  self:WantPadEvents(true)
  self:TurnOff()
end
function SkillTreeMenu:TurnOff()
  self:Goto("SkillTreeOff")
end
function SkillTreeMenu:EVT_PAUSE_MENU_OPEN()
  self.pauseMenuOpened = true
end
SkillTreeMenu.EVT_GAME_OVER = SkillTreeMenu.TurnOff
SkillTreeMenu.EVT_Restart = SkillTreeMenu.TurnOff
function SkillTreeOff:Setup()
  self.skillTreeOn = self:GetState("SkillTreeOn")
end
function SkillTreeOff:Enter()
end
function SkillTreeOff:Exit()
end
function SkillTreeOff:EVT_TURN_ON_SKILL_TREE_MENU(instructionEntries, instructionArgs)
  self.skillTreeOn.menu:set_instructionEntries(instructionEntries)
  self.skillTreeOn.menu:set_instructionArgs(instructionArgs)
  self:Goto("SkillTreeOn")
end
function SkillTreeOn:Setup()
  self.menu = menu.Menu.New(self, {
    FooterButtonInfo = {
      {
        Item = "Move",
        Text = "[JoystickL] " .. util.GetLAMSMsg(lamsIDs.Move)
      },
      {
        Item = "Map",
        Text = "[TouchPad] " .. util.GetLAMSMsg(lamsIDs.Map),
        EventHandlers = {
          {
            Events = {
              "EVT_TouchPad_Release"
            },
            Handler = function()
              self:SendEventToUIFsm("globalMenu", "EVT_GO_TO_MAP")
            end
          }
        }
      },
      {
        Item = "Weapon",
        Text = "",
        EventHandlers = {
          {
            Events = {
              "EVT_GO_TO_WEAPON_MENU"
            },
            Handler = function()
              self:SendEventToUIFsm("globalMenu", "EVT_GO_TO_WEAPON")
            end
          }
        }
      },
      {
        Item = "Quest",
        Text = "",
        EventHandlers = {
          {
            Events = {
              "EVT_GO_TO_QUEST_MENU"
            },
            Handler = function()
              self:SendEventToUIFsm("globalMenu", "EVT_GO_TO_QUEST")
            end
          }
        }
      },
      {
        Item = "Settings",
        Text = "[TriangleButton] " .. util.GetLAMSMsg(lamsIDs.Options),
        EventHandlers = {
          {
            Events = {
              "EVT_Triangle_Release"
            },
            Handler = function()
              self:Menu_Triangle_ReleaseHandler()
            end
          }
        }
      },
      {
        Item = "Exit",
        Text = "[BackButton] " .. util.GetLAMSMsg(lamsIDs.Exit),
        EventHandlers = {
          {
            Events = {
              "EVT_Back_Release"
            },
            Handler = function()
              self:Menu_Back_ReleaseHandler()
            end
          }
        }
      },
      {
        Item = "Close",
        Text = "",
        EventHandlers = {
          {
            Events = {
              "EVT_Options_Release",
              "EVT_GO_TO_SKILL_TREE_MENU"
            },
            Handler = function()
              self:Menu_Options_ReleaseHandler()
            end
          }
        }
      }
    }
  })
  self.skillTreeMenu = self:GetState("skillTreeMenu")
  self.menu:SetupSubmenuList(consts.inworldMenu_SubmenuList, {
    "EVT_Left_Release"
  }, {
    "EVT_Right_Release"
  })
  local nodeList = list.List.New(self, {
    MaxFocusableObjectCount = 1,
    EmptyTextLamsID = lamsIDs.NoSkillsAvailable,
    NextEvents = {},
    PreviousEvents = {},
    Button_OnGainFocus = function(button)
      local currSkillRecipe = button:get_item()
      self:Node_Button_OnGainFocus(currSkillRecipe)
    end,
    Button_OnLoseFocus = function(button)
      local currSkillRecipe = button:get_item()
      self:Node_Button_OnLoseFocus(currSkillRecipe)
    end,
    Button_HighlightOn = function(button, animateImmediately)
      self:Node_Button_HighlightOn(button, animateImmediately)
    end,
    Button_HighlightOff = function(button, animateImmediately)
      self:Node_Button_HighlightOff(button, animateImmediately)
    end,
    Button_ItemCompare = function(item, otherItem)
      return self:Node_Button_ItemCompare(item, otherItem)
    end
  })
  self.menu:SetList("nodeList", nodeList)
  local skillInfoCardGO = util.GetUiObjByName("SkillInfoCard")
  self._skillInfoCard = skillInfoCard.SkillInfoCard.New(self, skillInfoCardGO)
  self._skillInfoCard:Init()
  local craftGO = util.GetUiObjByName("SkillTree_HoldToCraft")
  local craftText = util.GetTextHandle(craftGO)
  UI.SetTextIsClickable(craftText)
  self.holdFill = sliderHoldFill.SliderHoldFill.New(self, {
    Name = "SkillTree_HoldToCraftMeter",
    SliderObject = util.GetUiObjByName("SkillTree_HoldToCraftMeter"),
    HoldSound = "SND_UX_Pause_Menu_Screen_Skills_Purchase_Hold",
    SuccessSound = "SND_UX_Pause_Menu_Screen_Skills_Purchase_Success",
    HoldStartSound = "SND_UX_Vendor_Upgrade_Hold_Button_Start",
    StopSound = "SND_UX_Vendor_Upgrade_Hold_Reset_LP",
    PressEvent = "EVT_Square_Press",
    ReleaseEvent = "EVT_Square_Release",
    StopWhenFull = true,
    IncAnimRate = 0.5,
    DecAnimRate = 0.4
  })
  self:InitGameObjects()
end
function SkillTreeOn:InitGameObjects()
  self.gameObjects = {}
  local goScene = util.GetUiObjByName("skill_tree_scene")
  local goVisualsGroup = goScene:FindSingleGOByName("visuals")
  self.gameObjects.goVisualsGroup = goVisualsGroup
  self.gameObjects.goAxeVis = goVisualsGroup:FindSingleGOByName("axe")
  self.gameObjects.goBladesVis = goVisualsGroup:FindSingleGOByName("blades")
  self.gameObjects.goBowVis = goVisualsGroup:FindSingleGOByName("bow")
  self.gameObjects.goShieldVis = goVisualsGroup:FindSingleGOByName("shield")
  local goAxeGeoRefnode = self.gameObjects.goAxeVis:FindSingleGOByName("AxeGeo")
  local goAxeGeoChild = goAxeGeoRefnode:FindSingleGOByName("Root")
  local goBladesGeoRefnode = self.gameObjects.goBladesVis:FindSingleGOByName("BladesGeo")
  local goBladesGeoChild = goBladesGeoRefnode:FindSingleGOByName("Root")
  local goBowGeoRefnode = self.gameObjects.goBowVis:FindSingleGOByName("BowGeo")
  local goBowGeoChild = goBowGeoRefnode:FindSingleGOByName("Root")
  self.gameObjects.goHighlighter = self.gameObjects.goVisualsGroup:FindSingleGOByName("Highlighter")
  self._goCursorRefnode = util.GetUiObjByName("SkillTreeWorldCursor")
  local animRate = 0.5
  UI.Anim(self.gameObjects.goHighlighter, consts.AS_ForwardCycle, "", animRate)
  goAxeGeoRefnode:Show()
  goAxeGeoChild:Show()
  goBladesGeoRefnode:Show()
  goBladesGeoChild:Show()
  goBowGeoRefnode:Show()
  goBowGeoChild:Show()
  self.gameObjects.goHighlighter:Show()
  util.Hide("skill_tree_scene")
  self.gameObjects.goVisualsGroup:Hide()
  self.gameObjects.goCollisionGroup_Axe = goScene:FindSingleGOByName("SkillTreeCollision_Axe")
  self.gameObjects.goCollisionGroup_Blades = goScene:FindSingleGOByName("SkillTreeCollision_Blades")
  self.gameObjects.goCollisionGroup_Bow = goScene:FindSingleGOByName("SkillTreeCollision_Bow")
  self.gameObjects.goCollisionGroup_Shield = goScene:FindSingleGOByName("SkillTreeCollision_Shield")
  self.gameObjects.goCollisionGroup_Axe:Show()
  self.gameObjects.goCollisionGroup_Blades:Show()
  self.gameObjects.goCollisionGroup_Bow:Show()
  self.gameObjects.goCollisionGroup_Shield:Show()
  self:InitializeSkillTreeBranches()
end
function SkillTreeOn:InitializeSkillTreeBranches()
  local goSkillTreeVis = self.gameObjects.goVisualsGroup
  self.branchGameObjects = {}
  for branchName, skillName in pairs(skillTreeConsts.BranchGONames) do
    local goBranch = goSkillTreeVis:FindSingleGOByName(branchName)
    goBranch:Show()
    goBranch.Child:Show()
    self.branchGameObjects[skillName] = goBranch
  end
end
function SkillTreeOn:Enter()
  self.menu:Activate()
  util.Show("SkillTree")
  self._skillInfoCard:ShowCard(false)
  self:UpdateSkillMenuNotifications()
  self.currentCharacter = self.currentCharacter ~= nil and self.currentCharacter or pickupConsts.TAG_PICKUP_KRATOS
  self.holdFill:SetOnComplete(function()
    self:UnlockSkill()
  end)
  self.shared.actionText = util.GetLAMSMsg(lamsIDs.HoldToUnlockSkill)
  util.Show("skilltreescene")
  self.goSkillTreeScene = util.GetUiObjByName("skill_tree_scene")
  self.goSkillTreeScene:Show()
  local goSkillTreeCursorChild = self._goCursorRefnode.Child
  self._goCursorRefnode:Show()
  goSkillTreeCursorChild:Show()
  util.Show(skillTree_camera.CursorGOName)
  local submenuList = self.menu:GetList(consts.inworldMenu_SubmenuList)
  if self.skillTreeMenu.pauseMenuOpened == true then
    local init_useOnGainFocus = false
    submenuList:SetSelectedButton(1, init_useOnGainFocus)
    self.skillTreeMenu.pauseMenuOpened = false
  end
  local newItemArray = skillUtil.GetSubStateNames()
  local showList = true
  local useOnGainFocus = not self.menu:HasInstructionEntryForMenuState()
  local itemDetermineFocusabilityFunc = function(substateName)
    return self:IsSubstateFocusable(substateName)
  end
  local getDisplayNameFunc = function(itemName)
    if itemName == pickupConsts.Shield then
      return util.GetLAMSMsg(lamsIDs.Shield)
    else
      return pickupUtil.GetDisplayName(itemName)
    end
  end
  local skipListCentering = true
  self.menu:RefreshSubmenuList(submenuList, newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, getDisplayNameFunc, skipListCentering)
  self:SetupCameraForSkillTree()
  UI.WorldUIRender(skillTree_camera.Name)
  self:SendEventToUIFsm("inWorldMenu", "EVT_REFRESH_NOTIFICATIONS")
  self.menu:ExecuteInstructions()
end
function SkillTreeOn:Exit()
  self.menu:Deactivate(true)
  util.Hide("SkillTree", "SkillTree_HoldToCraft")
  for _, pickupName in ipairs(skillUtil.GetSubStateNames()) do
    util.Hide(pickupName .. "_SkillTree")
  end
  self._skillInfoCard:HideCard()
  self.gameObjects.goVisualsGroup:Hide()
  self.holdFill:Deactivate()
  self.holdFill:OnSliderParentHide()
  if tweaks.tMapCamera then
    Camera.SetMapCamera()
  end
  self._goCursorRefnode:Hide()
  animationUtil.ClearVideo()
end
function SkillTreeOn:IsSubstateFocusable(substateName)
  local isBladesSelection = substateName == pickupConsts.Blades
  local hasBlades = resourceUtil.HasResource(pickupConsts.Blades)
  local isSonSelection = substateName == pickupConsts.SonBow
  return util.IsMenuSelectionFocusable(isBladesSelection, hasBlades, isSonSelection)
end
function SkillTreeOn:SubmenuList_Button_Update(button)
  local isFocusable = button:get_focusable()
  local alphaValue = isFocusable and consts.FOCUSABLE_ALPHA or consts.NON_FOCUSABLE_ALPHA
  local fadeTime = 0
  button:AlphaFade(alphaValue, fadeTime)
  button:SetIcon(resourceUtil.GetMaterialSwapName(button:get_item()))
  button:UpdateNewIcon(function(button)
    local currItem = button:GetSortItem()
    return skillUtil.AnyNewNodesByWeapon(currItem)
  end)
end
function SkillTreeOn:SubmenuList_Button_OnGainFocus(button)
  self.holdFill:Reset()
  self.holdFill:Deactivate()
  self.gameObjects.goHighlighter:Hide()
  local currentItem = button:get_item()
  self.menu:SetSubmenuListLabelText(consts.inworldMenu_SubmenuList, pickupUtil.GetDisplayName(currentItem))
  local ghostObjName = "bow"
  if currentItem ~= pickupConsts.SonBow then
    ghostObjName = currentItem
    self.currentCharacter = pickupConsts.TAG_PICKUP_KRATOS
  else
    self.currentCharacter = pickupConsts.TAG_PICKUP_SON
  end
  local goGhostPickup = self.goSkillTreeScene:FindSingleGOByName(ghostObjName)
  goGhostPickup:Show()
  self:SetVisibilityOfSkillTree(currentItem, true)
  local nodeList = self.menu:GetList("nodeList")
  self:UpdateNodeList(nodeList, currentItem)
  self:UpdateFooterButtonPrompt()
  self:UpdateTextLabels(currentItem)
  if Map.InstantlySnapToNextCursorTarget then
    Map.InstantlySnapToNextCursorTarget()
  end
end
function SkillTreeOn:SubmenuList_Button_OnLoseFocus(button)
  self:UpdateNewIcon(self.lastSelectedRecipeName, button)
end
function SkillTreeOn:UpdateTextLabels(pickupId)
  assert(pickupId ~= nil, "No pickupId passed into SkillTreeOn:UpdateTextLabels.")
  local textTable = skillTreeConsts.subtreeTextTables[pickupId]
  self:SetupTextLabel(textTable, "Left")
  self:SetupTextLabel(textTable, "Right")
end
function SkillTreeOn:SetupTextLabel(textTable, leftOrRight)
  assert(skillTreeConsts.subtreeTextTables_Validate(textTable), "Invalid table passed into SkillTreeOn:SetupTextLabel.")
  assert(leftOrRight == "Left" or leftOrRight == "Right", "Must pass in 'Left' or 'Right' into SkillTreeOn:SetupTextLabel.")
  local baseName = "textField_"
  local currentTextPair = textTable[leftOrRight]
  local goCurrent = self.gameObjects.goVisualsGroup:FindSingleGOByName(baseName .. leftOrRight)
  local goRoot = goCurrent.Child
  local thText2 = util.GetTextHandle(goRoot, "textObject")
  goCurrent:Show()
  goRoot:Show()
  UI.SetText(thText2, util.GetLAMSMsg(currentTextPair.Desc))
end
function SkillTreeOn:UpdateSkillMenuNotifications()
  if skillUtil.SkillsAreAvailableForPurchase() then
    self:SetSkillsAvailableForPurchaseVisibility(true)
  else
    self:SetSkillsAvailableForPurchaseVisibility(false)
  end
  self:SendEventToUIFsm("inWorldMenu", "EVT_REFRESH_NOTIFICATIONS")
end
function SkillTreeOn:SetSkillsAvailableForPurchaseVisibility(value)
end
function SkillTreeOn:SetVisibilityOfSkillTree(pickupName, visible)
  local gameObjs = self.gameObjects
  if visible then
    self.gameObjects.goVisualsGroup:Show()
    local args = {
      LocalPosition_Offset = engine.Vector.New(0, 0, 0),
      LocalPosition_Target = engine.Vector.New(0, 0, 0.075),
      InterpolationMode = consts.Interp_Smoothstep
    }
    animationUtil.DoTransitionAnim(self.gameObjects.goVisualsGroup, args)
  else
    self.gameObjects.goVisualsGroup:Hide()
  end
  if pickupName == pickupConsts.Axe then
    gameObjs.goBladesVis:Hide()
    gameObjs.goAxeVis:Show()
    gameObjs.goBowVis:Hide()
    gameObjs.goShieldVis:Hide()
    gameObjs.goCollisionGroup_Axe:Show()
    gameObjs.goCollisionGroup_Blades:Hide()
    gameObjs.goCollisionGroup_Bow:Hide()
    gameObjs.goCollisionGroup_Shield:Hide()
    local goRefnode = gameObjs.goAxeVis.Child
    local goRoot = goRefnode.Child
    local targetTimelinePos = 1
    local animRate = 0.05
    UI.Anim(goRoot, consts.AS_ForwardCycle, "", animRate, targetTimelinePos)
  elseif pickupName == pickupConsts.Blades then
    gameObjs.goAxeVis:Hide()
    gameObjs.goBladesVis:Show()
    gameObjs.goBowVis:Hide()
    gameObjs.goShieldVis:Hide()
    gameObjs.goCollisionGroup_Axe:Hide()
    gameObjs.goCollisionGroup_Blades:Show()
    gameObjs.goCollisionGroup_Bow:Hide()
    gameObjs.goCollisionGroup_Shield:Hide()
    local goRefnode = gameObjs.goBladesVis.Child
    local goRoot = goRefnode.Child
    local targetTimelinePos = 1
    local animRate = 0.1
    UI.Anim(goRoot, consts.AS_ForwardCycle, "", animRate, targetTimelinePos)
  elseif pickupName == pickupConsts.SonBow then
    gameObjs.goAxeVis:Hide()
    gameObjs.goBladesVis:Hide()
    gameObjs.goBowVis:Show()
    gameObjs.goShieldVis:Hide()
    gameObjs.goCollisionGroup_Axe:Hide()
    gameObjs.goCollisionGroup_Blades:Hide()
    gameObjs.goCollisionGroup_Bow:Show()
    gameObjs.goCollisionGroup_Shield:Hide()
    local goRefnode = gameObjs.goBowVis.Child
    local goRoot = goRefnode.Child
    local targetTimelinePos = 1
    local animRate = 0.1
    UI.Anim(goRoot, consts.AS_ForwardCycle, "", animRate, targetTimelinePos)
  elseif pickupName == pickupConsts.Shield then
    gameObjs.goAxeVis:Hide()
    gameObjs.goBladesVis:Hide()
    gameObjs.goBowVis:Hide()
    gameObjs.goShieldVis:Show()
    gameObjs.goCollisionGroup_Axe:Hide()
    gameObjs.goCollisionGroup_Blades:Hide()
    gameObjs.goCollisionGroup_Bow:Hide()
    gameObjs.goCollisionGroup_Shield:Show()
    local goRefnode = gameObjs.goShieldVis.Child
    local goRoot = goRefnode.Child
    local targetTimelinePos = 1
    local animRate = 0.1
    UI.Anim(goRoot, consts.AS_ForwardCycle, "", animRate, targetTimelinePos)
  else
    assert(false, "Improper pickup name passed into skill tree visibility function. Value was " .. tostring(pickupName))
  end
end
function SkillTreeOn:Menu_Back_ReleaseHandler()
  self:SendEventToUIFsm("globalMenu", "EVT_TURN_OFF_GLOBAL_MENU")
end
function SkillTreeOn:Menu_Options_ReleaseHandler()
  self:Menu_Back_ReleaseHandler()
end
function SkillTreeOn:Menu_Triangle_ReleaseHandler()
  self:SendEventToUIFsm("globalMenu", "EVT_OPEN_SETTINGS_MENU")
end
function SkillTreeOn:SetupCameraForSkillTree()
  if tweaks.tMapCamera then
    Camera.SetMapCamera(nil)
    Camera.SetMapCamera(skillTree_camera)
  end
end
function SkillTreeOn:AnimateNodeGO(gameObject, animStyle, animName, animRate, forceInstantChange)
  if gameObject ~= nil then
    local animStartPos = 0
    local animEndPos = 1
    if animStyle == consts.AS_Reverse then
      animStartPos = 1
      animEndPos = 0
    end
    if forceInstantChange == true then
      UI.Anim(gameObject, consts.AS_Forward, animName, 0, animEndPos)
    else
      UI.Anim(gameObject, animStyle, animName, animRate, animStartPos, animEndPos)
    end
  end
end
function SkillTreeOn:ShowNodeHighlight(skillRecipeName, forceInstantOff)
  local nodeGO = util.GetUiObjByName(skillRecipeName)
  local childGO = nodeGO:FindSingleGOByName("Node")
  self.gameObjects.goHighlighter:Show()
  self:MoveHighlightToNode(skillRecipeName)
end
function SkillTreeOn:MoveHighlightToNode(skillRecipeName)
  local goNode = util.GetUiObjByName(skillRecipeName)
  local goSkillCardRefnode = util.GetUiObjByName("skillinfocard")
  local GetPos = game.UI.GetGOLocalPosition
  local SetPos = game.UI.SetGOLocalPosition
  if goNode ~= nil then
    local vNodeLocalPos = GetPos(goNode)
    SetPos(self.gameObjects.goHighlighter, vNodeLocalPos)
  end
end
function SkillTreeOn:HideNodeHighlight(skillRecipeName, forceInstantOff)
  if skillRecipeName == nil then
    assert(false, "Skill recipe name was nil in HideNodeHightlight")
  end
  local nodeGO = util.GetUiObjByName(skillRecipeName)
  local childGO = nodeGO:FindSingleGOByName("Node")
  self.gameObjects.goHighlighter:Hide()
end
function SkillTreeOn:InitNodeAnims(nodeChildGO)
  nodeChildGO:Show()
  local jid_bonus = nodeChildGO:GetJointIndex("baseHousing_Bonus")
  nodeChildGO:HideJoint(jid_bonus)
end
function SkillTreeOn:UpdateNode(pickupName, skillRecipeName, onPurchase)
  UI.SetIsClickable(util.GetUiObjByName(skillRecipeName))
  local goNode = skillUtil.GetNodeGOFromSkillRecipeName(skillRecipeName)
  local lockedByReinforcement, reinforcementLevelRequired = skillUtil.IsSkillLockedByReinforcement(pickupName, skillRecipeName)
  local isSkillPurchased = skillUtil.IsSkillPurchased(skillRecipeName)
  self:SetNodeLockedIconVisibility(goNode, false)
  self:InitNodeAnims(goNode)
  local thIconText = util.GetTextHandle(goNode, "icon_text")
  local icon_text = iconConsts[skillRecipeName]
  if icon_text == nil then
    icon_text = ""
  end
  UI.SetText(thIconText, icon_text)
  if isSkillPurchased then
    if onPurchase then
      self:SetNode_OnPurchased(goNode, pickupName)
    else
      self:SetNode_Purchased(goNode, skillRecipeName)
    end
    local hasAnyAttributeBonus = skillUtil.HasAnyActiveAttributeBonuses(skillRecipeName)
    local jid_bonus = goNode:GetJointIndex("baseHousing_Bonus")
    if hasAnyAttributeBonus then
      goNode:ShowJoint(jid_bonus)
    else
      goNode:HideJoint(jid_bonus)
    end
    self:SetBranchActive(skillRecipeName)
  else
    local skillCanBePurchased = skillUtil.CanSkillBePurchased(pickupName, skillRecipeName)
    if skillCanBePurchased then
      if not recipeUtil.IsInWallet(resourceConsts.WALLET_KRATOS, skillRecipeName) then
        game.Wallets.AddRecipe(resourceConsts.WALLET_KRATOS, skillRecipeName)
      end
      self:SetNode_CanPurchase(goNode)
    elseif lockedByReinforcement then
      self:SetNode_CantPurchase_LockedByReinforcement(goNode, reinforcementLevelRequired)
    elseif skillUtil.IsSkillLockedByResourceRequirement(skillRecipeName) then
      self:SetNode_CantPurchase_NeedResource(goNode)
    else
      self:SetNode_CantPurchase_NeedXP(goNode)
    end
    self:SetBranchDeactive(skillRecipeName, skillCanBePurchased)
  end
  local isNodeNew = skillUtil.IsNodeNew(pickupName, skillRecipeName)
  skillUtil.UpdateNewIcon(skillRecipeName, isNodeNew)
  if lockedByReinforcement then
    skillUtil.MarkSkillAsLocked(skillRecipeName)
  elseif not isSkillPurchased and skillUtil.WasPreviouslyLocked(skillRecipeName) then
    self:SetNode_LockedByReinforcementText(goNode, reinforcementLevelRequired)
    if self._nodesToTriggerFlourishOn == nil then
      self._nodesToTriggerFlourishOn = {}
    end
    local unlockFlourishDelay = 0.5
    self._nodesToTriggerFlourishOn[skillRecipeName] = goNode
    self:SetNodeLockedIconVisibility(goNode, true)
    if not self:HaveTimer("unlockFlourishDelay") then
      self:StartTimer("unlockFlourishDelay", unlockFlourishDelay, function()
        self:PlayAllUnlockFlourishAnims()
      end)
    end
    skillUtil.MarkSkillAsUnlocked(skillRecipeName)
  end
end
function SkillTreeOn:PlayAllUnlockFlourishAnims()
  local anyUnlocked = false
  for _, gameObj in pairs(self._nodesToTriggerFlourishOn) do
    self:SetNode_OnUnlocked(gameObj)
    anyUnlocked = true
  end
  if anyUnlocked then
    self:StartTimer("UnlockFlourishSFXDelay", 0.5, function()
      Audio.PlaySound("SND_UX_Pause_Menu_SkillsTree_Unlock_Skills")
    end)
  end
  self._nodesToTriggerFlourishOn = nil
end
function SkillTreeOn:AnimatePurchaseState(gameObject, animTable)
  for animName, targetTimelinePos in pairs(animTable) do
    UI.Anim(gameObject, consts.AS_Forward, animName, 0, targetTimelinePos)
  end
end
local pickupNameToFX = {
  [pickupConsts.Axe] = "axegem_flourish",
  [pickupConsts.Blades] = "bladegem_flourish",
  [pickupConsts.SonBow] = "bowgem_flourish"
}
function SkillTreeOn:SetNode_OnPurchased(goButtonChild, pickupName)
  self:AnimatePurchaseState(goButtonChild, skillTreeConsts.NodePurchasedStateAnimPos.OnPurchased)
  local goPurchaseFlourish = util.GetUiObjByName("SkillTreePurchaseFlourish")
  goPurchaseFlourish:Show()
  local animName = ""
  local animRate = 1
  local frameMax = 30
  local animFrame_Start = 0
  local animFrame_End = 30
  local animPos_Start = animFrame_Start / frameMax
  local animPos_End = animFrame_End / frameMax
  UI.Anim(goPurchaseFlourish, consts.AS_Forward, animName, animRate, animPos_Start, animPos_End)
end
function SkillTreeOn:SetNode_OnUnlocked(goButtonChild)
  local animName = "smallDiamond_Unlock"
  local animRate = 1
  local frameMax = 30
  local animFrame_Start = 0
  local animFrame_End = 90
  local animPos_Start = animFrame_Start / frameMax
  local animPos_End = animFrame_End / frameMax
  UI.Anim(goButtonChild, consts.AS_Forward, animName, animRate, animPos_Start, animPos_End)
end
function SkillTreeOn:SetNode_ResetOnUnlocked(goButtonChild)
  local animName = "smallDiamond_Unlock"
  local animRate = 0
  local animFrame_End = 0
  UI.Anim(goButtonChild, consts.AS_Forward, animName, animRate, animFrame_End)
end
function SkillTreeOn:SetBranchActive(skillRecipeName)
  local goBranch = self.branchGameObjects[skillRecipeName]
  if goBranch ~= nil then
    local targetTimelinePos = 1
    local animRate = 0.06 + 0.001 * math.random(-25, 25)
    UI.Anim(goBranch.Child, consts.AS_ForwardCycle, "skillTreeBranch_Loop", animRate, targetTimelinePos)
    targetTimelinePos = 1
    animRate = 0
    UI.Anim(goBranch.Child, consts.AS_Forward, "skillTreeBranch_Show", animRate, targetTimelinePos)
    local color = colors.COLOR_CAN_PURCHASE_SKILL
    util.SetGameObjectColor(goBranch.Child, color, "skillTree_Branch", "Glow1", "cst_EmissiveTint")
  end
end
function SkillTreeOn:SetBranchDeactive(skillRecipeName, skillCanBePurchased)
  local goBranch = self.branchGameObjects[skillRecipeName]
  if goBranch ~= nil then
    local targetTimelinePos = 0
    local animRate = 0
    UI.Anim(goBranch.Child, consts.AS_Forward, "skillTreeBranch_Show", animRate, targetTimelinePos)
    local color = skillCanBePurchased and colors.COLOR_CAN_PURCHASE_SKILL or colors.COLOR_CANT_PURCHASE_SKILL
    util.SetGameObjectColor(goBranch.Child, color, "skillTree_Branch", "Glow1", "cst_EmissiveTint")
  end
end
function SkillTreeOn:SetNode_Purchased(goButtonChild, skillRecipeName)
  self:AnimatePurchaseState(goButtonChild, skillTreeConsts.NodePurchasedStateAnimPos.Purchased)
end
function SkillTreeOn:SetNode_CanPurchase(goButtonChild)
  self:AnimatePurchaseState(goButtonChild, skillTreeConsts.NodePurchasedStateAnimPos.CanPurchase)
end
function SkillTreeOn:SetNode_CantPurchase_NeedResource(goButtonChild)
  self:AnimatePurchaseState(goButtonChild, skillTreeConsts.NodePurchasedStateAnimPos.CantPurchase_NeedXP)
  local thIconText = util.GetTextHandle(goButtonChild, "icon_text")
  local icon_text = iconConsts.HiddenSkill
  UI.SetText(thIconText, icon_text)
end
function SkillTreeOn:SetNode_CantPurchase_NeedXP(goButtonChild)
  self:AnimatePurchaseState(goButtonChild, skillTreeConsts.NodePurchasedStateAnimPos.CantPurchase_NeedXP)
end
function SkillTreeOn:SetNode_CantPurchase_LockedByReinforcement(goButtonChild, reinforcementLevelRequired)
  self:SetNode_LockedByReinforcementText(goButtonChild, reinforcementLevelRequired)
  self:AnimatePurchaseState(goButtonChild, skillTreeConsts.NodePurchasedStateAnimPos.CantPurchase_LockedByReinforcement)
  self:SetNodeLockedIconVisibility(goButtonChild, true)
end
function SkillTreeOn:SetNode_LockedByReinforcementText(goButtonChild, reinforcementLevelRequired)
  local requiredReinforcementLevelText = tostring(reinforcementLevelRequired + 1)
  local thReinforcement = util.GetTextHandle(goButtonChild, "Text_ReinforcementNumber")
  UI.SetText(thReinforcement, requiredReinforcementLevelText)
end
function SkillTreeOn:SetNodeLockedIconVisibility(goButtonChild, show)
  local jointID_lockedIcon = goButtonChild:GetJointIndex("icon_locked")
  if show then
    self:SetNode_ResetOnUnlocked(goButtonChild)
    goButtonChild:ShowJoint(jointID_lockedIcon)
  else
    goButtonChild:HideJoint(jointID_lockedIcon)
  end
end
function SkillTreeOn:UpdateNodesByList(nodeList, pickupName, onPurchase)
  local skillRecipeNames = nodeList:GetItems()
  for _, skillRecipeName in ipairs(skillRecipeNames) do
    self:UpdateNode(pickupName, skillRecipeName, onPurchase)
  end
end
function SkillTreeOn:GetCurrentCreature(pickupName)
  if pickupName ~= nil and game.Pickup.HasTags(pickupName, pickupConsts.TAG_PICKUP_SON) then
    return AI.FindSon()
  else
    return Player.FindPlayer()
  end
end
function SkillTreeOn:UpdateNodeList(nodeList, pickupName, onPurchase)
  if pickupUtil.IsValidName(pickupName) then
    local newItemArray = skillUtil.GetSkillList(pickupName)
    local showList = false
    local useOnGainFocus = true
    local itemDetermineFocusabilityFunc, itemGetTextFunc
    nodeList:Refresh(newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, itemGetTextFunc)
    if nodeList:GetButtonCount() > 0 then
      util.Show(pickupName .. "_SkillTree")
      self:UpdateNodesByList(nodeList, pickupName, onPurchase)
    end
  end
end
function SkillTreeOn:UpdateHoldFill(holdFill, canRecipeBeMade, isSkillPurchased)
  holdFill:Reset()
  if canRecipeBeMade == true then
    util.Show("SkillTree_HoldToCraft", "SkillTree_HoldToCraftMeter")
    util.Hide("skillTree_unavailableBacking")
    holdFill:Activate()
  else
    util.Hide("SkillTree_HoldToCraft", "SkillTree_HoldToCraftMeter")
    if isSkillPurchased then
      util.Hide("skillTree_unavailableBacking")
    else
      util.Show("skillTree_unavailableBacking")
    end
    holdFill:Deactivate()
  end
end
function SkillTreeOn:Node_Button_OnGainFocus(currSkillRecipe)
  if currSkillRecipe ~= nil then
    local submenuList = self.menu:GetList(consts.inworldMenu_SubmenuList)
    local currPickup = submenuList:GetSelectedItem()
    local currCreature = self:GetCurrentCreature(currPickup)
    self._skillInfoCard:SetSkill(currSkillRecipe, self.currentCharacter)
    self._skillInfoCard:Update()
    local canRecipeBeMade = skillUtil.CanSkillBePurchased(currPickup, currSkillRecipe)
    local isSkillPurchased = skillUtil.IsSkillPurchased(currSkillRecipe)
    self:UpdateHoldFill(self.holdFill, canRecipeBeMade, isSkillPurchased)
    self:UpdateFooterButtonPrompt()
    self.lastSelectedRecipeName = currSkillRecipe
    local goExtraHoldFillNode = skillUtil.GetNodeGOFromSkillRecipeName(currSkillRecipe)
    self.holdFill:SetExtraSlidersTable({
      {
        GameObject = goExtraHoldFillNode,
        AnimName = "smallDiamond_Fill"
      }
    })
    if isSkillPurchased or canRecipeBeMade then
      self:ColorCursor(colors.CURSORCOLOR_PURCHASED)
      self:ColorHighlight(colors.CURSORCOLOR_PURCHASED)
    else
      self:ColorCursor(colors.CURSORCOLOR_DEFAULT)
      self:ColorHighlight(colors.CURSORCOLOR_DEFAULT)
    end
  end
end
function SkillTreeOn:Node_Button_OnLoseFocus(currSkillRecipe)
end
function SkillTreeOn:ColorHighlight(color)
  util.SetGameObjectColor(self.gameObjects.goHighlighter, color, "highlightGlow", "Layer0", "cst_EmissiveTint")
end
function SkillTreeOn:ColorCursor(color)
  local goCursorSelectedMesh = self._goCursorRefnode:FindSingleGOByName("Mesh_Selected")
  local goCursorUnselectedMesh = self._goCursorRefnode:FindSingleGOByName("Mesh_Unselected")
  util.SetGameObjectColor(goCursorSelectedMesh, color, "menucursor", "LayerX", "cst_EmissiveTint")
  util.SetGameObjectColor(goCursorUnselectedMesh, color, "menucursor", "LayerX", "cst_EmissiveTint")
end
function SkillTreeOn:Node_Button_HighlightOn(button, animateImmediately)
  local currSkillRecipe = button:get_item()
  if currSkillRecipe ~= nil then
    self:ShowNodeHighlight(currSkillRecipe, animateImmediately)
  end
end
function SkillTreeOn:Node_Button_HighlightOff(button, animateImmediately)
  local currSkillRecipe = button:get_item()
  if currSkillRecipe ~= nil then
    self:HideNodeHighlight(currSkillRecipe, animateImmediately)
  end
end
function SkillTreeOn:Node_Button_ItemCompare(string, otherString)
  return string.lower(string) == string.lower(otherString)
end
function SkillTreeOn:UpdateFooterButtonPrompt()
  local showTouchPadMap = game.IsMapAvailable() and game.build.GOLD_VERSION ~= 0
  self.menu:UpdateFooterButton("Move", true)
  self.menu:UpdateFooterButton("Map", showTouchPadMap)
  self.menu:UpdateFooterButton("Weapon", true)
  self.menu:UpdateFooterButton("Quest", true)
  self.menu:UpdateFooterButton("Settings", true)
  self.menu:UpdateFooterButton("Exit", true)
  self.menu:UpdateFooterButton("Close", true)
  self.menu:UpdateFooterButtonText()
end
function SkillTreeOn:UnlockSkill()
  local nodeList = self.menu:GetList("nodeList")
  local skillRecipeName = nodeList:GetSelectedItem()
  local submenuList = self.menu:GetList(consts.inworldMenu_SubmenuList)
  local currPickup = submenuList:GetSelectedItem()
  local currCreature = self:GetCurrentCreature(currPickup)
  local canRecipeBeMade = skillUtil.CanSkillBePurchased(currPickup, skillRecipeName)
  if canRecipeBeMade then
    recipeUtil.Craft(skillRecipeName, "SkillTree")
    local onPurchase = true
    self:UpdateNodeList(nodeList, currPickup, onPurchase)
    self:Node_Button_OnGainFocus(skillRecipeName)
  end
  local goNode = skillUtil.GetNodeGOFromSkillRecipeName(skillRecipeName)
  local animName = "smallDiamond_Flourish"
  local animRate = 1
  local frameMax = 30
  local animFrame_Start = 1
  local animFrame_End = 30
  local animPos_Start = animFrame_Start / frameMax
  local animPos_End = animFrame_End / frameMax
  UI.Anim(goNode, consts.AS_Forward, animName, animRate, animPos_Start, animPos_End)
  animationUtil.SpawnFX("skills_flourish", goNode, "")
  self:UpdateSkillMenuNotifications()
  self:SendEventToUIFsm("inWorldMenu", "EVT_UPDATE_EQUIPPED_ATTRIBUTES", pickupConsts.TAG_PICKUP_KRATOS)
end
function SkillTreeOn:UpdateNewIcon(skillRecipeName, submenuButton)
  if recipeUtil.IsValidRecipeName(skillRecipeName) and submenuButton ~= nil then
    local pickupName = submenuButton:get_item()
    if pickupUtil.IsValidName(pickupName) and skillUtil.ShouldCheckNew(pickupName, skillRecipeName) then
      recipeUtil.ClearNew(skillRecipeName)
      local isNodeNew = skillUtil.IsNodeNew(pickupName, skillRecipeName)
      skillUtil.UpdateNewIcon(skillRecipeName, isNodeNew)
      submenuButton:Update()
      self:SendEventToUIFsm("inWorldMenu", "EVT_REFRESH_NOTIFICATIONS")
    end
  end
end
function SkillTreeOn:EVT_HandleMapCollisionChangeHook(mapCollisionGOs)
  local collisionObjectFound = false
  for _, goCollisionObj in ipairs(mapCollisionGOs) do
    if collisionObjectFound then
      break
    end
    local isDummyCollisionObj = string.lower(goCollisionObj:GetName()) == "collision"
    if not isDummyCollisionObj then
      local truncatedName = string.sub(goCollisionObj:GetName(), 6)
      local displayName = recipeUtil.GetDisplayName(truncatedName)
      self.shared.ReticleText = displayName
      local nodeList = self.menu:GetList("nodeList")
      local itemToSelect = truncatedName
      local useOnGainFocus = true
      nodeList:SelectItem(itemToSelect, useOnGainFocus)
      self._skillInfoCard:ShowCard()
      collisionObjectFound = true
    end
  end
  local forSkillTree = true
  if not collisionObjectFound then
    self.shared.ReticleText = ""
    local forceInstantOff = true
    local submenuList = self.menu:GetList(consts.inworldMenu_SubmenuList)
    self:UpdateNewIcon(self.lastSelectedRecipeName, submenuList:GetSelectedButton())
    self.lastSelectedRecipeName = nil
    self:ColorCursor(colors.CURSORCOLOR_DEFAULT)
    animationUtil.SetCursorSelected(self._goCursorRefnode, false, forSkillTree)
  else
    animationUtil.SetCursorSelected(self._goCursorRefnode, true, forSkillTree)
  end
end
function SkillTreeOn:EVT_TURN_OFF_SKILL_TREE_MENU()
  self:Goto("SkillTreeOff")
end
function SkillTreeOn:EVT_Square_Press()
  if self.holdFill ~= nil then
    self.holdFill:PlayUnavailableSound()
  end
end
function SkillTreeOn:EVT_MOUSE_CLICKED()
  local selected = UI.GetEventSenderGameObject()
  local item = selected:GetName()
  local list = self.menu:GetList("nodeList")
  for _, button in ipairs(list._buttonArray) do
    if button:HasItem(item) then
      Camera.PointAtGO(selected)
      break
    end
  end
end
function SkillTreeMenu:OnSaveCheckpoint(tab)
end
function SkillTreeMenu:OnRestoreCheckpoint(tab)
end
