local _, addon = ...

addon.Bindings = addon.Bindings or {}
local Bindings = addon.Bindings

Bindings.SLOT_ORDER = {
    "NONE",
    "SHIFT",
    "CTRL",
    "ALT",
    "SHIFT_CTRL",
    "SHIFT_ALT",
    "CTRL_ALT",
    "SHIFT_CTRL_ALT",
}

Bindings.SLOT_LABELS = {
    NONE = "No Mod",
    SHIFT = "Shift",
    CTRL = "Ctrl",
    ALT = "Alt",
    SHIFT_CTRL = "Shift+Ctrl",
    SHIFT_ALT = "Shift+Alt",
    CTRL_ALT = "Ctrl+Alt",
    SHIFT_CTRL_ALT = "Shift+Ctrl+Alt",
}

local function getSafeBindingSet()
    local set = GetCurrentBindingSet and GetCurrentBindingSet() or nil
    if set ~= 1 and set ~= 2 then
        set = 2
    end
    return set
end

local function makeBindingToken(baseKey, slotKey)
    if not baseKey then
        return nil
    end
    if slotKey == "NONE" then
        return baseKey
    end

    local hasShift = slotKey == "SHIFT" or slotKey == "SHIFT_CTRL" or slotKey == "SHIFT_ALT" or slotKey == "SHIFT_CTRL_ALT"
    local hasCtrl = slotKey == "CTRL" or slotKey == "SHIFT_CTRL" or slotKey == "CTRL_ALT" or slotKey == "SHIFT_CTRL_ALT"
    local hasAlt = slotKey == "ALT" or slotKey == "SHIFT_ALT" or slotKey == "CTRL_ALT" or slotKey == "SHIFT_CTRL_ALT"

    -- WoW binding tokens are most reliable in ALT-CTRL-SHIFT order.
    local prefix = ""
    if hasAlt then
        prefix = prefix .. "ALT-"
    end
    if hasCtrl then
        prefix = prefix .. "CTRL-"
    end
    if hasShift then
        prefix = prefix .. "SHIFT-"
    end
    return prefix .. baseKey
end

function Bindings:Initialize()
    self:CreateSlotButtons()
    self:RegisterEvents()
    self:RefreshAllSlotButtons()
    self:ApplyActiveBinding()
end

function Bindings:CreateSlotButtons()
    if self.slotButtons then
        return
    end

    self.slotButtons = {}
    for _, slotKey in ipairs(self.SLOT_ORDER) do
        local button = CreateFrame("Button", "MountBinderActionButton_" .. slotKey, UIParent, "SecureActionButtonTemplate")
        button:RegisterForClicks("AnyDown")
        button:SetSize(1, 1)
        button:SetPoint("TOPLEFT", UIParent, "TOPLEFT", -200, 200)
        button:Show()
        button.slotKey = slotKey
        button:SetScript("PostClick", function(btn)
            if btn.mountRef then
                addon.Actions:ExecuteMount(btn.mountRef)
            end
            if btn.commandRef then
                addon.Actions:ExecuteCommand(btn.commandRef)
            end
        end)
        self.slotButtons[slotKey] = button
    end
end

function Bindings:RegisterEvents()
    if self.eventFrame then
        return
    end

    local frame = CreateFrame("Frame")
    frame:RegisterEvent("PLAYER_REGEN_ENABLED")
    frame:SetScript("OnEvent", function(_, event)
        if event == "PLAYER_REGEN_ENABLED" and self.needsRefresh then
            self.needsRefresh = false
            self:RefreshAllSlotButtons()
        end
    end)
    self.eventFrame = frame
end

function Bindings:RefreshSlotButton(slotKey)
    if not self.slotButtons or not self.slotButtons[slotKey] then
        return
    end
    if InCombatLockdown() then
        self.needsRefresh = true
        return
    end
    local actionRef = addon:GetBindingSlotAction(slotKey)
    addon.Actions:ApplyToSecureButton(self.slotButtons[slotKey], actionRef)
end

function Bindings:RefreshAllSlotButtons()
    for _, slotKey in ipairs(self.SLOT_ORDER) do
        self:RefreshSlotButton(slotKey)
    end
end

function Bindings:GetCurrentBoundKey()
    return addon.globalDB.bindings.key
end

function Bindings:SetBoundKey(key)
    addon.globalDB.bindings.key = key
    self:ApplyActiveBinding()
end

function Bindings:ClearBoundKey()
    if self.boundTokens then
        for _, token in ipairs(self.boundTokens) do
            SetBinding(token)
        end
    end
    addon.globalDB.bindings.key = nil
    SaveBindings(getSafeBindingSet())
    self.boundTokens = {}
end

function Bindings:IsAdvancedModifiersEnabled()
    return true
end

function Bindings:SetAdvancedModifiersEnabled(enabled)
    addon.charDB.bindings.advancedModifiers = true
    self:ApplyActiveBinding()
end

function Bindings:GetVisibleSlots()
    return self.SLOT_ORDER
end

function Bindings:ApplyActiveBinding()
    if not self.slotButtons then
        return
    end

    local bindingSet = getSafeBindingSet()
    local baseKey = addon.globalDB.bindings.key

    if self.boundTokens then
        for _, token in ipairs(self.boundTokens) do
            SetBinding(token)
        end
    end
    self.boundTokens = {}

    if baseKey then
        for _, slotKey in ipairs(self:GetVisibleSlots()) do
            local token = makeBindingToken(baseKey, slotKey)
            if token and self.slotButtons[slotKey] then
                SetBindingClick(token, self.slotButtons[slotKey]:GetName())
                self.boundTokens[#self.boundTokens + 1] = token

                -- Register alternate prefix order for clients that normalize differently.
                if slotKey == "SHIFT_CTRL" then
                    local altToken = "CTRL-SHIFT-" .. baseKey
                    SetBindingClick(altToken, self.slotButtons[slotKey]:GetName())
                    self.boundTokens[#self.boundTokens + 1] = altToken
                elseif slotKey == "SHIFT_ALT" then
                    local altToken = "SHIFT-ALT-" .. baseKey
                    SetBindingClick(altToken, self.slotButtons[slotKey]:GetName())
                    self.boundTokens[#self.boundTokens + 1] = altToken
                elseif slotKey == "CTRL_ALT" then
                    local altToken = "CTRL-ALT-" .. baseKey
                    SetBindingClick(altToken, self.slotButtons[slotKey]:GetName())
                    self.boundTokens[#self.boundTokens + 1] = altToken
                elseif slotKey == "SHIFT_CTRL_ALT" then
                    local altA = "CTRL-SHIFT-ALT-" .. baseKey
                    local altB = "SHIFT-CTRL-ALT-" .. baseKey
                    SetBindingClick(altA, self.slotButtons[slotKey]:GetName())
                    SetBindingClick(altB, self.slotButtons[slotKey]:GetName())
                    self.boundTokens[#self.boundTokens + 1] = altA
                    self.boundTokens[#self.boundTokens + 1] = altB
                end
            end
        end
    end

    SaveBindings(bindingSet)
end

function Bindings:StartCapture(onComplete)
    if self.captureFrame then
        self.captureFrame:Hide()
    end

    local frame = CreateFrame("Frame", nil, UIParent)
    frame:SetAllPoints(UIParent)
    frame:EnableKeyboard(true)
    frame:SetPropagateKeyboardInput(true)
    frame:SetFrameStrata("TOOLTIP")
    frame:SetScript("OnKeyDown", function(_, key)
        if key == "ESCAPE" then
            frame:EnableKeyboard(false)
            frame:Hide()
            if onComplete then
                onComplete(nil)
            end
            return
        end
        frame:SetPropagateKeyboardInput(false)
        frame:EnableKeyboard(false)
        frame:Hide()
        self:SetBoundKey(key)
        if onComplete then
            onComplete(key)
        end
    end)

    self.captureFrame = frame
end
