view Modules/ArtifactPower.lua @ 114:6748c98a6c6c

- Reworked OrderHallHandler triggers - OrderHallHandler minimializes hud info - WorldState currency headers have a unified font
author Nick@Zahhak
date Mon, 27 Mar 2017 00:39:29 -0400
parents 7c77fde36287
children 8c94bee4fdfc
line wrap: on
line source
-- Veneer
-- ArtifactPower.lua
-- Created: 1/15/2017 11:44 PM
-- %file-revision%
--

local print = DEVIAN_WORKSPACE and function(...) print('VnAP', ...) end or nop
VeneerArtifactPowerMixin = {
  numItems = 0,
  Tokens = {},
  cache = {},
  fishingCache = {},
  scanQueue = {},
  ItemButtons = {},
  anchorPoint = 'TOP',
  anchorFrom = 'TOP',
}
local defaultSettings = {
  firstUse = true,
  autoHide = true,
}
local ap = VeneerArtifactPowerMixin
local BAGS_TO_SCAN = {BACKPACK_CONTAINER }
local TOOLTIP_NAME = 'VeneerAPScanner'
local POINT_COSTS = {
  100, 300, 325, 350, 375,
  400, 425, 450, 525, 625,
  750, 875, 1000, 6840, 8830,
  11280, 14400, 18620, 24000, 30600,
  39520, 50880, 64800, 82500, 105280,
  138650, 182780, 240870, 325520, 417560,
  546000, 718200, 946660, 1245840, 1635200,
  191500, 2010000, 2110000, 2215000, 2325000,
  2440000, 2560000, 2690000, 2825000, 2965000,
  3115000, 3270000, 3435000, 3605000, 3785000,
  3975000, 4175000, 4385000, 4605000
}
local FISHING_MAX_TRAITS = 24
local WEAPON_MAX_TRAITS = 54
local BUTTON_SIZE = 48
local FRAME_LIST = {'ContainerFrame1', 'BankFrame'}
local BAG_FRAMES = {'ContainerFrame1'}
local BANK_FRAMES = {'BankFrame'}

function ap:OnLoad()
  self:RegisterEvent('BAG_UPDATE') -- use to obtain bag IDs to scan
  self:RegisterEvent('BAG_UPDATE_DELAYED') -- use to trigger actual scan activity
  self:RegisterEvent('BANKFRAME_OPENED')  -- determine when bank info is available
  self:RegisterEvent('BANKFRAME_CLOSED')  -- " " "
  self:RegisterEvent('ARTIFACT_UPDATE')    -- when artifact data has changed
  self:RegisterEvent('ARTIFACT_XP_UPDATE') -- when artifact xp has changed (but not necessarily data)
  self:RegisterEvent('PLAYER_REGEN_ENABLED')
  self:RegisterEvent('PLAYER_REGEN_DISABLED')
  self:RegisterEvent('PLAYER_ENTERING_WORLD')
  self:RegisterEvent('ITEM_LOCK_CHANGED') -- use to clear bag slot cache data
  Veneer:AddHandler(self, self.anchorPoint, 2)
  SLASH_VENEER_AP1 = "/vap"
  SLASH_VENEER_AP2 = "/veneerap"
  SlashCmdList.VENEER_AP = function(arg)
    if arg == 'fishing' then
      if VeneerData.ArtifactPower.EnableFishing then
        VeneerData.ArtifactPower.EnableFishing = nil
      else
        VeneerData.ArtifactPower.EnableFishing = true
      end
      self:Print('Show Underlight Angler:', (VeneerData.ArtifactPower.EnableFishing and 'ON' or 'OFF'))
      self:Update()
    elseif arg == 'reset' then
      if self.db then
        table.wipe(self.db.cache)
        table.wipe(self.db.fishingCache)
      end
      self:Print('Cache data reset.')
      self:Update()
    else
      self:Show()
    end
  end

  self.tooltip = CreateFrame('GameTooltip', TOOLTIP_NAME, self, 'GameTooltipTemplate')


end
local ShortNumberString = function (value)
  if value >= 100000 then
    return tostring(floor(value/1000)) .. 'k'
  elseif value >= 1000 then
    return tostring(floor(value/100)/10) .. 'k'
  else
    return value
  end
end


local IsBagnonOpen = function()
  return ((BagnonFramebank and BagnonFramebank:IsShown()) or (BagnonFrameinventory and BagnonFrameinventory:IsShown()))
end
local addonCompatibility = {
  ['Bagnon'] = {
    BagFrames = {'BagnonFrameinventory'},
    BankFrames = {'BagnonFramebank'},
    FrameMethods = {
      ['Hide'] = IsBagnonOpen,
      ['Show'] = IsBagnonOpen
    },
    PostHooks = {},
    MethodClass = 'Bagnon',
    MethodHooks = {'BANK_OPENED', 'BANKFRAME_CLOSED'},

  }
}



local queued_hooks = {}
local function CreateHook(...)
  if select('#', ...) >= 2 then
    tinsert(queued_hooks, {...})
  end
  if not InCombatLockdown() then
    local info = tremove(queued_hooks)
    while info do
      --[[local oFunc = tremove(info, #info)
      local args = info

      local func = function(...)
        print('|cFFFF0088Callback:|r', unpack(args))

        oFunc(...)
      end
      print('hooking', unpack(info), oFunc, func)
      hooksecurefunc(unpack(info), func)
      --]]
      hooksecurefunc(unpack(info))
      info = tremove(queued_hooks)
    end

  end
end

local function AddFrameHooks(frame, args)
  for funcName, func in pairs(args.FrameMethods) do
    print('binding', frame:GetName(), funcName, 'to', tostring(func))
    CreateHook(frame, funcName, function()
      print(frame:GetName(), funcName, 'hook')
      VeneerArtifactPower:TryToShow()
    end)
  end
end
local PENDING_HOOKS = {}

local function RegisterInventoryFrame(name, listType, args)
  print('register', name, 'as inventory frame type =', (listType == BAG_FRAMES) and 'bags' or 'bank')
  tinsert(FRAME_LIST, name)
  tinsert(listType, name)
  if _G[name] then
    AddFrameHooks(_G[name], args)
  else
    PENDING_HOOKS[name] = args
  end
end

function ap:Setup()
  print(self:GetName()..':Setup()')
  local guid = UnitGUID('player')
  VeneerData.ArtifactPower = VeneerData.ArtifactPower or defaultSettings
  self.db = VeneerData.ArtifactPower
  self.db[guid] = self.db[guid] or {}
  self.db.cache = self.db.cache or {}
  self.db.fishingCache = self.db.fishingCache or {}

  for i, data in pairs(self.cache) do
    -- bring in anything found before player data is active
    self.db.cache[i] = data
  end
  for i, data in pairs(self.fishingCache) do
    self.db.fishingCache[i] = data
  end


  self.profile = self.db[guid]
  self.profile.cache = self.profile.cache or {}
  self.profile.cache.bagItems = self.profile.cache.bagItems or {}
  self.profile.cache.bags = self.profile.cache.bags or {}
  self.profile.cache.fishing = self.profile.cache.fishing or {}
  self.profile.cache.items = self.profile.cache.items or {}
  self.profile.bagslots = self.profile.bagslots or {}
  self.profile.artifacts = self.profile.artifacts or {}
  self.updateSummary = true
  self.cache = self.profile.cache

  VeneerArtifactPowerTimer:SetScript('OnUpdate', function()
    self:OnUpdate()
  end)

  local DoTryToShow = function()

    self:TryToShow()
  end
  CreateHook("OpenBackpack", DoTryToShow)
  CreateHook("CloseBackpack", DoTryToShow)

  -- Bagnon compatibility
  -- todo: ArkInventory, Elv, etc
  for addon, args in pairs(addonCompatibility) do
    if IsAddOnLoaded(addon) then

      for _, name in ipairs(args.BagFrames) do
        RegisterInventoryFrame(name, BAG_FRAMES, args)
      end
      for _, name in ipairs(args.BankFrames) do
        RegisterInventoryFrame(name, BANK_FRAMES, args)
      end

      -- should only specify non-secure functions in this table
      for _, name in ipairs(args.PostHooks) do
        local oFunc = _G[name]
        print('hook entry', name, tostring(oFunc))
        CreateHook(name, function(...)
          print('|cFFFF0088' .. name .. '|r', ..., 'original', tostring(oFunc))
          oFunc(...)
          self:TryToShow()
        end)
      end
      local frame = _G[args.MethodClass]
      if frame then
        print()
        for _, name in ipairs(args.MethodHooks) do
          CreateHook(frame, name, DoTryToShow)
        end
      end
    end
  end

  if self.db.firstUse then
    self.db.firstUse = nil

  end
end

local UNDERLIGHT_ANGLER_ID = 133755

function ap:ResetCache()
  table.wipe(self.cache.items)
  table.wipe(self.cache.fishing)
  table.wipe(self.cache.bags)
  table.wipe(self.cache.bagItems)
  self:ScanAllBags()
end

function ap:QueueBag(containerID)
  containerID = tonumber(containerID)
  if not containerID then
    return
  end

  if not tContains(BAGS_TO_SCAN, containerID) then
    print(' queueing', containerID, type(containerID), #BAGS_TO_SCAN , 'in line')
    BAGS_TO_SCAN[#BAGS_TO_SCAN + 1] = containerID
  end
end

function ap:Reanchor()
  if Veneer then
    Veneer:DynamicReanchor()
  end
end

function ap:OnShow()
  print('|cFFFFFF00OnShow()|r')

  for name, args in pairs(PENDING_HOOKS) do
    if _G[name] then
      AddFrameHooks(_G[name], args)
      PENDING_HOOKS[name] = nil
    end
  end


  self.enabled = true
  self:ScanAllBags()
  self:Reanchor()
end
function ap:OnHide()
  print('|cFF88FF00OnHide()|r', debugstack())
  self:Reanchor()
end
function ap:OnEnter()

  GameTooltip:SetOwner(self, 'ANCHOR_CURSOR')


  GameTooltip:AddLine(self.bagAP)
  GameTooltip:AddLine(self.bankAP)

end

function ap:TryToShow()

  print('|cFFFFFF00TryToShow()')

  if not InCombatLockdown() then
    for _, name in ipairs(FRAME_LIST) do
      print('test:', name, (_G[name] and _G[name]:IsShown()))
      if _G[name] and _G[name]:IsVisible() then
        if self:IsShown() then
          self:Update()
        else
          self:Show()
        end
        return
      end
    end
  end

  print('failed tests')
  self:Hide()
end


function ap:OnEvent(event, ...)
  print('|cFF00FF88OnEvent()', event, ...)
  if event == 'PLAYER_ENTERING_WORLD' then
    self:TryToShow()
  elseif event == 'BAG_UPDATE' then
    local containerID = ...


    self:QueueBag(containerID)
  elseif event == 'ITEM_LOCK_CHANGED' then

    local containerID, slotID = ...

    if self.cache.bags[containerID] and self.cache.bags[containerID][slotID] then
      self.cache.bags[containerID][slotID] = nil
      self.cache.fishing[containerID][slotID] = nil
    end


  elseif event == 'PLAYER_BANKSLOTS_CHANGED' then
    self:ScanAllBags()
  elseif event == 'BAG_UPDATE_DELAYED' then
    if not self.firstHit then
      self.firstHit = true
    else
      self:ScanAllBags()
    end
  elseif event == 'BANKFRAME_OPENED' then
    self.bankAccess = true
    self:ScanAllBags()
  elseif event == 'BANKFRAME_CLOSED' then
    self.bankAccess = nil
  elseif event == 'ARTIFACT_UPDATE' then
    local newItem = ...
    if newItem then
      local itemID, _, name, texture, currentXP, pointsSpent = C_ArtifactUI:GetArtifactInfo()
      self:SetArtifact(itemID, name, texture, currentXP, pointsSpent)
      self:ScanAllBags(self.bankAccess)
    end
  elseif event == 'ARTIFACT_XP_UPDATE' then
    local itemID, _, name, texture, currentXP, pointsSpent = C_ArtifactUI:GetEquippedArtifactInfo()
    self:SetArtifact(itemID, name, texture, currentXP, pointsSpent)
    self:ScanAllBags(self.bankAccess)
  elseif event == 'PLAYER_REGEN_ENABLED' then

    if self.queuedScan then
      self:ScanAllBags(self.backAccess)
    else
      self:TryToShow()
    end

    if #queued_hooks >= 1 then
      CreateHook()
    end
  elseif event == 'PLAYER_REGEN_DISABLED' then
    self:Hide()
  end
end

function ap:OnUpdate()
  if #self.scanQueue >= 1 then
    local scanInfo = tremove(self.scanQueue, 1)
  end
  if IsShiftKeyDown() then
    self.Refresh:Show()
  else
    self.Refresh:Hide()
  end

end

function ap:OnMouseDown()
  self.enabled = nil
  self:Hide()
end

function ap:Update()
  if not self:IsShown() then
    print('|cFFFF4400Update()|r')
    return
  end
  print('|cFF00FFFFUpdate()|r')

  local bankText, bagText
  if not self.profile.knowledgeMultiplier then
    bankText = '|cFF00FF00Shift-Right-Click an artifact weapon to start building data.'
  elseif not (self.bankAP and self.bagAP) then
    bankText = '|cFFFF0000Open bank frame to count all AP|r '
  else

    if self.bagAP and (self.bagAP > 0) then
      bankText = 'Inventory: |cFFFFFFFF' .. ShortNumberString(self.bagAP) .. '|r'
    end
    if self.bankAP and (self.bankAP > 0) then
      bankText = (bankText and (bankText .. ' | ') or '') .. '|cFFFFFF00'..ShortNumberString(self.bankAP)..' banked|r'
    end
    if self.fishingAP and self.fishingAP > 0 then
      bankText = (bankText and (bankText .. ' | ') or '') .. '|cFF0088FF' .. ShortNumberString(self.fishingAP) .. ' fishing|r'
    end
  end

  self.worldQuestAP = 0
  if WorldPlan then

    if not self.worldPlanHooked then
      WorldPlan:RegisterDataCallback(function()
        self:Update()
      end)
    end


    local showWQ
    print('world plan is loaded')
    local worldQuests = WorldPlan:GetQuestPins()
    for index, pin in ipairs(worldQuests) do
      if pin.dataLoaded and (pin.rewardType == WORLD_QUEST_REWARD_TYPE_FLAG_ARTIFACT_POWER) and (pin.isActive) then
        showWQ = true
        print(pin.itemNumber)
        self.worldQuestAP = self.worldQuestAP + pin.itemNumber
      end
    end

    if showWQ then
      bankText = (bankText and (bankText .. '\n') or '') .. '|cFFFFBB00World Quests:|r |cFFFFFFFF' .. ShortNumberString(self.worldQuestAP) .. ''
    end

  end

  self.SummaryHeader:SetText(bankText)

  local numButtons = 0
  local contentsHeight = 16 + self.SummaryHeader:GetHeight()
  local contentsWidth = self.SummaryHeader:GetWidth() + 16
  if self.profile.knowledgeMultiplier then
    numButtons = self:UpdateArtifactButtons()

    if numButtons ~= 0 then
      contentsHeight = contentsHeight + 64
    end

    contentsWidth = max(contentsWidth, 64*numButtons + 4 * (numButtons+1))

    local itemsWidth, itemsHeight = self:UpdateItemButtons()
    contentsHeight = contentsHeight + itemsHeight
    contentsWidth = max(contentsWidth, itemsWidth)
  end


  if not self.hasArtifacts then
    self:SetShown(false)
  end


  self:SetWidth(contentsWidth)
  self:SetHeight(contentsHeight)
  self:Reanchor()
end

function ap:UpdateArtifactButtons()

  -- Artifact icons, in no particular order
  self.equippedID = C_ArtifactUI.GetEquippedArtifactInfo()
  self.canAddAP = nil
  self.canAddFishingAP = nil
  local hasArtifacts
  local numButtons = 0
  local lastFrame = self
  local fishingID, fishingData
  local index, button
  for itemID, artifact in pairs(self.profile.artifacts) do
    if (itemID == UNDERLIGHT_ANGLER_ID)  then
      if VeneerData.ArtifactPower.EnableFishing then
        fishingID = itemID
        fishingData = artifact
      end

      if artifact.level < FISHING_MAX_TRAITS then
        if itemID == self.equippedID then
          self.canAddFishingAP = true
        end
      end


    else
      if artifact.level < WEAPON_MAX_TRAITS then

        if itemID == self.equippedID then
          self.canAddAP = true
        end
        hasArtifacts = true
        numButtons = numButtons + 1
        button = self.Artifact[numButtons]
        button.relativeFrame = lastFrame
        lastFrame = button:SetButton(itemID, artifact, numButtons, (self.equippedID == itemID), nil)
      end
    end
  end


  if fishingData and (self.fishingAP and self.fishingAP > 0) then
    numButtons = numButtons + 1
    hasArtifacts = true
    local button = self.Artifact[numButtons]
    button.relativeFrame = lastFrame
    button.isFishing = true
    button:SetButton(fishingID, fishingData, numButtons, self.equippedID == fishingID)
  end

  self.hasArtifacts = hasArtifacts
  for i = numButtons+ 1, #self.Artifact do
    print('hide', i)
    self.Artifact[i]:Hide()
  end


  return numButtons
end


function ap:UpdateItemButtons()
  print('|cFF00FFFFUpdateItemButtons()|r')

  local apType
  if self.canAddFishingAP then
    apType = true
  elseif not self.canAddAP then
    for index, button in ipairs(self.Tokens) do
      button:Hide()
    end
    return 0, 0
  end


  local lastFrame, upFrame
  local numButtons = 0
  local buttonsHeight = 0
  local buttonsWidth = 0

  for index, button in ipairs(self.Tokens) do
    if (button.numItems >= 1) and (button.isFishingAP == apType) then
      if button.itemName then
        self:SetItemAction(button)
      end

      button:ClearAllPoints()
      numButtons = numButtons + 1
      local col = mod(numButtons,8)
      print(index, button:GetID(), button.Icon:GetTexture())
      if numButtons == 1 then
        button:SetPoint('TOPLEFT', self, 'TOPLEFT', 4, -76)
        upFrame = button
        buttonsHeight = 52
      else
        if col == 1 then
          button:SetPoint('TOPLEFT', upFrame, 'BOTTOMLEFT', 0, -2)
          upFrame = button
          buttonsHeight = buttonsHeight + 52

        else
          button:SetPoint('TOPLEFT', lastFrame, 'TOPRIGHT', 2, 0)

        end
      end

      button.Count:SetText(button.numItems)
      lastFrame = button
      button:Show()
    else
      button:Hide()
    end
    buttonsWidth = min(numButtons, 8) * (BUTTON_SIZE)
  end



  if buttonsWidth ~= 0 then
    buttonsWidth = buttonsWidth + 8+ ((min(numButtons, 8)-1)*2)
  end



  return buttonsWidth, buttonsHeight
end

function ap:SetItemAction(button, name)
  name = name or self.itemName
  if InCombatLockdown() then
    self.itemName = name
    return
  else
      button:SetAttribute('*type*','item')
    button:SetAttribute('*item*', name)
  end
end

function ap:GetItemButton(itemID, texture, itemAP, fishing)
  print('|cFF00FFFFGetItemButton()|r', itemID, texture, itemAP)
  local button = self.ItemButtons[itemID]

  if not button then
    button = CreateFrame('Button', 'VeneerAPToken'..itemID, self, 'VeneerItemButton')
    button.baseAP = itemAP

    button:SetPushedTexture([[Interface\Buttons\UI-Quickslot-Depress]])
    button:SetHighlightTexture([[Interface\Buttons\ButtonHilight-Square]],"ADD")
    button:SetID(itemID)
    button.numItems = 0
    button.Icon:SetTexture(texture)
    button:RegisterForClicks("AnyUp")
    button.isFishingAP = fishing
    self:SetItemAction(button, GetItemInfo(itemID))

    print('  created')
    self.ItemButtons[itemID] = button
    self.numItems = self.numItems +  1
  end

  local itemAPtext = itemAP
  if itemAPtext >= 100000 then
    itemAPtext = floor(itemAPtext/1000) .. 'k'
  elseif itemAPtext >= 1000 then
    itemAPtext = (floor(itemAPtext/100)/10 ) .. 'k'
  end
  button.Label:SetText(itemAPtext)

  button.numItems = button.numItems + 1
  return button
end

function ap:GetItemAP(itemID, itemLink, bagData)
  if not self.cache.items[itemID] then

    print('doing tooltip scan', itemLink, itemID)
    self.tooltip:SetOwner(self, 'ANCHOR_NONE')
    self.tooltip:SetHyperlink(itemLink)
    self.tooltip:Show()
    local numLines = self.tooltip:NumLines()
    if numLines >= 3 then
        for i = 3, numLines do
          local text = _G[TOOLTIP_NAME .. 'TextLeft'.. i]:GetText()
          if text then

            text = text:lower():gsub(',', '')
            if text:match('equipped artifact') then
              print(itemLink, '-', tonumber(text))
              local itemAP = text:match('%d+')
              if itemAP then
                itemAP = itemAP
                self.cache.items[itemID] = tonumber(itemAP)
              end
            end
            if text:match('fishing artifact') then
              local fishingAP = text:match("%d+")
              fishingAP = fishingAP
              print(itemLink, 'fishing', tonumber(text))
              if fishingAP then
                self.cache.items[itemID] = tonumber(fishingAP)
                self.cache.fishing[itemID] = true
              end
            end
          end
        end
    else

      self.cache.items[itemID] = 0
    end
  end
  return self.cache.items[itemID], self.cache.fishing[itemID]
end

function ap:SetArtifact(itemID, name, texture, currentXP, pointsSpent)
  print('|cFF00FF00SetArtifact()|r')
  if not self.profile then
    return
  end
  local artifacts = self.profile.artifacts

  local multi = C_ArtifactUI.GetArtifactKnowledgeMultiplier()
  if multi and (self.profile.knowledgeMultiplier ~= multi) then
    table.wipe(self.cache.items)
    table.wipe(self.cache.fishing)
  end

  self.profile.knowledgeMultiplier = multi or self.profile.knowledgeMultiplier
  print('multiplier:', multi)

  if itemID then

    self.currentEquipped = itemID

    artifacts[itemID] = artifacts[itemID] or {}
    table.wipe(artifacts[itemID])
    local artifact = artifacts[itemID]

    artifact.name = name
    artifact.texture = texture
    artifact.currentXP = currentXP
    artifact.level = pointsSpent
    local cost = C_ArtifactUI.GetCostForPointAtRank(pointsSpent)
    artifact.currentCost = cost

    local pointsAvailable = pointsSpent
    local actualCost = cost
    local actualXP = currentXP
    while actualXP >= actualCost do
      pointsAvailable = pointsAvailable + 1
      actualXP = actualXP - actualCost
      print(pointsAvailable, '-', actualCost, '=', actualXP)
      actualCost = C_ArtifactUI.GetCostForPointAtRank(pointsAvailable)
    end
    print('updating', itemID, name,  currentXP, pointsSpent, pointsAvailable, actualXP)
    artifact.actualXP = actualXP
    artifact.actualLevel = pointsAvailable
    artifact.actualCost = actualCost

  end
end

function ap:ScanBag(id)
  print('|cFF00FFFFScanBag()|r', id, IsBagOpen(id), GetContainerNumSlots(id))
  local numSlots = GetContainerNumSlots(id)
  local requiresUpdate
  if numSlots == 0 then
    return nil
  end


  self.profile.bagslots[id] = self.profile.bagslots[id] or {}
  table.wipe(self.profile.bagslots[id])
  local bagData = self.profile.bagslots[id]
  bagData.totalAP = 0
  bagData.fishingAP = 0
  bagData.items = bagData.items or {}
  table.wipe(bagData.items)
  local c = self.cache

  c.bagItems[id] = c.bagItems[id] or {}
  c.bags[id] = c.bags[id] or {}
  c.fishing[id] = c.fishing[id] or {}

  for slotID = 1, numSlots do
    local texture, count, locked, quality, readable, lootable, link = GetContainerItemInfo(id, slotID)
    if link then
      local itemID = GetContainerItemID(id, slotID)
      local name, _, quality, iLevel, reqLevel, class, subclass = GetItemInfo(link)

      if class == 'Consumable' or subclass == 'Cooking' then
        --print(GetItemInfo(link))
        local itemAP, isFishingAP
        if c.bags[id][slotID] and (c.bagItems[id][slotID] == itemID) then
          --print('cached slot', id, slotID, name)
          itemAP = c.bags[id][slotID]
          isFishingAP = c.fishing[id] and c.fishing[id][slotID]
        else
          itemAP, isFishingAP = self:GetItemAP(itemID, link)
          c.bagItems[id][slotID] = itemID
          c.bags[id][slotID] = itemAP
          c.fishing[id][slotID] = isFishingAP
        end


        --print(itemAP, isFishingAP)
        if itemAP and (itemAP > 0) then
          local itemButton = self:GetItemButton(itemID, texture, itemAP, isFishingAP)

          if isFishingAP then
            bagData.fishingItems = (bagData.fishingItems or 0) + 1
            bagData.fishingAP = (bagData.fishingAP or 0) + itemAP
          else
            itemAP = itemAP
            bagData.numItems = (bagData.numItems or 0) + 1
            bagData.totalAP = (bagData.totalAP or 0) + itemAP
          end
          bagData.items[itemID] = (bagData.items[itemID] or 0) + 1
        end
      elseif self.profile.artifacts[itemID] then
        --print('artifact weapon', itemID, link, id, slotID)
        self.profile.artifacts[itemID].containerID = id
        self.profile.artifacts[itemID].slotID = slotID
      else
        --print('skipping', class, subclass, link)
      end

    end

  end

end

local BAG_SLOTS = {0, 1, 2, 3, 4 }
local BANK_SLOTS = {-1, 5, 6,7, 8, 9, 10, 11, 12}
local ItemCounts = {}
function ap:ScanAllBags()
  if InCombatLockdown() then
    self.queuedScan = true
    return
  end
  if not self.profile.knowledgeMultiplier then
    print('need to get knowledge level')
    return
  end

  self.queuedScan = nil

  print('|cFFFF0088ScanAllBags()|r', self.profile.knowledgeMultiplier)

  for _, button in ipairs(self.Tokens) do
    button.numItems = 0
  end


  for _, bagID in ipairs(BAG_SLOTS) do
    self:ScanBag(bagID)
  end

  if self.bankAccess then
    for _, bagID in ipairs(BANK_SLOTS) do
      self:ScanBag(bagID)
    end
  end

  self.bankAP = 0
  self.bagAP = 0
  self.fishingAP = 0

  table.wipe(ItemCounts)
  for id, bagData in pairs(self.profile.bagslots) do
    print(id, GetBagName(id), bagData.totalAP, bagData.fishingAP)
    id = tonumber(id)
    if bagData.totalAP then
      if (id == BANK_CONTAINER) or (id >= 5) then
        self.bankAP = self.bankAP + bagData.totalAP
      else
        self.bagAP = self.bagAP + bagData.totalAP
      end
    end
    if bagData.fishingAP then
      self.fishingAP = self.fishingAP + bagData.fishingAP
    end

  end
  self.lastUpdate = GetTime()
  self.queuedScan = nil
  self:TryToShow()
end

VeneerArtifactButtonMixin = {}

function VeneerArtifactButtonMixin:SetButton(itemID, artifact, index, equipped, fishing)
  print(itemID, index)
  print(artifact.name, artifact.texture, artifact.currentXP)
  self:SetID(itemID)
  if not artifact.currentCost then
    artifact.currentCost = artifact.cost
  end

  for k,v in pairs(artifact) do
    --print('::',k,v)
    self[k] = v
  end

  self.isFishing = fishing
  -- this can change between artifact parses
  local potentialPoints = self.actualLevel
  local totalAP = (itemID ~= UNDERLIGHT_ANGLER_ID) and ((self:GetParent().bankAP or 0) + (self:GetParent().bagAP or 0)) or (self:GetParent().fishingAP or 0)
  print(totalAP)
  local potentialXP = self.actualXP + totalAP

  self.potentialXP = potentialXP
  local potentialCost = C_ArtifactUI.GetCostForPointAtRank(potentialPoints)
  while potentialXP >= potentialCost do
    potentialXP = potentialXP - potentialCost
    potentialPoints = potentialPoints + 1
    print('inc estimate', potentialXP, potentialPoints)
    potentialCost = C_ArtifactUI.GetCostForPointAtRank(potentialPoints)
  end
  self.potentialCost = potentialCost
  self.potentialLevel = potentialPoints
  self.potentialAdjustedXP = potentialXP

  self.maxCost = self.currentCost
  for i = self.level + 1, #POINT_COSTS do
    self.maxCost = self.maxCost + POINT_COSTS[i]
  end

  if index ~= 1 then
    self:ClearAllPoints()
    self:SetPoint('TOPLEFT', self.relativeFrame, 'TOPRIGHT', 4, 0)
  else
    self:ClearAllPoints()
    self:SetPoint('TOPLEFT', self.relativeFrame, 'TOPLEFT', 4, -4)
  end

  self.isEquipped = equipped
  self:Update()
  self:Show()
  return self
end

function VeneerArtifactButtonMixin:Update()
  local r, g, b = 1, 1, 1
  local lR, lG, lB = 1, 1, 0
  local levelText = self.level
  local xpValue = ShortNumberString(self.currentXP)
  local costValue = self.currentCost
  if self.actualLevel ~= self.level then
    levelText, r,g,b = self.actualLevel, 0,1,0
    xpValue, costValue, lR, lG, lB = ShortNumberString(self.potentialXP) .. '\n' .. ShortNumberString(self.potentialCost-self.potentialXP), self.actualCost, 0, 1, 0
  elseif self.potentialLevel ~= self.level then
    levelText, r, g, b = self.potentialLevel, 0,1,1
    xpValue, costValue, lR, lG, lB = ShortNumberString(self.potentialXP) .. '\n' .. ShortNumberString(self.potentialCost-self.potentialXP), self.potentialCost, 0,1,1
  else
    xpValue, lR, lG, lB = ShortNumberString(self.actualXP) .. '\n|cFFFFFF00' .. ShortNumberString(self.actualCost-self.actualXP)..'|r', 1, 1, 1
  end

  self.Level:SetText(levelText)
  self.Level:SetTextColor(r, g, b)
  self.CurrentXP:SetText( xpValue)
  self.CurrentXP:SetTextColor(lR, lG, lB)

  if self.isEquipped then
    self:SetNormalTexture([[Interface\Buttons\ButtonHilight-Square]])
    self:GetNormalTexture():SetBlendMode('ADD')
    self:GetNormalTexture():SetVertexColor(0,1,0)
  else
    self:SetNormalTexture(nil, 'ADD')
  end

  local currentProgress = (self.currentXP < self.currentCost) and (self.currentXP / self.currentCost) or 1
  if self.level <= 53 then
    self.CurrentProgress.animateFrom = self.CurrentProgress:GetHeight() or 1
    self.CurrentProgress.animateTo = currentProgress * self:GetHeight()
    self.CurrentProgress:Show()
    self.ProgressLine:Show()
  else
    self.CurrentProgress:Hide()
    self.ProgressLine:Hide()
  end

  if self.potentialXP > self.currentXP then
    local projectedProgress = (self.potentialAdjustedXP < self.potentialCost) and (self.potentialXP / self.potentialCost) or 1
    if (projectedProgress > currentProgress) then
      self.AdjustedProgress:SetPoint('BOTTOM', self.CurrentProgress, 'TOP')
      projectedProgress = projectedProgress - currentProgress
    else
      self.AdjustedProgress:SetPoint('BOTTOM', self, 'BOTTOM')
    end
    print('show potential', currentProgress, projectedProgress)
    self.AdjustedProgress.animateFrom = self.AdjustedProgress:GetHeight() or 1
    self.AdjustedProgress.animateTo = projectedProgress * self:GetHeight()
    self.AdjustedLine:Show()
    self.AdjustedProgress:Show()
  else
    self.AdjustedProgress:Hide()
    self.AdjustedLine:Hide()
  end
  self.Icon:SetTexture(self.texture)
  self:SetSize(64,64)
end


function VeneerArtifactButtonMixin:AnimateProgress(region)
  local cTime = GetTime()
  if not region.animateStart then
    region.animateStart = cTime
  end
  local progressTo, progressFrom = region.animateTo, region.animateFrom
    local elapsed = cTime - region.animateStart
  if elapsed >= .5 then
    region:SetHeight(progressTo)
    region.animateTo = nil
    region.animateStart = nil
    region.animateFrom = nil
  else
    local progress = elapsed / .5
    local height = (progressFrom + (progressTo - progressFrom) * progress)
    --print(self:GetName(), progressTo, progressFrom, (progressTo - progressFrom), ceil(progress*10)/10, ceil(height))
    region:SetHeight(height)
  end
end

function VeneerArtifactButtonMixin:OnUpdate(sinceLast)
  if self.CurrentProgress.animateTo then
    self:AnimateProgress(self.CurrentProgress)
  end

  if self.AdjustedProgress.animateTo then
    self:AnimateProgress(self.AdjustedProgress)
  end
end

function VeneerArtifactButtonMixin:OnEnter()
  GameTooltip:SetOwner(self, 'ANCHOR_CURSOR')
  GameTooltip:SetText(self.name)
  GameTooltip:AddLine(ShortNumberString(self.currentXP) .. ' / '..ShortNumberString(self.currentCost), 1, 1, 0)
  if self.potentialXP > self.currentXP then
    GameTooltip:AddLine(ShortNumberString(self.potentialXP) .. ' potential XP', 0, 1, 1)
    if self.potentialAdjustedXP ~= self.potentialXP then
      GameTooltip:AddLine(ShortNumberString(self.potentialAdjustedXP) .. ' / ' .. ShortNumberString(self.potentialCost).. ' after', 0, 1, 0)
    end
  end
  if self.actualLevel ~= self.level then
    GameTooltip:AddLine(ShortNumberString(self.actualLevel - self.level) .. ' points unlocked', 0, 1, 1)
  end
  if self.currentXP < self.currentCost then
    GameTooltip:AddLine(ShortNumberString(self.currentCost - self.currentXP) .. ' needed')
  end

  GameTooltip:Show()
end
function VeneerArtifactButtonMixin:OnLeave()
  if GameTooltip:IsOwned(self) then
    GameTooltip:Hide()
  end
end
function VeneerArtifactButtonMixin:OnHide()

  if GameTooltip:IsOwned(self) then
    GameTooltip:Hide()
  end
end

function VeneerArtifactButtonMixin:OnClick(button, down)
  if self.isEquipped then
    SocketInventoryItem(16)
  else
    if IsShiftKeyDown() then
    SocketContainerItem(self.containerID, self.slotID)
    else

    end
  end
end