local tablex = require("core.tablex")
local animationUtil = require("ui.animationUtil")
local buttonUtil = require("ui.buttonUtil")
local characterUtil = require("ui.characterUtil")
local colors = require("ui.colors")
local consts = require("ui.consts")
local iconConsts = require("ui.iconConsts")
local lamsConsts = require("ui.lamsConsts")
local list = require("ui.list")
local menu = require("ui.menu")
local pickupConsts = require("ui.pickupConsts")
local pickupUtil = require("ui.pickupUtil")
local resourceConsts = require("ui.resourceConsts")
local resourceUtil = require("ui.resourceUtil")
local runeUtil = require("ui.runeUtil")
local tutorialUtil = require("ui.tutorialUtil")
local util = require("ui.util")
local Player = game.Player
local Recipes = game.Recipes
local Resources = game.Resources
local UI = game.UI
local Wallets = game.Wallets
local allRecipesTables = {}
local CreateAllRecipesTables = function()
  for subStateSelection, mainSelectionTable in pairs(resourceConsts.RECIPE_SUBMENUS_MAIN_SELECTIONS) do
    allRecipesTables[subStateSelection] = {}
    for _, mainSelection in ipairs(mainSelectionTable) do
      allRecipesTables[subStateSelection][mainSelection] = {}
      for _, categorySelection in ipairs(resourceConsts.RECIPE_SUBMENUS_CATEGORY_SELECTIONS[mainSelection]) do
        allRecipesTables[subStateSelection][mainSelection][categorySelection] = {}
      end
    end
  end
  return allRecipesTables
end
CreateAllRecipesTables()
local IsValidRecipeName = function(resourceName)
  return not util.IsStringNilOrEmpty(resourceName)
end
local GetInputRecipeItems = function(recipeName)
  local inputRecipeItems = {}
  if recipeName ~= nil then
    inputRecipeItems = Recipes.GetInputs(recipeName)
  end
  return inputRecipeItems
end
local GetOutputRecipeItems = function(recipeName)
  local outputRecipeItems = {}
  if recipeName ~= nil then
    local outputs = Recipes.GetOutputs(recipeName)
    if outputs ~= nil then
      outputRecipeItems = outputs
    end
  end
  assert(#outputRecipeItems ~= 0, "Zero output items recieved from recipe: " .. tostring(recipeName) .. ". Please double check recipe/resource definition exists.")
  return outputRecipeItems
end
local GetRecipeItemName = function(recipeItem)
  local name
  if recipeItem ~= nil then
    name = recipeItem.Name
  end
  return name
end
local GetRecipeItemType = function(recipeItem)
  local itemType
  if recipeItem ~= nil then
    itemType = recipeItem.Type
  end
  return itemType
end
local GetRecipeItemLamsNameId = function(recipeItem)
  local lamsNameId
  if recipeItem ~= nil then
    lamsNameId = recipeItem.LamsNameId
  end
  return lamsNameId
end
local GetRecipeItemLamsDescriptionId = function(recipeItem)
  local lamsDescriptionId
  if recipeItem ~= nil then
    lamsDescriptionId = recipeItem.LamsDescriptionId
  end
  return lamsDescriptionId
end
local GetRecipeItemAmount = function(recipeItem)
  local amount
  if recipeItem ~= nil then
    amount = recipeItem.Amount
  end
  return amount
end
local HasFlag = function(recipeName, resourceFlag)
  local hasFlag = false
  if recipeName ~= nil and resourceFlag ~= nil then
    hasFlag = Recipes.HasFlag(recipeName, resourceFlag)
  end
  return hasFlag
end
local GetPrimaryRecipeItem = function(recipeName)
  assert(IsValidRecipeName(recipeName), "No recipe passed into GetPrimaryRecipeItem!")
  local recipeItem
  if HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) then
    local inputs = GetInputRecipeItems(recipeName)
    assert(inputs and 0 < #inputs, "Selected a recipe with no inputs. Please add inputs to " .. recipeName)
    recipeItem = inputs[1]
  else
    local outputs = GetOutputRecipeItems(recipeName)
    assert(outputs and 0 < #outputs, "Selected a recipe with no outputs. Please add outputs to " .. recipeName)
    recipeItem = outputs[1]
  end
  return recipeItem
end
local IsReinforcementRecipe = function(recipeName)
  return HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_UPGRADE)
end
local RecipeIsforQuiver = function(recipeName)
  return HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_QUIVER)
end
local GetReinforcementRecipePickup = function(recipeName, inputRecipeItems)
  assert(recipeName ~= nil)
  local reinforcementPickup
  if RecipeIsforQuiver(recipeName) then
    return pickupConsts.SonQuiver
  end
  if game.Recipes.GetReinforcementRecipePickup ~= nil then
    return game.Recipes.GetReinforcementRecipePickup(recipeName)
  end
  if inputRecipeItems == nil then
    inputRecipeItems = GetInputRecipeItems(recipeName)
  end
  for _, input in pairs(inputRecipeItems) do
    local isTheAssociatedPickup = not input.Consume
    if isTheAssociatedPickup then
      reinforcementPickup = input.Name
    end
  end
  return reinforcementPickup
end
local GetDisplayName = function(recipeName)
  local displayName = ""
  if recipeName ~= nil then
    local lamsId = Recipes.GetLamsNameId(recipeName)
    if lamsId == 0 then
      local primaryItem = GetPrimaryRecipeItem(recipeName)
      lamsId = GetRecipeItemLamsNameId(primaryItem)
    end
    displayName = util.GetLAMSMsg(lamsId, recipeName)
  end
  return displayName
end
local GetDescription = function(recipeName)
  local desc = ""
  if recipeName ~= nil then
    local lamsId = Recipes.GetLamsDescriptionId(recipeName)
    if lamsId == 0 then
      local primaryItem = GetPrimaryRecipeItem(recipeName)
      lamsId = GetRecipeItemLamsDescriptionId(primaryItem)
    end
    desc = util.GetLAMSMsg(lamsId, recipeName)
  end
  return desc
end
local GetTier = function(recipeName)
  local flag
  local num = 0
  for tierIndex, tierFlag in ipairs(resourceConsts.TIER_FLAGS) do
    if HasFlag(recipeName, tierFlag) == true then
      flag = tierFlag
      num = tierIndex
      break
    end
  end
  return flag, num
end
local GetReinforcementWeapon = function(recipeName)
  local weaponName
  if HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_UPGRADE) then
    if HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_AXE) then
      weaponName = pickupConsts.Axe
    elseif HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_BLADES) then
      weaponName = pickupConsts.Blades
    elseif HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SON_BOW) then
      weaponName = pickupConsts.SonBow
    end
  end
  return weaponName
end
local AtLimit = function(recipeName)
  local atLimit = false
  if IsValidRecipeName(recipeName) and not HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) then
    atLimit = Wallets.IsRecipeOutputResourceMaxedOut(resourceConsts.WALLET_KRATOS, recipeName, 0)
  end
  return atLimit
end
local AtPickupTier = function(pickupName, recipeName)
  local pickupTier = pickupUtil.GetTier(pickupName)
  local tierFlag, tierNum = GetTier(recipeName)
  assert(tierFlag ~= nil, "Recipe " .. recipeName .. " does not have a Tier flag set!")
  return pickupTier >= tierNum
end
local RecipeIsForBuy = function(recipeName)
  local isForBuy = false
  if HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_BUY) then
    assert(not HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL), "Recipe named: " .. tostring(recipeName) .. " has both Buy and Sell flags!")
    isForBuy = true
  end
  return isForBuy
end
local RecipeIsForBuyGenerateRune = function(recipeName)
  return HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_BUY) and HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_RUNE)
end
local RecipeIsForBuyResurrectionStone = function(recipeName)
  return HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_BUY) and HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_RESURRECTION_STONE)
end
local RecipeIsForSell = function(recipeName)
  local isForSell = false
  if HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) then
    assert(not HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_BUY), "Recipe named: " .. tostring(recipeName) .. " has both Sell and Buy flags!")
    isForSell = true
  end
  return isForSell
end
local RecipeIsForSellRune = function(recipeName)
  return HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) and HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_RUNE)
end
local IsRuneCreatorRecipe = function(recipeName)
  return not HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_GENERATE_RUNE) and HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_BUY) and HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_ROLL_CONDITION)
end
local RecipeIsForRunicAttack = function(recipeName)
  local hasRunicAttackFlag = false
  for _, flag in ipairs(resourceConsts.RUNIC_ATTACK_FLAGS) do
    if HasFlag(recipeName, flag) then
      hasRunicAttackFlag = true
      break
    end
  end
  return hasRunicAttackFlag
end
local RecipeIsForSellArtifact = function(recipeName)
  return HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) and HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_ARTIFACT)
end
local IsNew = function(recipeName)
  local isNew = false
  if IsValidRecipeName(recipeName) then
    isNew = UI.HasNotification(consts.NotificationTypes.Recipe, recipeName)
  end
  return isNew
end
local ClearNew = function(recipeName)
  if IsNew(recipeName) then
    UI.ClearNotification(consts.NotificationTypes.Recipe, recipeName)
  end
end
local IsInWallet = function(walletName, recipeName)
  local isInWallet = false
  if walletName ~= nil and recipeName ~= nil then
    isInWallet = Wallets.HasRecipe(walletName, recipeName)
  end
  return isInWallet
end
local CanPurchase = function(recipeName)
  local canRecipeBePurchased = false
  if IsValidRecipeName(recipeName) then
    if RecipeIsForBuyGenerateRune(recipeName) then
      canRecipeBePurchased = runeUtil.HasMemorySpaceForRune() and Wallets.CanPurchase(resourceConsts.WALLET_KRATOS, recipeName)
    else
      canRecipeBePurchased = Wallets.CanPurchase(resourceConsts.WALLET_KRATOS, recipeName)
    end
  end
  return canRecipeBePurchased
end
local CanFulfill = function(walletName, recipeName)
  local canFulfill = false
  if walletName ~= nil and recipeName ~= nil then
    canFulfill = Wallets.CanFulfill(walletName, recipeName)
  end
  return canFulfill
end
local Purchase = function(sellerWalletName, recipeName)
  Wallets.Purchase(resourceConsts.WALLET_KRATOS, sellerWalletName, recipeName)
end
local GetWalletForRecipe = function(recipeName)
  return resourceConsts.WALLET_KRATOS
end
local Craft = function(recipeName, recipeSource)
  local wallet = GetWalletForRecipe(recipeName)
  if not Wallets.HasRecipe(wallet, recipeName) then
    Wallets.AddRecipe(wallet, recipeName)
  end
  assert(IsInWallet(wallet, recipeName), "Failed to craft recipe: " .. tostring(recipeName) .. [[

Wallet:]] .. wallet .. " does not contain the recipe to craft it.")
  local success = Wallets.Craft(wallet, recipeName, recipeSource)
  assert(success == true, "Failed to craft recipe: " .. tostring(recipeName) .. ". Please check the recipe definition for errors.")
end
local GenerateRunes = function(recipeName)
  local recipeItems = GetOutputRecipeItems(recipeName)
  local runeID
  for _, runeRecipeItem in ipairs(recipeItems) do
    local runeRollName = GetRecipeItemName(runeRecipeItem)
    local generateSuccessful, newRuneID = Wallets.GenerateRune(resourceConsts.WALLET_KRATOS, runeRollName)
    if generateSuccessful then
      runeID = newRuneID
    end
  end
  return runeID
end
local RollCondition = function(recipeName)
  local recipeItems = GetOutputRecipeItems(recipeName)
  local oldTableOfAllRunes = game.Wallets.GetAllRunes(resourceConsts.WALLET_KRATOS, {}, resourceConsts.RESOURCE_FLAG_SOCKET_ARMOR, {})
  for _, conditionRecipeItem in ipairs(recipeItems) do
    local conditionName = GetRecipeItemName(conditionRecipeItem)
    game.Loot.RollCondition(conditionName, resourceConsts.WALLET_KRATOS)
  end
  local newTableOfAllRunes = game.Wallets.GetAllRunes(resourceConsts.WALLET_KRATOS, {}, resourceConsts.RESOURCE_FLAG_SOCKET_ARMOR, {})
  local tableCountDiff = #newTableOfAllRunes - #oldTableOfAllRunes
  assert(tableCountDiff == 1, "RollCondition recipe: " .. tostring(recipeName) .. " did not generate a rune!!")
  return newTableOfAllRunes[#newTableOfAllRunes]
end
local GetRecipesWithAllFlags = function(wallet, includeTypes, excludeTypes, sortingGroup)
  local recipeList = Wallets.GetRecipesWithAllFlags(wallet, includeTypes, excludeTypes, resourceConsts.POWER_LEVEL_SORTING)
  Wallets.SortRecipeList(wallet, recipeList, resourceConsts.RARITY_SORTING)
  if sortingGroup ~= resourceConsts.RARITY_SORTING then
    Wallets.SortRecipeList(wallet, recipeList, sortingGroup)
  end
  return recipeList
end
local GetRecipesWithAllFlags_NGPSpecial = function(wallet, includeTypes, excludeTypes, sortingGroup)
  local recipeList = Wallets.GetRecipesWithAllFlags(wallet, includeTypes, excludeTypes, resourceConsts.POWER_LEVEL_SORTING, "NGPGrantImmediate")
  Wallets.SortRecipeList(wallet, recipeList, resourceConsts.RARITY_SORTING)
  if sortingGroup ~= resourceConsts.RARITY_SORTING then
    Wallets.SortRecipeList(wallet, recipeList, sortingGroup)
  end
  return recipeList
end
local GetSubMenuNameFromSubStateIndex = function(subStateIndex, isVendorMenu)
  local subStatePrefix = isVendorMenu and "Vendor" or "Recipes"
  local subStateName = subStatePrefix .. subStateIndex
  return subStateName
end
local IsVendorOnlySelection = function(selection)
  return resourceConsts.RECIPE_SUBMENUS_VENDOR_ONLY[selection] ~= nil
end
local IsSonSelection = function(selection)
  return resourceConsts.RECIPE_SUBMENUS_SON_SELECTIONS[selection] ~= nil
end
local IsBladesSelection = function(selection)
  return resourceConsts.RECIPE_SUBMENUS_BLADES_SELECTIONS[selection] ~= nil
end
local GetCategoryExtraIncludeFlags = function(mainSelection, categorySelection)
  local categoryIncludeFlags
  if resourceConsts.RECIPE_SUBMENUS_HAS_CATEGORY_INCLUDES[mainSelection] ~= nil then
    categoryIncludeFlags = resourceConsts.RECIPE_SUBMENUS_CATEGORY_EXTRA_INCLUDE_FLAGS[categorySelection]
  end
  return categoryIncludeFlags
end
local ShouldHideAttributesCard = function(subStateSelection, categorySelection)
  return resourceConsts.RECIPE_SUBMENUS_HIDE_ATTR_CAT_SELECTIONS[categorySelection] == true
end
local GetSortingGroupIndex = function(categorySelection)
  assert(not util.IsStringNilOrEmpty(categorySelection), "No selection passed into GetSortingGroupFromCategorySelection!")
  return resourceConsts.RECIPE_SUBMENUS_CATEGORY_SORTING_GROUP_INDEX[categorySelection]
end
local GetSortingGroupFromSelection = function(categorySelection)
  local sortingGroup
  local sortingGroupIndex = GetSortingGroupIndex(categorySelection)
  if sortingGroupIndex ~= nil then
    sortingGroup = resourceConsts.SORTING_GROUPS[sortingGroupIndex]
  end
  return sortingGroup
end
local GetVendorExcludeFlag = function(vendorWallet)
  local vendorAdditionalExcludeFlag
  if vendorWallet ~= nil then
    vendorAdditionalExcludeFlag = vendorWallet == resourceConsts.WALLET_BROK and resourceConsts.RESOURCE_FLAG_SINDRI or resourceConsts.RESOURCE_FLAG_BROK
  end
  return vendorAdditionalExcludeFlag
end
local GetRecipeExcludeTypes = function(vendorWallet, currCategorySelection)
  local excludeTypes = {}
  local vendorAdditionalExcludeFlag = GetVendorExcludeFlag(vendorWallet)
  if vendorAdditionalExcludeFlag ~= nil then
    tablex.FastInsert(excludeTypes, vendorAdditionalExcludeFlag, #excludeTypes + 1)
  end
  local isSonSelection = IsSonSelection(currCategorySelection)
  local characterExcludeFlag = isSonSelection and resourceConsts.RESOURCE_FLAG_KRATOS or resourceConsts.RESOURCE_FLAG_SON
  tablex.FastInsert(excludeTypes, characterExcludeFlag, #excludeTypes + 1)
  return excludeTypes
end
local RemoveUnavailableRecipes = function(recipesTable)
  for recipeIndex = #recipesTable, 1, -1 do
    local recipeName = recipesTable[recipeIndex]
    if AtLimit(recipeName) then
      if not RecipeIsForBuyResurrectionStone(recipeName) then
        table.remove(recipesTable, recipeIndex)
      end
    elseif RecipeIsForSell(recipeName) then
      if not CanPurchase(recipeName) then
        if HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_RESOURCE) or HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_ARTIFACT) then
          table.remove(recipesTable, recipeIndex)
        end
      else
        local primaryItem = GetPrimaryRecipeItem(recipeName)
        local primaryItemType = GetRecipeItemType(primaryItem)
        local primaryItemName = GetRecipeItemName(primaryItem)
        if primaryItemType == resourceConsts.RESOURCE_FLAG_PICKUP and characterUtil.ItemIsEquipped(primaryItemName) then
          table.remove(recipesTable, recipeIndex)
        end
      end
    elseif HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_CRAFT) then
      local primaryItem = GetPrimaryRecipeItem(recipeName)
      local primaryItemType = GetRecipeItemType(primaryItem)
      local primaryItemName = GetRecipeItemName(primaryItem)
      if primaryItemType == resourceConsts.RESOURCE_FLAG_PICKUP then
        local amount = game.Pickup.GetProfileStage(primaryItemName)
        if -1 < amount then
          table.remove(recipesTable, recipeIndex)
        end
      end
    elseif IsReinforcementRecipe(recipeName) and not HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_QUIVER) then
      local pickupName = GetReinforcementRecipePickup(recipeName)
      local amount = resourceUtil.GetAmount(pickupName)
      if amount <= 0 then
        table.remove(recipesTable, recipeIndex)
      end
    end
  end
end
local RemoveUnavailableRunes = function(runeIDTable)
  local isNewGamePlus = game.GetNewGamePlus()
  for runeIndex = #runeIDTable, 1, -1 do
    local runeID = runeIDTable[runeIndex]
    if isNewGamePlus then
      local runeInfo = game.Wallets.GetRuneInfo("HERO", runeID)
      local sellRecipe = runeInfo.SellRecipe
      if not sellRecipe or sellRecipe == "Recipe_FreeRuneExchange" then
        table.remove(runeIDTable, runeIndex)
      end
    elseif runeUtil.HasFlag(runeID, resourceConsts.RESOURCE_FLAG_UNIQUE_RUNE) then
      table.remove(runeIDTable, runeIndex)
    end
  end
end
local RecipeListItem_CreateItem = function(recipeName, inputRecipeItems, primaryItem, reinforcementPickupName)
  return {
    RecipeName = recipeName,
    InputRecipeItems = inputRecipeItems,
    PrimaryItem = primaryItem,
    ReinforcementPickupName = reinforcementPickupName
  }
end
local RecipeListItem_GetRecipeName = function(recipeListItem)
  local recipeName
  if recipeListItem ~= nil then
    recipeName = recipeListItem.RecipeName
  end
  return recipeName
end
local RecipeListItem_GetInputRecipeItems = function(recipeListItem)
  local inputRecipeItems
  if recipeListItem ~= nil then
    inputRecipeItems = recipeListItem.InputRecipeItems
  end
  return inputRecipeItems
end
local RecipeListItem_GetPrimaryItem = function(recipeListItem)
  local primaryItem
  if recipeListItem ~= nil then
    primaryItem = recipeListItem.PrimaryItem
  end
  return primaryItem
end
local RecipeListItem_GetReinforcementPickupName = function(recipeListItem)
  local reinforcementPickupName
  if recipeListItem ~= nil then
    reinforcementPickupName = recipeListItem.ReinforcementPickupName
  end
  return reinforcementPickupName
end
local RecipeListItem_GetDisplayName = function(recipeListItem)
  local displayName = ""
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  if recipeName ~= nil then
    local lamsId = Recipes.GetLamsNameId(recipeName)
    if lamsId == 0 then
      local primaryRecipeItem = RecipeListItem_GetPrimaryItem(recipeListItem)
      lamsId = GetRecipeItemLamsNameId(primaryRecipeItem)
    end
    displayName = util.GetLAMSMsg(lamsId, recipeName)
  end
  return displayName
end
local RecipeListItem_GetDescription = function(recipeListItem)
  local description = ""
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  if recipeName ~= nil then
    local lamsId = Recipes.GetLamsDescriptionId(recipeName)
    if lamsId == 0 then
      local primaryRecipeItem = RecipeListItem_GetPrimaryItem(recipeListItem)
      lamsId = GetRecipeItemLamsDescriptionId(primaryRecipeItem)
    end
    description = util.GetLAMSMsg(lamsId, recipeName)
  end
  return description
end
local RecipeListItem_GetRecipeRarity = function(recipeListItem)
  local recipeRarity
  if recipeListItem ~= nil then
    local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
    local primaryRecipeItem = RecipeListItem_GetPrimaryItem(recipeListItem)
    if RecipeIsForSellRune(recipeName) then
      local runeID = runeUtil.GetRuneInfoRuneID(primaryRecipeItem)
      recipeRarity = runeUtil.GetRarityFromID(runeID)
    elseif RecipeIsForRunicAttack(recipeName) or RecipeIsForSellArtifact(recipeName) then
      recipeRarity = nil
    else
      local primaryItemName = GetRecipeItemName(primaryRecipeItem)
      recipeRarity = resourceUtil.GetRarity(primaryItemName)
    end
  end
  return recipeRarity
end
local RecipeListItem_GetRecipeColor = function(recipeListItem)
  local recipeColor = colors.WHITE
  if recipeListItem ~= nil then
    local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
    local primaryRecipeItem = RecipeListItem_GetPrimaryItem(recipeListItem)
    if RecipeIsForSellRune(recipeName) then
      local runeID = runeUtil.GetRuneInfoRuneID(primaryRecipeItem)
      recipeColor = runeUtil.GetRuneColorFromID(runeID)
    elseif RecipeIsForRunicAttack(recipeName) or RecipeIsForSellArtifact(recipeName) or GetReinforcementWeapon(recipeName) ~= nil or RecipeIsforQuiver(recipeName) then
      recipeColor = nil
    else
      local primaryItemName = GetRecipeItemName(primaryRecipeItem)
      local rarity = resourceUtil.GetRarity(primaryItemName)
      if rarity == nil then
        recipeColor = nil
      else
        recipeColor = resourceUtil.GetRarityColor(primaryItemName)
      end
    end
  end
  return recipeColor
end
local RecipeListItem_AtLimit = function(recipeListItem)
  local atLimit = false
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  if IsValidRecipeName(recipeName) and not HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) then
    atLimit = AtLimit(recipeName)
  end
  return atLimit
end
local RecipeListItem_ShouldUpdateAttributes = function(recipeListItem)
  local shouldUpdateAttributes = false
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  if not RecipeIsForSellRune(recipeName) then
    local primaryRecipeItem = RecipeListItem_GetPrimaryItem(recipeListItem)
    local primaryItemType = GetRecipeItemType(primaryRecipeItem)
    local isPrimaryItemPickup = primaryItemType == resourceConsts.RESOURCE_FLAG_PICKUP
    shouldUpdateAttributes = IsReinforcementRecipe(recipeName) or isPrimaryItemPickup
  end
  return shouldUpdateAttributes
end
local RecipeListItem_IsEquipped = function(recipeListItem)
  local isEquipped = false
  local pickupName
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  if IsReinforcementRecipe(recipeName) then
    pickupName = RecipeListItem_GetReinforcementPickupName(recipeListItem)
  elseif not RecipeIsForSellRune(recipeName) then
    local primaryRecipeItem = RecipeListItem_GetPrimaryItem(recipeListItem)
    local primaryItemType = GetRecipeItemType(primaryRecipeItem)
    local isPrimaryItemPickup = primaryItemType == resourceConsts.RESOURCE_FLAG_PICKUP
    local recipeIsForRunicAttack = RecipeIsForRunicAttack(recipeName)
    if isPrimaryItemPickup or recipeIsForRunicAttack then
      pickupName = GetRecipeItemName(primaryRecipeItem)
    end
  end
  if pickupUtil.IsValidName(pickupName) then
    isEquipped = characterUtil.ItemIsEquipped(pickupName)
  end
  return isEquipped
end
local RecipeListItem_RevealRecipeInputs = function(recipeListItem)
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  if not RecipeIsForSell(recipeName) and IsNew(recipeName) then
    local inputs = RecipeListItem_GetInputRecipeItems(recipeListItem)
    for _, input in pairs(inputs) do
      local recipeItemName = GetRecipeItemName(input)
      if GetRecipeItemType(input) == resourceConsts.RESOURCE_FLAG_RESOURCE and not pickupUtil.Exists(recipeItemName) then
        Wallets.AddResource(resourceConsts.WALLET_KRATOS, recipeItemName, 0)
      end
    end
  end
end
local RecipeListItem_GetEffectIcon = function(currState, recipeListItem)
  local effectFlag
  local effectFlagIcon = ""
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  local primaryItem = RecipeListItem_GetPrimaryItem(recipeListItem)
  local upgradePickupName = RecipeListItem_GetReinforcementPickupName(recipeListItem)
  if RecipeIsForSellRune(recipeName) then
    local runeID = runeUtil.GetRuneInfoRuneID(primaryItem)
    effectFlag = runeUtil.GetEffectFlagFromID(runeID)
  elseif upgradePickupName ~= nil then
    effectFlag = resourceUtil.GetEffectFlag(currState.lastEnteredCategorySelection, upgradePickupName)
  else
    local primaryItemName = GetRecipeItemName(primaryItem)
    effectFlag = resourceUtil.GetEffectFlag(currState.lastEnteredCategorySelection, primaryItemName)
  end
  if effectFlag ~= nil then
    effectFlagIcon = iconConsts[effectFlag]
  end
  return effectFlagIcon
end
local AllRecipesTables_GetRecipeTable = function(subStateSelection, mainSelection, categorySelection)
  local allFieldsValid = not util.IsStringNilOrEmpty(subStateSelection) and not util.IsStringNilOrEmpty(mainSelection) and not util.IsStringNilOrEmpty(categorySelection)
  assert(allFieldsValid, "AllRecipesTables_GetRecipeTable could not find table for [" .. tostring(subStateSelection) .. "][" .. tostring(mainSelection) .. "][" .. tostring(categorySelection) .. "]")
  assert(allRecipesTables[subStateSelection], "allRecipesTables[" .. tostring(subStateSelection) .. "] does not exist")
  assert(allRecipesTables[subStateSelection][mainSelection], "allRecipesTables[" .. tostring(subStateSelection) .. "][" .. tostring(mainSelection) .. "] does not exist")
  assert(allRecipesTables[subStateSelection][mainSelection][categorySelection], "allRecipesTables[" .. tostring(subStateSelection) .. "][" .. tostring(mainSelection) .. "][" .. tostring(categorySelection) .. "] does not exist")
  return allRecipesTables[subStateSelection][mainSelection][categorySelection]
end
local AllRecipesTables_RefreshSingle = function(vendorWallet, subStateSelection, mainSelection, categorySelection, sortingGroup, removeUnavailableRecipes)
  if subStateSelection == "LostItems" then
    return
  end
  tablex.Clear(allRecipesTables[subStateSelection][mainSelection][categorySelection])
  if sortingGroup == nil then
    sortingGroup = resourceConsts.SORTING_GROUPS[resourceConsts.SORTING_GROUP_RARITY_STATS][1]
  end
  if subStateSelection == resourceConsts.RESOURCE_FLAG_SELL and categorySelection == resourceConsts.RESOURCE_FLAG_RUNE then
    local excludeSocketedRunes = true
    local pickupName, currSocketIndex
    allRecipesTables[subStateSelection][mainSelection][categorySelection] = runeUtil.GetRunes(excludeSocketedRunes, pickupName, currSocketIndex, sortingGroup)
    RemoveUnavailableRunes(allRecipesTables[subStateSelection][mainSelection][categorySelection])
  else
    local includeTypes = {subStateSelection, categorySelection}
    local excludeTypes = GetRecipeExcludeTypes(vendorWallet, categorySelection)
    local categoryExtraIncludeFlags = GetCategoryExtraIncludeFlags(mainSelection, categorySelection)
    if categoryExtraIncludeFlags ~= nil then
      for _, flag in ipairs(categoryExtraIncludeFlags) do
        tablex.FastInsert(includeTypes, flag, #includeTypes + 1)
      end
    end
    if subStateSelection == resourceConsts.RESOURCE_FLAG_SELL then
      allRecipesTables[subStateSelection][mainSelection][categorySelection] = GetRecipesWithAllFlags_NGPSpecial(resourceConsts.WALLET_KRATOS, includeTypes, excludeTypes, sortingGroup)
    else
      allRecipesTables[subStateSelection][mainSelection][categorySelection] = GetRecipesWithAllFlags(resourceConsts.WALLET_KRATOS, includeTypes, excludeTypes, sortingGroup)
    end
    if removeUnavailableRecipes == true then
      RemoveUnavailableRecipes(allRecipesTables[subStateSelection][mainSelection][categorySelection])
    end
  end
end
local AllRecipesTables_RefreshSubState = function(vendorWallet, subStateSelection, sortingGroup)
  for _, mainSelection in ipairs(resourceConsts.RECIPE_SUBMENUS_MAIN_SELECTIONS[subStateSelection]) do
    for _, categorySelection in ipairs(resourceConsts.RECIPE_SUBMENUS_CATEGORY_SELECTIONS[mainSelection]) do
      local removeUnavailableRecipes = true
      AllRecipesTables_RefreshSingle(vendorWallet, subStateSelection, mainSelection, categorySelection, sortingGroup, removeUnavailableRecipes)
    end
  end
end
local AllRecipesTables_RefreshAll = function(vendorWallet, isVendorMenu, sortingGroup)
  for _, subStateSelection in ipairs(consts.SUB_STATES.Recipes) do
    local isVendorOnlySubState = IsVendorOnlySelection(subStateSelection)
    if isVendorMenu or not isVendorMenu and not isVendorOnlySubState then
      AllRecipesTables_RefreshSubState(vendorWallet, subStateSelection, sortingGroup)
    end
  end
end
local AllRecipesTables_HasRecipes_CategorySelection = function(subStateSelection, mainSelection, categorySelection)
  return 0 < #allRecipesTables[subStateSelection][mainSelection][categorySelection]
end
local AllRecipesTables_HasRecipes_MainSelection = function(subStateSelection, mainSelection)
  local hasRecipes = false
  for _, categorySelection in ipairs(resourceConsts.RECIPE_SUBMENUS_CATEGORY_SELECTIONS[mainSelection]) do
    if AllRecipesTables_HasRecipes_CategorySelection(subStateSelection, mainSelection, categorySelection) then
      hasRecipes = true
      break
    end
  end
  return hasRecipes
end
local _internalStreamRecipe = function(character, recipeName, PrestreamFunction)
  if recipeName ~= nil then
    local pickupName
    if game.Recipes.GetPrimaryInput_Type_Name ~= nil then
      local type, name
      if HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) then
        type, name = game.Recipes.GetPrimaryInput_Type_Name(recipeName)
      else
        type, name = game.Recipes.GetPrimaryOutput_Type_Name(recipeName)
      end
      if type ~= nil then
        if type == "Pickup" then
          pickupName = name
        elseif type == "Resource" then
          pickupName = GetReinforcementRecipePickup(recipeName)
        end
      end
    else
      local primaryItem = HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) and game.Recipes.GetPrimaryInput(recipeName) or game.Recipes.GetPrimaryOutput(recipeName)
      if primaryItem ~= nil then
        if primaryItem.Type == "Pickup" then
          pickupName = primaryItem.Name
        elseif primaryItem.Type == "Resource" then
          pickupName = GetReinforcementRecipePickup(recipeName)
        end
      end
    end
    if pickupName ~= nil then
      PrestreamFunction(character, pickupName)
    end
  end
end
local AllRecipesTables_PreStreamInitialSelection = function(subStateSelection, PrestreamFunction)
  local mainSelectionsCount = #resourceConsts.RECIPE_SUBMENUS_MAIN_SELECTIONS[subStateSelection]
  for i = 1, mainSelectionsCount do
    local mainSelection = resourceConsts.RECIPE_SUBMENUS_MAIN_SELECTIONS[subStateSelection][i]
    local categoryCount = #resourceConsts.RECIPE_SUBMENUS_CATEGORY_SELECTIONS[mainSelection]
    for j = 1, categoryCount do
      local categorySelection = resourceConsts.RECIPE_SUBMENUS_CATEGORY_SELECTIONS[mainSelection][j]
      if categorySelection ~= resourceConsts.RESOURCE_FLAG_RUNE and categorySelection ~= resourceConsts.RESOURCE_FLAG_RESURRECTION_STONE and categorySelection ~= resourceConsts.RESOURCE_FLAG_ARTIFACT and categorySelection ~= resourceConsts.RESOURCE_FLAG_RESOURCE then
        local categoryTable = allRecipesTables[subStateSelection][mainSelection][categorySelection]
        local character = IsSonSelection(categorySelection) and pickupConsts.TAG_PICKUP_SON or pickupConsts.TAG_PICKUP_KRATOS
        _internalStreamRecipe(character, categoryTable[1], PrestreamFunction)
        _internalStreamRecipe(character, categoryTable[2], PrestreamFunction)
        _internalStreamRecipe(character, categoryTable[#categoryTable], PrestreamFunction)
      end
    end
  end
end
local SubmenuList_GetItems = function(isVendorMenu)
  local subStateNames = {}
  for _, substateName in ipairs(consts.SUB_STATES.Recipes) do
    local isVendorOnlyState = IsVendorOnlySelection(substateName)
    if isVendorMenu or not isVendorMenu and not isVendorOnlyState then
      tablex.FastInsert(subStateNames, substateName, #subStateNames + 1)
    end
  end
  return subStateNames
end
local MainList_GetItems = function(subStateSelection)
  assert(subStateSelection ~= nil, "No sub state index item passed into MainList_GetItems!")
  local mainListItems = {}
  for _, mainSelection in ipairs(resourceConsts.RECIPE_SUBMENUS_MAIN_SELECTIONS[subStateSelection]) do
    local isBladesSelection = IsBladesSelection(mainSelection)
    local hasBlades = resourceUtil.HasResource(pickupConsts.Blades)
    local isSonSelection = IsSonSelection(mainSelection)
    local isMenuSelectionAvailable = util.IsMenuSelectionAvailable(isBladesSelection, hasBlades, isSonSelection)
    local selectionHasRecipes = AllRecipesTables_HasRecipes_MainSelection(subStateSelection, mainSelection)
    if isMenuSelectionAvailable and selectionHasRecipes then
      tablex.FastInsert(mainListItems, mainSelection, #mainListItems + 1)
    end
  end
  return mainListItems
end
local CategoryList_GetItems = function(subStateSelection, mainSelection)
  assert(subStateSelection ~= nil, "No sub state selection passed into CategoryList_GetItems!")
  local categoryListItems = {}
  if mainSelection ~= nil and resourceConsts.RECIPE_SUBMENUS_CATEGORY_SELECTIONS[mainSelection] ~= nil then
    for _, categorySelection in ipairs(resourceConsts.RECIPE_SUBMENUS_CATEGORY_SELECTIONS[mainSelection]) do
      local isBladesSelection = IsBladesSelection(categorySelection)
      local hasBlades = resourceUtil.HasResource(pickupConsts.Blades)
      local isSonSelection = IsSonSelection(categorySelection)
      local isMenuSelectionAvailable = util.IsMenuSelectionAvailable(isBladesSelection, hasBlades, isSonSelection)
      local selectionHasRecipes = AllRecipesTables_HasRecipes_CategorySelection(subStateSelection, mainSelection, categorySelection)
      if isMenuSelectionAvailable and selectionHasRecipes then
        tablex.FastInsert(categoryListItems, categorySelection, #categoryListItems + 1)
      end
    end
  end
  return categoryListItems
end
local GetCategoryIncludeFlag = function(subStateSelection, mainSelection, categorySelection)
  local categoryIncludeFlag = categorySelection
  local categoryItems = CategoryList_GetItems(subStateSelection, mainSelection)
  if #categoryItems == 1 then
    categoryIncludeFlag = categoryItems[1]
  end
  return categoryIncludeFlag
end
local SubList_GetItems = function(vendorWallet, subStateSelection, mainSelection, categorySelection)
  local recipeListItems = {}
  local currRecipeTable = AllRecipesTables_GetRecipeTable(subStateSelection, mainSelection, categorySelection)
  if subStateSelection == resourceConsts.RESOURCE_FLAG_SELL and categorySelection == resourceConsts.RESOURCE_FLAG_RUNE then
    for _, runeID in ipairs(currRecipeTable) do
      local runeInfo = runeUtil.GetRuneInfo(runeID)
      local runeSellRecipe = runeUtil.GetRuneInfoSellRecipe(runeInfo)
      local sellRecipeInputs = GetInputRecipeItems(runeSellRecipe)
      local reinforcementPickupName
      local newListItem = RecipeListItem_CreateItem(runeSellRecipe, sellRecipeInputs, runeInfo, reinforcementPickupName)
      tablex.FastInsert(recipeListItems, newListItem, #recipeListItems + 1)
    end
  else
    for _, recipeName in ipairs(currRecipeTable) do
      local inputRecipeItems = GetInputRecipeItems(recipeName)
      local outputRecipeItems = GetOutputRecipeItems(recipeName)
      local primaryItem = HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_SELL) and inputRecipeItems[1] or outputRecipeItems[1]
      local primaryItemName = GetRecipeItemName(primaryItem)
      local reinforcementPickupName
      if IsReinforcementRecipe(recipeName) then
        reinforcementPickupName = GetReinforcementRecipePickup(recipeName, inputRecipeItems)
      end
      local newListItem = RecipeListItem_CreateItem(recipeName, inputRecipeItems, primaryItem, reinforcementPickupName)
      tablex.FastInsert(recipeListItems, newListItem, #recipeListItems + 1)
    end
  end
  return recipeListItems
end
local GetCurrentCategorySelection = function(currState)
  local mainList = currState.menu:GetList("MainList")
  local currMainSelection = mainList:GetSelectedItem()
  local categoryList = currState.menu:GetList("CategoryList")
  local currCategorySelection = categoryList:GetSelectedItem()
  local categoryItems = CategoryList_GetItems(currState.subStateIndex, currMainSelection)
  if #categoryItems == 1 then
    currCategorySelection = categoryItems[1]
  end
  return currCategorySelection
end
local GetCurrentCharacterByCategorySelection = function(currState)
  local currCategorySelection = GetCurrentCategorySelection(currState)
  local isSonSelection = IsSonSelection(currCategorySelection)
  return isSonSelection and pickupConsts.TAG_PICKUP_SON or pickupConsts.TAG_PICKUP_KRATOS
end
local Category_AnyNewInside = function(vendorWallet, subStateSelection, mainSelection, categorySelection)
  local anyNew = false
  if Wallets.GetAnyRecipeWithFlagsHasNotification ~= nil then
    local includeTypes = {subStateSelection}
    local excludeTypes = GetRecipeExcludeTypes(vendorWallet, categorySelection)
    tablex.FastInsert(excludeTypes, resourceConsts.RESOURCE_FLAG_SELL, #excludeTypes + 1)
    local categoryIncludeFlag = GetCategoryIncludeFlag(subStateSelection, mainSelection, categorySelection)
    tablex.FastInsert(includeTypes, categoryIncludeFlag, #includeTypes + 1)
    local categoryExtraIncludeFlags = GetCategoryExtraIncludeFlags(mainSelection, categorySelection)
    if categoryExtraIncludeFlags ~= nil then
      for _, flag in ipairs(categoryExtraIncludeFlags) do
        tablex.FastInsert(includeTypes, flag, #includeTypes + 1)
      end
    end
    anyNew = Wallets.GetAnyRecipeWithFlagsHasNotification(resourceConsts.WALLET_KRATOS, includeTypes, excludeTypes)
  end
  return anyNew
end
local CategoryList_Button_AnyNewInside = function(currState, button)
  local anyNew = false
  local currentItem = button:get_item()
  if currentItem ~= nil then
    local mainList = currState.menu:GetList("MainList")
    anyNew = Category_AnyNewInside(currState:GetRefreshWallet(), currState.subStateIndex, mainList:GetSelectedItem(), currentItem)
  end
  return anyNew
end
local Main_AnyNewInside = function(vendorWallet, subStateSelection, mainSelection)
  local anyNew = false
  if subStateSelection == "LostItems" then
    if Wallets.GetAnyHasNotification ~= nil then
      anyNew = Wallets.GetAnyHasNotification(resourceConsts.WALLET_LOST_ITEMS)
    end
    return anyNew
  end
  local categorySelections = CategoryList_GetItems(subStateSelection, mainSelection)
  for _, categorySelection in ipairs(categorySelections) do
    if Category_AnyNewInside(vendorWallet, subStateSelection, mainSelection, categorySelection) then
      anyNew = true
      break
    end
  end
  return anyNew
end
local MainList_Button_AnyNewInside = function(currState, button)
  local anyNew = false
  local currentItem = button:get_item()
  if currentItem ~= nil then
    anyNew = Main_AnyNewInside(currState:GetRefreshWallet(), currState.subStateIndex, currentItem)
  end
  return anyNew
end
local Submenu_AnyNewInside = function(vendorWallet, subStateSelection)
  local anyNew = false
  local mainSelections = MainList_GetItems(subStateSelection)
  for _, mainSelection in ipairs(mainSelections) do
    if Main_AnyNewInside(vendorWallet, subStateSelection, mainSelection) then
      anyNew = true
      break
    end
  end
  return anyNew
end
local SubmenuList_Button_AnyNewInside = function(currState, button, isVendorMenu)
  local anyNew = false
  local currentItem = button:get_item()
  if currentItem ~= nil then
    anyNew = Submenu_AnyNewInside(currState:Submenu_GetRefreshWallet(), currentItem)
  end
  return anyNew
end
local RecipesMenu_AnyNewInside = function()
  local anyNew = false
  local isVendorMenu = false
  local subStateSelections = SubmenuList_GetItems(isVendorMenu)
  local vendorWallet
  for _, subStateSelection in ipairs(subStateSelections) do
    if Submenu_AnyNewInside(vendorWallet, subStateSelection) then
      anyNew = true
      break
    end
  end
  return anyNew
end
local ResetListSelection = function(currState, listName)
  if currState.listsToReset[listName] == true then
    local currList = currState.menu:GetList(listName)
    if currList ~= nil then
      local targetButtonIndex = 1
      local useOnGainFocus = false
      currList:SetSelectedButton(targetButtonIndex, useOnGainFocus)
    end
    currState.listsToReset[listName] = false
  end
end
local AssembleTriggerName = function(action, mainSelection, categorySelection)
  local triggerName = "Trigger_" .. mainSelection
  if not util.IsStringNilOrEmpty(action) then
    triggerName = triggerName .. "_" .. action
  end
  if not util.IsStringNilOrEmpty(categorySelection) then
    triggerName = triggerName .. "_" .. categorySelection
  end
  return triggerName
end
local SendTriggerEvent = function(vendorWallet, action, mainSelection, categorySelection, ...)
  if vendorWallet ~= nil then
    local triggerName = AssembleTriggerName(action, mainSelection, categorySelection)
    util.CallScriptOnGameObject("vendor", triggerName, ...)
  end
end
local SendTriggerEvent_Submenu = function(vendorWallet, action, subStateSelection)
  SendTriggerEvent(vendorWallet, action, subStateSelection, nil)
end
local SendTriggerEvent_Category = function(currState, action)
  local categorySelection = GetCurrentCategorySelection(currState)
  SendTriggerEvent(currState.vendorWallet, action, currState.subStateIndex, categorySelection)
end
local SendTriggerEvent_Craft = function(currState, pickupName)
  local categorySelection = GetCurrentCategorySelection(currState)
  SendTriggerEvent(currState.vendorWallet, nil, currState.subStateIndex, categorySelection, pickupName)
end
local SubmenuList_IsItemFocusable = function(subStateIndex)
  return true
end
local SubmenuList_GetDisplayName = function(subStateIndex, isVendorMenu)
  local subStateName = GetSubMenuNameFromSubStateIndex(subStateIndex, isVendorMenu)
  return util.GetLAMSMsg(lamsConsts[subStateName])
end
local SubmenuList_Button_Update = function(currState, button, isVendorMenu)
  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(button:get_item())
  button:UpdateNewIcon(function(button)
    return SubmenuList_Button_AnyNewInside(currState, button, isVendorMenu)
  end)
end
local SubmenuList_Button_OnGainFocus = function(currState, button, listName, isVendorMenu)
  local currentItem = button:get_item()
  local subStateName = GetSubMenuNameFromSubStateIndex(currentItem, isVendorMenu)
  local subState = currState:GetState(subStateName)
  local labelText = SubmenuList_GetDisplayName(currentItem, isVendorMenu)
  currState.menu:SetSubmenuListLabelText(listName, labelText)
  subState.listsToReset.MainList = true
  subState.listsToReset.CategoryList = true
  subState.listsToReset.SubList = true
  subState.menu:set_instructionEntries(currState.menu:get_instructionEntries())
  currState:Goto(subStateName)
end
local SubmenuList_Button_OnLoseFocus = function(currState, button, listName, isVendorMenu)
end
local MainList_IsItemFocusable = function(mainSelection)
  local isBladesSelection = IsBladesSelection(mainSelection)
  local hasBlades = resourceUtil.HasResource(pickupConsts.Blades)
  local isSonSelection = IsSonSelection(mainSelection)
  return util.IsMenuSelectionFocusable(isBladesSelection, hasBlades, isSonSelection)
end
local MainList_GetDisplayName = function(mainListItem)
  local lamsIndex = "Recipes" .. mainListItem
  return util.GetLAMSMsg(lamsConsts[lamsIndex])
end
local MainList_Button_UpdateEquippedIcon = function(button)
  return false
end
local MainList_Button_Update = function(currState, 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:SetTextColor(colors.WHITE)
  button:SetText("text_leftAligned", "")
  button:SetText("text_rightAligned", "")
  button:SetText("icon_text", "")
  buttonUtil.UpdateColorQuad(button, nil)
  button:UpdateNewIcon(function(button)
    return MainList_Button_AnyNewInside(currState, button)
  end)
  button:UpdateEquippedIcon(MainList_Button_UpdateEquippedIcon)
end
local MainList_Button_OnLoseFocus = function(currState, button)
  if currState.MainList_Button_OnLoseFocus ~= nil then
    currState:MainList_Button_OnLoseFocus(button)
  end
end
local CategoryList_IsItemFocusable = function(categorySelection)
  local isBladesSelection = IsBladesSelection(categorySelection)
  local hasBlades = resourceUtil.HasResource(pickupConsts.Blades)
  local isSonSelection = IsSonSelection(categorySelection)
  return util.IsMenuSelectionFocusable(isBladesSelection, hasBlades, isSonSelection)
end
local CategoryList_GetDisplayName = function(categorySelection)
  local lamsIndex = "Recipes" .. categorySelection
  return util.GetLAMSMsg(lamsConsts[lamsIndex])
end
local CategoryList_Button_UpdateEquippedIcon = function(button)
  return false
end
local CategoryList_Button_Update = function(currState, 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:SetTextColor(colors.WHITE)
  button:SetText("text_leftAligned", "")
  button:SetText("text_rightAligned", "")
  button:SetText("icon_text", "")
  buttonUtil.UpdateColorQuad(button, nil)
  button:UpdateNewIcon(function(button)
    return CategoryList_Button_AnyNewInside(currState, button)
  end)
  button:UpdateEquippedIcon(CategoryList_Button_UpdateEquippedIcon)
end
local CategoryList_Button_OnLoseFocus = function(currState, button)
end
local SubList_IsItemFocusable = function(recipeListItem)
  return true
end
local SubList_GetDisplayName = function(recipeListItem)
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  local primaryItem = RecipeListItem_GetPrimaryItem(recipeListItem)
  if RecipeIsForSellRune(recipeName) then
    return runeUtil.GetRuneInfoDisplayName(primaryItem)
  elseif IsReinforcementRecipe(recipeName) then
    local pickupName = RecipeListItem_GetReinforcementPickupName(recipeListItem)
    local displayName = pickupUtil.GetDisplayName(pickupName)
    return displayName
  else
    return RecipeListItem_GetDisplayName(recipeListItem)
  end
end
local SubList_Button_UpdateNewIcon = function(button)
  local recipeListItem = button:get_item()
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  return IsNew(recipeName) and not RecipeIsForSell(recipeName)
end
local SubList_Button_UpdateEquippedIcon = function(button)
  local recipeListItem = button:get_item()
  return RecipeListItem_IsEquipped(recipeListItem)
end
local SubList_Button_Update = function(currState, button)
  local recipeListItem = button:get_item()
  local buttonText = button:get_text()
  local isFocusable = button:get_focusable()
  local alphaValue = isFocusable and consts.FOCUSABLE_ALPHA or consts.NON_FOCUSABLE_ALPHA
  local fadeTime = 0
  button:AlphaFade(alphaValue, fadeTime)
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  local primaryItem = RecipeListItem_GetPrimaryItem(recipeListItem)
  local canBeMade = currState:CanRecipeBeMade(recipeListItem)
  local showAmount = not (RecipeIsForBuy(recipeName) or RecipeIsForSell(recipeName)) or HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_RESOURCE) or HasFlag(recipeName, resourceConsts.RESOURCE_FLAG_ARTIFACT)
  local regularText = buttonText
  local leftAlignedText = ""
  local rightAlignedText = ""
  if not canBeMade then
    regularText = util.StyleText(buttonText, "Color_Gray15")
  end
  if showAmount then
    local primaryItemName = GetRecipeItemName(primaryItem)
    local amount = resourceUtil.GetAmount(primaryItemName)
    leftAlignedText = regularText
    regularText = ""
    rightAlignedText = canBeMade and tostring(amount) or util.StyleText(tostring(amount), "Color_Gray15")
  end
  button:SetText("text", regularText)
  button:SetText("text_leftAligned", leftAlignedText)
  button:SetText("text_rightAligned", rightAlignedText)
  local effectFlagIcon = RecipeListItem_GetEffectIcon(currState, recipeListItem)
  button:SetText("icon_text", effectFlagIcon)
  local recipeColor = RecipeListItem_GetRecipeColor(recipeListItem)
  buttonUtil.UpdateColorQuad(button, recipeColor)
  if recipeName ~= nil then
    button:UpdateNewIcon(SubList_Button_UpdateNewIcon)
    button:UpdateEquippedIcon(SubList_Button_UpdateEquippedIcon)
  end
end
local SubList_Button_OnLoseFocus = function(currState, button)
  local recipeListItem = button:get_item()
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  if currState.cardManager then
    currState.cardManager:UpdateHoldFill(nil, nil)
  end
  if recipeName ~= nil then
    RecipeListItem_RevealRecipeInputs(recipeListItem)
    ClearNew(recipeName)
    button:UpdateNewIcon(SubList_Button_UpdateNewIcon)
  end
end
local SubList_Button_ItemCompare = function(item, otherItem)
  if type(item) == "number" or type(otherItem) == "number" then
    local a = item
    local b = otherItem
    if type(item) ~= "number" then
      local runeInfo = RecipeListItem_GetPrimaryItem(item)
      a = runeUtil.GetRuneInfoRuneID(runeInfo)
    end
    if type(otherItem) ~= "number" then
      local runeInfo = RecipeListItem_GetPrimaryItem(otherItem)
      b = runeUtil.GetRuneInfoRuneID(runeInfo)
    end
    return a == b
  elseif type(item) == "string" or type(otherItem) == "string" then
    local a = item
    local b = otherItem
    if type(item) ~= "string" then
      a = RecipeListItem_GetRecipeName(item)
    end
    if type(otherItem) ~= "string" then
      b = RecipeListItem_GetRecipeName(otherItem)
    end
    return a == b
  else
    local a = RecipeListItem_GetRecipeName(item)
    local b = RecipeListItem_GetRecipeName(otherItem)
    local reinforcementPickupA = RecipeListItem_GetReinforcementPickupName(item)
    local reinforcementPickupB = RecipeListItem_GetReinforcementPickupName(otherItem)
    if reinforcementPickupA ~= nil then
      a = reinforcementPickupA
    else
      local primaryItemA = RecipeListItem_GetPrimaryItem(item)
      local runeIdA = runeUtil.GetRuneInfoRuneID(primaryItemA)
      if runeIdA ~= nil then
        a = runeIdA
      end
    end
    if reinforcementPickupB ~= nil then
      b = reinforcementPickupB
    else
      local primaryItemB = RecipeListItem_GetPrimaryItem(otherItem)
      local runeIdB = runeUtil.GetRuneInfoRuneID(primaryItemB)
      if runeIdB ~= nil then
        b = runeIdB
      end
    end
    return a == b
  end
end
local SubList_GetSortItem = function(button)
  local recipeListItem = button:get_item()
  local recipeName = RecipeListItem_GetRecipeName(recipeListItem)
  local sortItem = recipeName
  if RecipeIsForSellRune(recipeName) then
    local runeInfo = RecipeListItem_GetPrimaryItem(recipeListItem)
    sortItem = runeUtil.GetRuneInfoRuneID(runeInfo)
  end
  return sortItem
end
local SubList_Sort_Recipes = function(recipesTable, sortingGroup)
  Wallets.SortRecipeList(resourceConsts.WALLET_KRATOS, recipesTable, sortingGroup)
end
local SubList_Sort_Runes = function(runeIDTable, sortingGroup)
  if sortingGroup == resourceConsts.POWER_LEVEL_SORTING then
    local tiers = {
      "Tier09",
      "Tier08",
      "Tier07",
      "Tier06",
      "Tier05",
      "Tier04",
      "Tier03",
      "Tier02",
      "Tier01"
    }
    Wallets.SortRuneList(resourceConsts.WALLET_KRATOS, runeIDTable, tiers)
  end
  Wallets.SortRuneList(resourceConsts.WALLET_KRATOS, runeIDTable, sortingGroup)
end
local SubmenuList_UpdateSingle = function(currState, subStateSelection)
  local submenuListName = currState.menu:get_submenuListName()
  local submenuList = currState.menu:GetList(submenuListName)
  submenuList:UpdateButtonByItem(subStateSelection)
end
local SubmenuList_Refresh = function(currState, isVendorMenu)
  local submenuListName = currState.menu:get_submenuListName()
  local submenuList = currState.menu:GetList(submenuListName)
  local newItemArray = SubmenuList_GetItems(isVendorMenu)
  local showList = true
  local useOnGainFocus = not currState.menu:HasInstructionEntryForMenuState()
  local itemDetermineFocusabilityFunc = SubmenuList_IsItemFocusable
  local getDisplayNameFunc = function(subStateIndex)
    return SubmenuList_GetDisplayName(subStateIndex, isVendorMenu)
  end
  currState.menu:RefreshSubmenuList(submenuList, newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, getDisplayNameFunc)
end
local SubmenuList_Activate = function(currState, subStateSelection, isVendorMenu)
  SubmenuList_UpdateSingle(currState, subStateSelection)
  local submenuListName = currState.menu:get_submenuListName()
  local submenuList = currState.menu:GetList(submenuListName)
  local submenuList_showList = true
  local submenuList_useOnGainFocus = true
  submenuList:Activate(submenuList_showList, submenuList_useOnGainFocus)
end
local SubmenuList_Deactivate = function(currState)
  local submenuListName = currState.menu:get_submenuListName()
  local submenuList = currState.menu:GetList(submenuListName)
  local submenuList_hideList = true
  local submenuList_clearButtons = false
  submenuList:Deactivate(submenuList_hideList, submenuList_clearButtons)
end
local MainList_Refresh = function(currState, resetListSelection)
  local mainList = currState.menu:GetList("MainList")
  if currState.lastEnteredSubStateSelection ~= currState.subStateIndex then
    local newItemArray = MainList_GetItems(currState.subStateIndex)
    local showList = false
    local useOnGainFocus = false
    local itemDetermineFocusabilityFunc = MainList_IsItemFocusable
    local getDisplayNameFunc = MainList_GetDisplayName
    mainList:Refresh(newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, getDisplayNameFunc)
    currState.lastEnteredSubStateSelection = currState.subStateIndex
  end
  if resetListSelection == true then
    ResetListSelection(currState, "MainList")
  end
  local activate_showList = true
  local activate_useOnGainFocus = not currState.menu:HasInstructionEntryForMenuState()
  mainList:Activate(activate_showList, activate_useOnGainFocus)
  mainList:SetHeaderText(SubmenuList_GetDisplayName(currState.subStateIndex, currState.isVendorMenu))
end
local lostResourceCatetories = {
  {
    category = "RecipesArmor",
    flags = {
      "ArmorChest",
      "ArmorLegs",
      "ArmorWrist",
      "ArmorTrinket",
      "ArmorShield",
      "SonArmor"
    }
  },
  {
    category = "RecipesWeaponSpecial_Light",
    flags = {
      "WeaponSpecial_Light"
    }
  },
  {
    category = "RecipesWeaponSpecial_Heavy",
    flags = {
      "WeaponSpecial_Heavy"
    }
  },
  {
    category = "RecipesWeaponComponent",
    flags = {
      "WeaponComponent"
    }
  },
  {
    category = "RecipesWeaponSpecial_Blades_Light",
    flags = {
      "WeaponSpecial_Blades_Light"
    }
  },
  {
    category = "RecipesWeaponSpecial_Blades_Heavy",
    flags = {
      "WeaponSpecial_Blades_Heavy"
    }
  },
  {
    category = "RecipesWeaponComponent_Blades",
    flags = {
      "WeaponComponent_Blades"
    }
  },
  {
    category = "RecipesSonSpecial",
    flags = {"SonSpecial"}
  }
}
local LostItems_Refresh = function(currState, resetListSelection)
  local mainList = currState.menu:GetList("MainList")
  local showList = false
  local useOnGainFocus = false
  local itemDetermineFocusabilityFunc = function(item)
    if item.ButtonType == "category" then
      return false
    end
    return true
  end
  local getDisplayNameFunc = function(item)
    local type = item.Type
    local name = item.Name
    if type == resourceConsts.RESOURCE_FLAG_RESOURCE then
      return resourceUtil.GetDisplayName(name)
    elseif type == resourceConsts.RESOURCE_FLAG_RUNE then
      local runeInfo = Wallets.GetRuneInfo(resourceConsts.WALLET_LOST_ITEMS, name)
      return runeUtil.GetRuneInfoDisplayName(runeInfo)
    end
    return name
  end
  local lostItems = {}
  local lostRunes = game.Wallets.GetAllRunes(resourceConsts.WALLET_LOST_ITEMS, {}, "", resourceConsts.POWER_LEVEL_SORTING)
  game.Wallets.SortRuneList(resourceConsts.WALLET_LOST_ITEMS, lostRunes, resourceConsts.RARITY_SORTING)
  if 0 < #lostRunes then
    lostItems[#lostItems + 1] = {
      ButtonType = "category",
      Name = util.GetLAMSMsg(lamsConsts.RecipesEnchantments)
    }
  end
  for i = 1, #lostRunes do
    lostItems[#lostItems + 1] = {
      Name = lostRunes[i],
      Type = resourceConsts.RESOURCE_FLAG_RUNE
    }
  end
  local lostResources = game.Wallets.GetExistingResources(resourceConsts.WALLET_LOST_ITEMS, resourceConsts.POWER_LEVEL_SORTING)
  game.Wallets.SortResourceList(resourceConsts.WALLET_LOST_ITEMS, lostResources, resourceConsts.RARITY_SORTING)
  for i = #lostResources, 1, -1 do
    if not resourceUtil.HasFlag(lostResources[i], resourceConsts.RESOURCE_FLAG_RESOURCE) and not resourceUtil.HasFlag(lostResources[i], resourceConsts.RESOURCE_FLAG_PICKUP) and not resourceUtil.HasFlag(lostResources[i], resourceConsts.RESOURCE_FLAG_QUEST_ITEM) then
      table.remove(lostResources, i)
    end
  end
  for i = 1, #lostResourceCatetories do
    local category = lostResourceCatetories[i].category
    local flags = lostResourceCatetories[i].flags
    local categoryHeader = false
    local resourceIndex = 1
    while resourceIndex <= #lostResources do
      local resource = lostResources[resourceIndex]
      local flagged = false
      for flagIndex = 1, #flags do
        if Resources.HasFlag(resource, flags[flagIndex]) then
          flagged = true
          break
        end
      end
      if flagged then
        if not categoryHeader then
          categoryHeader = true
          lostItems[#lostItems + 1] = {
            ButtonType = "category",
            Name = util.GetLAMSMsg(lamsConsts[category])
          }
        end
        lostItems[#lostItems + 1] = {
          Name = resource,
          Type = resourceConsts.RESOURCE_FLAG_RESOURCE
        }
        table.remove(lostResources, resourceIndex)
      else
        resourceIndex = resourceIndex + 1
      end
    end
  end
  if 0 < #lostResources then
    lostItems[#lostItems + 1] = {
      ButtonType = "category",
      Name = util.GetLAMSMsg(lamsConsts.RecipesResources)
    }
  end
  for i = 1, #lostResources do
    lostItems[#lostItems + 1] = {
      Name = lostResources[i],
      Type = resourceConsts.RESOURCE_FLAG_RESOURCE,
      Category = "Resource"
    }
  end
  if 0 < #lostItems then
    currState.holdFill:Activate(true)
    currState.goLostItemFooter:Show()
  else
    currState.holdFill:Deactivate(true)
    currState.goLostItemFooter:Hide()
  end
  mainList:Refresh(lostItems, showList, useOnGainFocus, itemDetermineFocusabilityFunc, getDisplayNameFunc)
  if resetListSelection == true then
    ResetListSelection(currState, "MainList")
  end
  local activate_showList = true
  local activate_useOnGainFocus = not currState.menu:HasInstructionEntryForMenuState()
  mainList:Activate(activate_showList, activate_useOnGainFocus)
  mainList:SetHeaderText(SubmenuList_GetDisplayName(currState.subStateIndex, currState.isVendorMenu))
  return #lostItems
end
local CategoryList_Refresh = function(currState, currMainSelection)
  local categoryList = currState.menu:GetList("CategoryList")
  local newItemArray = CategoryList_GetItems(currState.subStateIndex, currMainSelection)
  local showList = false
  local useOnGainFocus = false
  local itemDetermineFocusabilityFunc = CategoryList_IsItemFocusable
  local getDisplayNameFunc = CategoryList_GetDisplayName
  categoryList:Refresh(newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, getDisplayNameFunc)
  ResetListSelection(currState, "CategoryList")
  local activate_showList = true
  local activate_useOnGainFocus = not currState.menu:HasInstructionEntryForMenuState()
  categoryList:Activate(activate_showList, activate_useOnGainFocus)
  categoryList:SetHeaderText(MainList_GetDisplayName(currMainSelection))
end
local SubList_Refresh = function(currState, currMainSelection, currCategorySelection, activateList, sortingGroup)
  local subList = currState.menu:GetList("SubList")
  local selectedItem = subList:GetSelectedItem()
  if currState.lastEnteredCategorySelection ~= currCategorySelection then
    local newItemArray = SubList_GetItems(currState.vendorWallet, currState.subStateIndex, currMainSelection, currCategorySelection)
    local showList = false
    local useOnGainFocus = false
    local itemDetermineFocusabilityFunc = SubList_IsItemFocusable
    local getDisplayNameFunc = SubList_GetDisplayName
    subList:Refresh(newItemArray, showList, useOnGainFocus, itemDetermineFocusabilityFunc, getDisplayNameFunc)
    currState.lastEnteredCategorySelection = currCategorySelection
  end
  if sortingGroup ~= nil then
    subList:Sort(sortingGroup, selectedItem, activateList, not activateList)
  end
  if activateList == true then
    local activate_showList = true
    local activate_useOnGainFocus = not currState.menu:HasInstructionEntryForMenuState()
    subList:Activate(activate_showList, activate_useOnGainFocus)
  end
  subList:SetHeaderText(CategoryList_GetDisplayName(currCategorySelection))
end
local SubList_Update = function(currState, sortingGroup)
  local mainList = currState.menu:GetList("MainList")
  local currMainSelection = mainList:GetSelectedItem()
  local categoryList = currState.menu:GetList("CategoryList")
  local currCategorySelection = categoryList:GetSelectedItem()
  local categoryItems = CategoryList_GetItems(currState.subStateIndex, currMainSelection)
  local activateList = false
  if #categoryItems == 1 or currCategorySelection == nil then
    currCategorySelection = currMainSelection
  end
  SubList_Refresh(currState, currMainSelection, currCategorySelection, activateList, sortingGroup)
end
local Category_OnSelect = function(currState, currMainSelection, currCategorySelection)
  SubList_Refresh(currState, currMainSelection, currCategorySelection, true, nil)
  SendTriggerEvent_Category(currState, "Enter")
  local subList = currState.menu:GetList("SubList")
  subList:SetLabelText(SubmenuList_GetDisplayName(currState.subStateIndex, currState.isVendorMenu))
  subList:SetIcon(currState.subStateIndex)
  if currState.subStateIndex == "Sell" and currCategorySelection == resourceConsts.RESOURCE_FLAG_RUNE then
    subList:set_sort(SubList_Sort_Runes)
  else
    subList:set_sort(SubList_Sort_Recipes)
  end
  if currState.Category_OnSelect ~= nil then
    currState:Category_OnSelect(currMainSelection, currCategorySelection)
  end
end
local MainList_AdvanceReleaseHandler = function(subMenuState, currState)
  local mainList = currState.menu:GetList("MainList")
  local currMainSelection = mainList:GetSelectedItem()
  local categoryItems = CategoryList_GetItems(currState.subStateIndex, currMainSelection)
  local categoryItemCount = #categoryItems
  if 0 < categoryItemCount then
    SubmenuList_Deactivate(subMenuState)
    mainList:Deactivate()
    if categoryItemCount == 1 then
      Category_OnSelect(currState, currMainSelection, currMainSelection)
    else
      CategoryList_Refresh(currState, currMainSelection)
    end
    game.Audio.PlaySound("SND_UX_Menu_Tick_Confirm")
  end
end
local CategoryList_AdvanceReleaseHandler = function(currState)
  local mainList = currState.menu:GetList("MainList")
  local currMainSelection = mainList:GetSelectedItem()
  local categoryList = currState.menu:GetList("CategoryList")
  local currCategorySelection = categoryList:GetSelectedItem()
  categoryList:Deactivate()
  Category_OnSelect(currState, currMainSelection, currCategorySelection)
end
local CategoryList_BackReleaseHandler = function(currState)
  local mainList = currState.menu:GetList("MainList")
  local categoryList = currState.menu:GetList("CategoryList")
  categoryList:Deactivate()
  MainList_Refresh(currState, false)
  game.Audio.PlaySound("SND_UX_Menu_Tick_Back")
end
local SubList_BackReleaseHandler = function(currState)
  local mainList = currState.menu:GetList("MainList")
  local currMainSelection = mainList:GetSelectedItem()
  local categoryList = currState.menu:GetList("CategoryList")
  local currCategorySelection = categoryList:GetSelectedItem()
  local subList = currState.menu:GetList("SubList")
  local categoryItems = CategoryList_GetItems(currState.subStateIndex, currMainSelection)
  SendTriggerEvent_Category(currState, "Exit")
  ResetListSelection(currState, "SubList")
  local hide = true
  subList:Deactivate(hide)
  if #categoryItems == 1 or currCategorySelection == nil then
    MainList_Refresh(currState, false)
  else
    CategoryList_Refresh(currState, currMainSelection)
  end
  game.Audio.PlaySound("SND_UX_Menu_Tick_Back")
end
local Submenu_SetupLists = function(currState, listObjectName)
  currState.listsToReset = {
    MainList = true,
    CategoryList = true,
    SubList = true
  }
  local mainListName = currState.menu:get_name() .. "_MainList"
  local mainList = list.List.New(currState, {
    Name = mainListName,
    MaxFocusableObjectCount = 10,
    ListObjectName = listObjectName,
    EmptyTextLamsID = currState.MainList_EmptyText and currState:MainList_EmptyText() or lamsConsts.NoRecipesAvailable,
    HoverToSelect = true,
    EnableLabel = false,
    EventHandlers = {
      {
        Events = {
          "EVT_Advance_Release"
        },
        Handler = function()
          currState:MainList_AdvanceReleaseHandler()
        end
      },
      {
        Events = {
          "EVT_MOUSE_RELEASED"
        },
        Handler = function()
          local selected = UI.GetEventSenderGameObject()
          local list = currState.menu:GetList("MainList")
          local button = list:GetSelectedButton()
          if button ~= nil and selected == button:GetInstancedChildObject() then
            currState:MainList_AdvanceReleaseHandler()
          end
        end
      },
      {
        Events = {
          "EVT_Back_Release",
          "EVT_MOUSE_RIGHT_CLICKED"
        },
        Handler = function()
          currState:MainList_BackReleaseHandler()
        end
      }
    },
    Button_Update = function(button)
      currState:MainList_Button_Update(button)
    end,
    Button_OnGainFocus = function(button)
      currState:MainList_Button_OnGainFocus(button)
    end,
    Button_OnLoseFocus = function(button)
      MainList_Button_OnLoseFocus(currState, button)
    end
  })
  currState.menu:SetList("MainList", mainList)
  tutorialUtil.RegisterDesaturationList(mainListName, mainList)
  local categoryListName = currState.menu:get_name() .. "_CategoryList"
  local categoryList = list.List.New(currState, {
    Name = categoryListName,
    MaxFocusableObjectCount = 10,
    ListObjectName = listObjectName,
    EmptyTextLamsID = lamsConsts.NoRecipesAvailable,
    HoverToSelect = true,
    EventHandlers = {
      {
        Events = {
          "EVT_Advance_Release"
        },
        Handler = function()
          CategoryList_AdvanceReleaseHandler(currState)
        end
      },
      {
        Events = {
          "EVT_Back_Release",
          "EVT_MOUSE_RIGHT_CLICKED"
        },
        Handler = function()
          currState:CategoryList_BackReleaseHandler()
        end
      }
    },
    Button_Update = function(button)
      CategoryList_Button_Update(currState, button)
    end,
    Button_OnGainFocus = function(button)
      currState:CategoryList_Button_OnGainFocus(button)
    end,
    Button_OnLoseFocus = function(button)
      CategoryList_Button_OnLoseFocus(currState, button)
    end
  })
  currState.menu:SetList("CategoryList", categoryList)
  tutorialUtil.RegisterDesaturationList(categoryListName, categoryList)
  local subListName = currState.menu:get_name() .. "_SubList"
  local subList = list.List.New(currState, {
    Name = subListName,
    MaxFocusableObjectCount = 10,
    ListObjectName = listObjectName,
    IconObjectName = "VendorIcon",
    EmptyTextLamsID = lamsConsts.NoRecipesAvailable,
    HoverToSelect = true,
    DelayTime = consts.SingleFrameDelayTime,
    EventHandlers = {
      {
        Events = {
          "EVT_Advance_Release"
        },
        Handler = function(list)
          if currState.SubList_AdvanceReleaseHandler ~= nil then
            currState:SubList_AdvanceReleaseHandler(list)
          end
        end
      },
      {
        Events = {
          "EVT_Back_Release",
          "EVT_MOUSE_RIGHT_CLICKED"
        },
        Handler = function(list)
          currState:SubList_BackReleaseHandler()
        end
      },
      {
        Events = {
          "EVT_R2_Press"
        },
        Handler = function(list)
          currState:SubList_R2PressHandler(list)
        end
      },
      {
        Events = {
          "EVT_R2_Release"
        },
        Handler = function(list)
          currState:SubList_R2ReleaseHandler()
        end
      },
      {
        Events = {
          "EVT_R3_Release"
        },
        Handler = function(list)
          currState:SubList_R3ReleaseHandler(list)
        end
      }
    },
    Button_Update = function(button)
      SubList_Button_Update(currState, button)
    end,
    Button_OnGainFocus = function(button)
    end,
    Button_OnLoseFocus = function(button)
      currState:SubList_Button_OnLoseFocus(button)
    end,
    Button_ItemCompare = function(item, otherItem)
      return SubList_Button_ItemCompare(item, otherItem)
    end,
    Button_GetSortItem = function(button)
      return SubList_GetSortItem(button)
    end,
    AfterDelay = function(list, delayStartButtonIndex, selectedButtonIndex)
      local selectedButton = list:GetButton(selectedButtonIndex)
      currState:SubList_AfterDelay(selectedButton)
    end
  })
  currState.menu:SetList("SubList", subList)
  tutorialUtil.RegisterDesaturationList(subListName, subList)
end
return {
  IsValidRecipeName = IsValidRecipeName,
  GetInputRecipeItems = GetInputRecipeItems,
  GetOutputRecipeItems = GetOutputRecipeItems,
  GetRecipeItemName = GetRecipeItemName,
  GetRecipeItemType = GetRecipeItemType,
  GetRecipeItemLamsNameId = GetRecipeItemLamsNameId,
  GetRecipeItemLamsDescriptionId = GetRecipeItemLamsDescriptionId,
  GetRecipeItemAmount = GetRecipeItemAmount,
  HasFlag = HasFlag,
  GetPrimaryRecipeItem = GetPrimaryRecipeItem,
  IsReinforcementRecipe = IsReinforcementRecipe,
  RecipeIsforQuiver = RecipeIsforQuiver,
  GetReinforcementRecipePickup = GetReinforcementRecipePickup,
  GetDisplayName = GetDisplayName,
  GetDescription = GetDescription,
  GetTier = GetTier,
  GetReinforcementWeapon = GetReinforcementWeapon,
  AtLimit = AtLimit,
  AtPickupTier = AtPickupTier,
  RecipeIsForBuy = RecipeIsForBuy,
  RecipeIsForSell = RecipeIsForSell,
  RecipeIsForSellRune = RecipeIsForSellRune,
  IsRuneCreatorRecipe = IsRuneCreatorRecipe,
  RecipeIsForRunicAttack = RecipeIsForRunicAttack,
  RecipeIsForSellArtifact = RecipeIsForSellArtifact,
  IsNew = IsNew,
  ClearNew = ClearNew,
  IsInWallet = IsInWallet,
  CanPurchase = CanPurchase,
  CanFulfill = CanFulfill,
  Purchase = Purchase,
  Craft = Craft,
  GenerateRunes = GenerateRunes,
  RollCondition = RollCondition,
  GetSubMenuNameFromSubStateIndex = GetSubMenuNameFromSubStateIndex,
  IsVendorOnlySelection = IsVendorOnlySelection,
  IsSonSelection = IsSonSelection,
  IsBladesSelection = IsBladesSelection,
  GetCategoryExtraIncludeFlags = GetCategoryExtraIncludeFlags,
  ShouldHideAttributesCard = ShouldHideAttributesCard,
  GetSortingGroupIndex = GetSortingGroupIndex,
  GetSortingGroupFromSelection = GetSortingGroupFromSelection,
  GetVendorExcludeFlag = GetVendorExcludeFlag,
  GetRecipeExcludeTypes = GetRecipeExcludeTypes,
  RemoveUnavailableRecipes = RemoveUnavailableRecipes,
  RemoveUnavailableRunes = RemoveUnavailableRunes,
  RecipeListItem_CreateItem = RecipeListItem_CreateItem,
  RecipeListItem_GetRecipeName = RecipeListItem_GetRecipeName,
  RecipeListItem_GetInputRecipeItems = RecipeListItem_GetInputRecipeItems,
  RecipeListItem_GetPrimaryItem = RecipeListItem_GetPrimaryItem,
  RecipeListItem_GetReinforcementPickupName = RecipeListItem_GetReinforcementPickupName,
  RecipeListItem_GetDisplayName = RecipeListItem_GetDisplayName,
  RecipeListItem_GetDescription = RecipeListItem_GetDescription,
  RecipeListItem_GetRecipeRarity = RecipeListItem_GetRecipeRarity,
  RecipeListItem_GetRecipeColor = RecipeListItem_GetRecipeColor,
  RecipeListItem_AtLimit = RecipeListItem_AtLimit,
  RecipeListItem_ShouldUpdateAttributes = RecipeListItem_ShouldUpdateAttributes,
  RecipeListItem_IsEquipped = RecipeListItem_IsEquipped,
  RecipeListItem_RevealRecipeInputs = RecipeListItem_RevealRecipeInputs,
  AllRecipesTables_GetRecipeTable = AllRecipesTables_GetRecipeTable,
  AllRecipesTables_RefreshSingle = AllRecipesTables_RefreshSingle,
  AllRecipesTables_RefreshSubState = AllRecipesTables_RefreshSubState,
  AllRecipesTables_RefreshAll = AllRecipesTables_RefreshAll,
  AllRecipesTables_HasRecipes_CategorySelection = AllRecipesTables_HasRecipes_CategorySelection,
  AllRecipesTables_HasRecipes_MainSelection = AllRecipesTables_HasRecipes_MainSelection,
  AllRecipesTables_PreStreamInitialSelection = AllRecipesTables_PreStreamInitialSelection,
  CategoryList_GetItems = CategoryList_GetItems,
  SendTriggerEvent_Submenu = SendTriggerEvent_Submenu,
  SendTriggerEvent_Category = SendTriggerEvent_Category,
  SendTriggerEvent_Craft = SendTriggerEvent_Craft,
  RecipesMenu_AnyNewInside = RecipesMenu_AnyNewInside,
  SubmenuList_GetDisplayName = SubmenuList_GetDisplayName,
  SubmenuList_Button_Update = SubmenuList_Button_Update,
  SubmenuList_Button_OnGainFocus = SubmenuList_Button_OnGainFocus,
  SubmenuList_Button_OnLoseFocus = SubmenuList_Button_OnLoseFocus,
  SubmenuList_UpdateSingle = SubmenuList_UpdateSingle,
  SubmenuList_Refresh = SubmenuList_Refresh,
  SubmenuList_Activate = SubmenuList_Activate,
  SubmenuList_Deactivate = SubmenuList_Deactivate,
  MainList_Refresh = MainList_Refresh,
  MainList_Button_Update = MainList_Button_Update,
  LostItems_Refresh = LostItems_Refresh,
  MainList_AdvanceReleaseHandler = MainList_AdvanceReleaseHandler,
  CategoryList_BackReleaseHandler = CategoryList_BackReleaseHandler,
  SubList_Update = SubList_Update,
  SubList_Button_OnLoseFocus = SubList_Button_OnLoseFocus,
  SubList_BackReleaseHandler = SubList_BackReleaseHandler,
  SubList_Sort_Runes = SubList_Sort_Runes,
  Submenu_SetupLists = Submenu_SetupLists,
  GetCurrentCategorySelection = GetCurrentCategorySelection,
  GetCurrentCharacterByCategorySelection = GetCurrentCharacterByCategorySelection,
  Category_AnyNewInside = Category_AnyNewInside
}
