--========================================================--
--             Scorpio Secure Bag Handler                 --
--                                                        --
-- Author      :  kurapica125@outlook.com                 --
-- Create Date :  2021/03/29                              --
--========================================================--

--========================================================--
Scorpio        "Scorpio.Secure.BagHandler"           "1.0.0"
--========================================================--

__Sealed__() enum "BagSlotCountStyle" { "Hidden", "Empty", "Total", "AllEmpty", "All" }

_Enabled                        = false

------------------------------------------------------
-- Action Handler
------------------------------------------------------
handler                         = ActionTypeHandler {
    Name                        = "bag",
    DragStyle                   = "Keep",
    ReceiveStyle                = "Keep",

    InitSnippet                 = [[ _BagSlotMap = newtable() ]],

    PickupSnippet               = [[
        local id                = ...
        if id ~= 0 then
            Manager:CallMethod("CloseContainerForSafe", id)
            return "clear", "bag", _BagSlotMap[id]
        end
    ]],

    ReceiveSnippet              = "Custom",

    UpdateSnippet               = [[
        local target            = ...
        target                  = tonumber(target)

        if target == 0 then
            self:SetAttribute("*type*", "click")
            Manager:CallMethod("BindProxyClick", self:GetName(), "MainMenuBarBackpackButton")

        elseif target and target <= 4 then
            self:SetAttribute("*type*", "click")
            Manager:CallMethod("BindProxyClick", self:GetName(), "CharacterBag".. tostring(target-1) .."Slot")

        else
            Manager:CallMethod("ClearProxyClick", self:GetName())

            if target and target <= 11 then
                self:SetAttribute("*type*", "openbank")
                Manager:CallMethod("RegisterBankBag", self:GetName())
            else
                self:SetAttribute("*type*", nil)
                self:SetAttribute("*macrotext*", nil)
                Manager:CallMethod("UnregisterBankBag", self:GetName())
            end
        end
    ]],

    ClearSnippet                = [[
        self:SetAttribute("*type*", nil)
        self:SetAttribute("*macrotext*", nil)
        Manager:CallMethod("ClearProxyClick", self:GetName())
        Manager:CallMethod("UnregisterBankBag", self:GetName())
    ]],

    OnEnableChanged             = function(self, value) _Enabled = value end,
}

------------------------------------------------------
-- Module Event Handler
------------------------------------------------------
_BagSlotMapTemplate             = "_BagSlotMap[%d] = %d"

_, _EmptyTexture                = GetInventorySlotInfo("Bag0Slot")
_BagSlotMap                     = {}
_ContainerMap                   = {}

_IconLocked                     = {}

function OnEnable(self)
    OnEnable                    = nil

    local cache                 = {}

    -- Container
    _BagSlotMap[0]              = GetInventorySlotInfo("BackSlot")

    for i = 1, 11 do
        _BagSlotMap[i]          = ContainerIDToInventoryID(i)
        tinsert(cache, _BagSlotMapTemplate:format(i, _BagSlotMap[i]))
    end

    handler:RunSnippet( tblconcat(cache, ";") )
    handler:RefreshActionButtons()

    for i = 1, NUM_CONTAINER_FRAMES do
        SecureActionButton.RegisterContainer(_G["ContainerFrame"..i])
    end
end

------------------------------------------------------
-- System Event Handler
------------------------------------------------------
__SystemEvent__()
function ITEM_LOCK_CHANGED(bag, slot)
    if not slot then
        for i, map in pairs(_BagSlotMap) do
            if map == bag then
                _IconLocked[i]  = IsInventoryItemLocked(bag)

                return handler:RefreshIconLocked()
            end
        end
    end
end

__SystemEvent__"BAG_UPDATE_DELAYED" "PLAYERBANKBAGSLOTS_CHANGED"
function BAG_UPDATE_DELAYED()
    return handler:RefreshActionButtons()
end

__SystemEvent__()
function PLAYERBANKSLOTS_CHANGED(slot)
    if slot > NUM_BANKGENERIC_SLOTS then
        slot = slot - NUM_BANKGENERIC_SLOTS + NUM_BAG_FRAMES

        for _, btn in handler:GetIterator() do
            if btn.ActionTarget == slot then
                handler:RefreshActionButtons(btn)
            end
        end
    end
end

__SystemEvent__()
function INVENTORY_SEARCH_UPDATE()
    return handler:RefreshShowSearchOverlay()
end

__SecureMethod__() __NoCombat__()
function handler.Manager:RegisterBankBag(btnName)
    _G[btnName]:SetAttribute("_openbank", [=[ self:GetFrameRef("_Manager"):RunFor(self, [[ Manager:CallMethod("OpenBankBag", self:GetName()) ]]) ]=])
end

__SecureMethod__() __NoCombat__()
function handler.Manager:UnregisterBankBag(btnName)
    _G[btnName]:SetAttribute("_openbank",  nil)
end

__SecureMethod__()
function handler.Manager:OpenBankBag(btnName)
    local button                = UI.GetProxyUI(_G[btnName])
    local bankID                = button.BagSlot

    if bankID and not InCombatLockdown() then
        local inventoryID       = BankButtonIDToInvSlotID(bankID - 4, 1)
        if not PutItemInBag(inventoryID) then
            -- open bag
            ToggleBag(bankID)
        end
    end
end

__SecureMethod__()
function handler.Manager:CloseContainerForSafe(id)
    if id and not InCombatLockdown() then
        CloseBag(id)
    end
end

-- Overridable Methods
function handler:IsUsableAction()
    return self.ActionTarget - 4 <= GetNumBankSlots()
end

function handler:ReceiveAction(target, detail)
    if target == 0 then
        return PutItemInBackpack()
    elseif _BagSlotMap[target] then
        return PutItemInBag(_BagSlotMap[target])
    end
end

function handler:HasAction()
    return _BagSlotMap[self.ActionTarget] and true or false
end

function handler:IsSearchOverlayShow()
    return IsContainerFiltered(self.ActionTarget)
end

function handler:IsIconLocked()
    return _IconLocked[self.ActionTarget] or false
end

function handler:GetActionTexture()
    if self.ActionTarget == 0 then return MainMenuBarBackpackButtonIconTexture:GetTexture() end
    local target                = _BagSlotMap[self.ActionTarget]
    return target and GetInventoryItemTexture("player", target) or _EmptyTexture
end

function handler:GetActionCharges()
    local style                 = self.BagSlotCountStyle

    if style == "Hidden" then
        return nil
    elseif style == "Empty" or style == "Total" then
        local free, total       = GetContainerNumFreeSlots(self.ActionTarget), GetContainerNumSlots(self.ActionTarget)
        if style == "Empty" then
            return free, total
        else
            return total, total
        end
    elseif style == "AllEmpty" or style == "All" then
        if self.ActionTarget <= 4 then
            local sFree, sTotal, free, total, bagFamily = 0, 0
            local _, tarFamily  = GetContainerNumFreeSlots(self.ActionTarget)
            if not tarFamily then return nil end

            for i = BACKPACK_CONTAINER, NUM_BAG_SLOTS do
                free, bagFamily = GetContainerNumFreeSlots(i)
                total           = GetContainerNumSlots(i)
                if bagFamily == tarFamily then
                    sFree       = sFree + free
                    sTotal      = sTotal + total
                end
            end
            if self.ActionTarget == 0 then
                self.__BagHandler_FreeSlots = sFree
            end
            if style == "AllEmpty" then
                return sFree, sTotal
            else
                return sTotal, sTotal
            end
        else
            return nil
        end
    end
end

function handler:IsActivedAction()
    local id                    = self.ActionTarget
    for container in pairs(_ContainerMap) do
        if container:GetID() == id and container:IsVisible() then
            return true
        end
    end
    return false
end

function handler:SetTooltip(tip)
    tip:SetOwner(self, "ANCHOR_LEFT");
    local target                = self.ActionTarget
    if target == 0 then
        tip:SetText(BACKPACK_TOOLTIP, 1.0, 1.0, 1.0)
        local keyBinding        = GetBindingKey("TOGGLEBACKPACK")
        if ( keyBinding ) then
            tip:AppendText(" "..NORMAL_FONT_COLOR_CODE.."("..keyBinding..")"..FONT_COLOR_CODE_CLOSE)
        end
        tip:AddLine(string.format(NUM_FREE_SLOTS, (self.__BagHandler_FreeSlots or 0)))
        tip:Show()
    elseif _BagSlotMap[target] then
        local id                = _BagSlotMap[target]
        if ( tip:SetInventoryItem("player", id) ) then
            if id <= 4 then
                local bindingKey = GetBindingKey("TOGGLEBAG"..(5 -  target))
                if ( bindingKey ) then
                    tip:AppendText(" "..NORMAL_FONT_COLOR_CODE.."("..bindingKey..")"..FONT_COLOR_CODE_CLOSE)
                end
            end
            if Scorpio.IsRetail then
                if ContainerFrame_CanContainerUseFilterMenu(target) then
                    local filterList = ContainerFrameSettingsManager:GenerateFilterList(target)
                    if filterList then
                        local wrapText = true;
                        GameTooltip_AddNormalLine(GameTooltip, BAG_FILTER_ASSIGNED_TO:format(filterList), wrapText);
                    end
                end
            else
                if (not IsInventoryItemProfessionBag("player", ContainerIDToInventoryID(target))) then
                    for i = LE_BAG_FILTER_FLAG_EQUIPMENT, NUM_LE_BAG_FILTER_FLAGS do
                        if ( GetBagSlotFlag(target, i) ) then
                            tip:AddLine(BAG_FILTER_ASSIGNED_TO:format(BAG_FILTER_LABELS[i]))
                            break
                        end
                    end
                end
            end
            tip:Show()
        else
            tip:SetText(EQUIP_CONTAINER, 1.0, 1.0, 1.0)
        end
    end
end

-- Expand IFActionHandler
class "SecureActionButton" (function(_ENV)
    local function OnShowOrHide(self)
        return handler:RefreshButtonState()
    end

    ------------------------------------------------------
    -- Static Method
    ------------------------------------------------------
    __Static__() __Arguments__{ UI }
    function RegisterContainer(container)
        if _ContainerMap[container] then return end
        _ContainerMap[container]= true

        container:HookScript("OnShow", OnShowOrHide)
        container:HookScript("OnHide", OnShowOrHide)
    end

    ------------------------------------------------------
    -- Property
    ------------------------------------------------------
    --- What to be shown as the count
    property "BagSlotCountStyle" { type = BagSlotCountStyle, default = "Hidden", handler = function(self) return handler:RefreshCount(self) end }
end)
