local E, C, L = select(2, ...):unpack()

------------------------------------------------------------------------
-- Button Style
------------------------------------------------------------------------
local module = E:Module("Actionbar"):Sub("StyleButton")
local LAB = LibStub("LibActionButton-1.0")

local cfg = C.actionbar.styles.buttons

------------------------------------------------------------------------
-- Action button style config
------------------------------------------------------------------------

local hooksecurefunc = hooksecurefunc
local unpack, pairs, gsub = unpack, pairs, gsub
local RANGE_INDICATOR = RANGE_INDICATOR
local KEY_BUTTON3, KEY_BUTTON4, KEY_SPACE, KEY_NUMPAD1 = KEY_BUTTON3, KEY_BUTTON4, KEY_SPACE, KEY_NUMPAD1
local KEY_MOUSEWHEELUP, KEY_MOUSEWHEELDOWN = KEY_MOUSEWHEELUP, KEY_MOUSEWHEELDOWN

local actionStyle = {
    icon = {
        texCoord = { 0.1, 0.9, 0.1, 0.9 },
        points = {
            { "TOPLEFT", 1, -1 },
            { "BOTTOMRIGHT", -1, 1 },
        },
    },
    border = {
        file = C.media.button.border,
        points = {
            { "TOPLEFT", -2, 2 },
            { "BOTTOMRIGHT", 2, -2 },
        },
    },
    flash = {
        file = C.media.button.flash,
        points = {
            { "TOPLEFT", 0, 0 },
            { "BOTTOMRIGHT", 0, 0 },
        },
    },
    normalTexture = {
        file = C.media.button.normal,
        color = { 0.5, 0.5, 0.5, 0.6 },
        points = {
            { "TOPLEFT", 0, 0 },
            { "BOTTOMRIGHT", 0, 0 },
        },
    },
    pushedTexture = {
        file = C.media.button.glow,
        points = {
            { "TOPLEFT", -2, 2 },
            { "BOTTOMRIGHT", 2, -2 },
        },
    },
    checkedTexture = {
        file = "",
        points = {
            { "TOPLEFT", 0, 0 },
            { "BOTTOMRIGHT", 0, 0 },
        },
    },
    highlightTexture = {
        file = "",
        points = {
            { "TOPLEFT", 0, 0 },
            { "BOTTOMRIGHT", 0, 0 },
        },
    },
    hotkey = {
        font = { STANDARD_TEXT_FONT, 11, "OUTLINE" },
        points = {
            { "TOPRIGHT", 0, 0 },
            { "TOPLEFT", 0, 0 },
        },
    },
    count = {
        font = { STANDARD_TEXT_FONT, 11, "OUTLINE" },
        points = {
            { "BOTTOMRIGHT", 0, 0 },
        },
    },
    name = {
        font = { STANDARD_TEXT_FONT, 10, "OUTLINE" },
        points = {
            { "BOTTOMLEFT", 0, 0 },
            { "BOTTOMRIGHT", 0, 0 },
        },
    },
    cooldown = {
        font = { STANDARD_TEXT_FONT, 16, "OUTLINE" },
        points = {
            { "TOPLEFT", 0, 0 },
            { "BOTTOMRIGHT", 0, 0 },
        },
    },
    backdrop = {
        bgFile = C.media.button.buttonback,
        edgeFile = C.media.button.outer_shadow,
        tile = false,
        tileSize = 16,
        edgeSize = 2,
        insets = { left = 2, right = 2, top = 2, bottom = 2 },
        backgroundColor = C.media.backdrop_color,
        borderColor = C.media.border_color,
        points = {
            { "TOPLEFT", -2, 2 },
            { "BOTTOMRIGHT", 2, -2 },
        },
    },
}

------------------------------------------------------------------------
-- Hotkey text replacements
------------------------------------------------------------------------

local keyButton = gsub(KEY_BUTTON4, "%d", "")
local keyNumpad = gsub(KEY_NUMPAD1, "%d", "")

local replaces = {
    { "(" .. keyButton .. ")", "M" },
    { "(" .. keyNumpad .. ")", "N" },
    { "(a%-)", "a" },
    { "(c%-)", "c" },
    { "(s%-)", "s" },
    { KEY_BUTTON3, "M3" },
    { KEY_MOUSEWHEELUP, "MU" },
    { KEY_MOUSEWHEELDOWN, "MD" },
    { KEY_SPACE, "Sp" },
    { "CAPSLOCK", "CL" },
    { "BUTTON", "M" },
    { "NUMPAD", "N" },
    { "(ALT%-)", "a" },
    { "(CTRL%-)", "c" },
    { "(SHIFT%-)", "s" },
    { "MOUSEWHEELUP", "MU" },
    { "MOUSEWHEELDOWN", "MD" },
    { "SPACE", "Sp" },
}

------------------------------------------------------------------------
-- Apply helpers
------------------------------------------------------------------------

local function updateHotKey(hotkey)
    local text = hotkey:GetText()
    if not text then return end

    if text == RANGE_INDICATOR then
        text = ""
    else
        for _, value in pairs(replaces) do
            text = gsub(text, value[1], value[2])
        end
    end

    hotkey:SetFormattedText("%s", text)
end

local function applyPoints(self, points)
    if not points then return end
    self:ClearAllPoints()
    for _, point in next, points do
        self:SetPoint(unpack(point))
    end
end

local function applyTexCoord(texture, texCoord)
    if texture.__lockdown or not texCoord then return end
    texture:SetTexCoord(unpack(texCoord))
end

local function applyVertexColor(texture, color)
    if not color then return end
    texture:SetVertexColor(unpack(color))
end

local function applyAlpha(region, alpha)
    if not alpha then return end
    region:SetAlpha(alpha)
end

local function applyFont(fontString, font)
    if not font then return end
    fontString:SetFont(unpack(font))
end

local function applyHorizontalAlign(fontString, align)
    if not align then return end
    fontString:SetJustifyH(align)
end

local function applyVerticalAlign(fontString, align)
    if not align then return end
    fontString:SetJustifyV(align)
end

local function applyTexture(texture, file)
    if not file then return end
    texture:SetTexture(file)
end

local function applyNormalTexture(button, file)
    if not file then return end
    button:SetNormalTexture(file)
end

local function callButtonFunc(button, func, ...)
    if button and func and button[func] then button[func](button, ...) end
end

------------------------------------------------------------------------
-- Setup helpers
------------------------------------------------------------------------

local function setupTexture(texture, cfg, func, button)
    if not texture or not cfg then return end

    applyTexCoord(texture, cfg.texCoord)
    applyPoints(texture, cfg.points)
    applyVertexColor(texture, cfg.color)
    applyAlpha(texture, cfg.alpha)
    if func == "SetTexture" then
        applyTexture(texture, cfg.file)
    elseif func == "SetNormalTexture" then
        applyNormalTexture(button, cfg.file)
    elseif cfg.file then
        callButtonFunc(button, func, cfg.file)
    end
end

local function setupFontString(fontString, cfg)
    if not fontString or not cfg then return end

    applyPoints(fontString, cfg.points)
    applyFont(fontString, cfg.font)
    applyAlpha(fontString, cfg.alpha)
    applyHorizontalAlign(fontString, cfg.halign)
    applyVerticalAlign(fontString, cfg.valign)
end

local function setupCooldown(cooldown, cfg)
    if not cooldown or not cfg then return end
    applyPoints(cooldown, cfg.points)
end

local function setupBackdrop(button, bdCfg)
    if not bdCfg or button.__bg then return end

    Mixin(button, BackdropTemplateMixin)
    button.SetupTextureCoordinates = E.SafeSetupTextureCoordinates
    local bg = CreateFrame("Frame", nil, button, "BackdropTemplate")
    applyPoints(bg, bdCfg.points)
    bg:SetFrameLevel(button:GetFrameLevel() - 1)
    bg:SetBackdrop(bdCfg)

    if bdCfg.backgroundColor then bg:SetBackdropColor(unpack(bdCfg.backgroundColor)) end
    if bdCfg.borderColor then bg:SetBackdropBorderColor(unpack(bdCfg.borderColor)) end

    button.__bg = bg
end

------------------------------------------------------------------------
-- Style action button
------------------------------------------------------------------------

local function styleActionButton(button, force)
    if not button then return end
    if button.__styled and not force then return end

    local buttonName = button:GetName()
    local icon = button.icon or _G[buttonName .. "Icon"]
    local hotkey = button.HotKey or _G[buttonName .. "HotKey"]
    local count = button.Count or _G[buttonName .. "Count"]
    local name = button.Name or _G[buttonName .. "Name"]
    local flash = button.Flash or _G[buttonName .. "Flash"]
    local border = button.Border or _G[buttonName .. "Border"]
    local autoCastable = button.AutoCastable or _G[buttonName .. "AutoCastable"]
    local cooldown = button.cooldown or _G[buttonName .. "Cooldown"] or button.Cooldown
    local normal = button.NormalTexture or button:GetNormalTexture()
    local pushed = button.PushedTexture or button:GetPushedTexture()
    local checked = button.CheckedTexture or (button.GetCheckedTexture and button:GetCheckedTexture() or nil)
    local highlight = button.HighlightTexture or button:GetHighlightTexture()
    local newActionTexture = button.NewActionTexture
    local spellHighlight = button.SpellHighlightTexture
    local slotbg = button.SlotBackground
    local iconMask = button.IconMask
    local petShine = _G[buttonName .. "Shine"]

    if border then border:SetTexture("") end
    if flash then flash:SetTexture("") end
    if newActionTexture then newActionTexture:SetTexture("") end
    if slotbg then slotbg:Hide() end
    if iconMask then iconMask:Hide() end
    if petShine then petShine:SetInside() end
    if spellHighlight then spellHighlight:SetOutside() end
    if autoCastable then
        autoCastable:SetTexCoord(0.217, 0.765, 0.217, 0.765)
        autoCastable:SetInside()
    end

    setupBackdrop(button, actionStyle.backdrop)
    setupCooldown(cooldown, actionStyle.cooldown)

    setupTexture(icon, actionStyle.icon, "SetTexture", icon)
    setupTexture(flash, actionStyle.flash, "SetTexture", flash)
    setupTexture(border, actionStyle.border, "SetTexture", border)
    setupTexture(normal, actionStyle.normalTexture, "SetNormalTexture", button)
    setupTexture(pushed, actionStyle.pushedTexture, "SetPushedTexture", button)
    setupTexture(highlight, actionStyle.highlightTexture, "SetHighlightTexture", button)
    setupTexture(checked, actionStyle.checkedTexture, "SetCheckedTexture", button)

    if checked then checked:SetColorTexture(1, 0.8, 0, 0.35) end
    if highlight then highlight:SetColorTexture(1, 1, 1, 0.25) end
    if spellHighlight then spellHighlight:SetOutside() end

    setupFontString(hotkey, actionStyle.hotkey)
    setupFontString(count, actionStyle.count)
    setupFontString(name, actionStyle.name)

    if hotkey then
        updateHotKey(hotkey)
        hooksecurefunc(hotkey, "SetText", updateHotKey)
    end

    button.__styled = true
end

E:Module("Actionbar").StyleActionButton = styleActionButton

------------------------------------------------------------------------
-- Per-button visibility config
------------------------------------------------------------------------

local function styleButton(button)
    if not button then return end

    styleActionButton(button, true)

    local buttonName = button:GetName()

    if not cfg.showMacroName then
        local name = button.Name or (buttonName and _G[buttonName .. "Name"])
        if name then name:SetAlpha(0) end
    end

    if not cfg.showCooldown then
        local cooldown = button.cooldown or (buttonName and _G[buttonName .. "Cooldown"]) or button.Cooldown
        if cooldown then cooldown:SetAlpha(0) end
    end

    if not cfg.showHotkey then
        local hotkey = button.HotKey or (buttonName and _G[buttonName .. "HotKey"])
        if hotkey then hotkey:SetAlpha(0) end
    end

    if not cfg.showStackCount then
        local count = button.Count or (buttonName and _G[buttonName .. "Count"])
        if count then count:SetAlpha(0) end
    end
end

------------------------------------------------------------------------
-- Lifecycle
------------------------------------------------------------------------

function module:OnEnable()
    for i = 1, 8 do
        for j = 1, NUM_ACTIONBAR_BUTTONS do
            styleButton(_G["DarkUI_ActionBar" .. i .. "Button" .. j])
        end
    end

    for i = 1, NUM_PET_ACTION_SLOTS do
        styleButton(_G["PetActionButton" .. i])
    end

    for i = 1, (NUM_STANCE_SLOTS or 10) do
        styleButton(_G["StanceButton" .. i])
    end

    for i = 1, (NUM_POSSESS_SLOTS or 2) do
        styleButton(_G["PossessButton" .. i])
    end

    styleButton(ExtraActionButton1)

    local bagButtons = {
        MainMenuBarBackpackButton,
        CharacterBag0Slot,
        CharacterBag1Slot,
        CharacterBag2Slot,
        CharacterBag3Slot,
        CharacterReagentBag0Slot,
    }
    for _, button in next, bagButtons do
        styleButton(button)
    end

    -- Spell flyout
    if SpellFlyout then
        SpellFlyout.Background:SetAlpha(0)
        local numFlyouts = 1
        local function checkForFlyoutButtons()
            local button = _G["SpellFlyoutButton" .. numFlyouts]
            while button do
                styleButton(button)
                numFlyouts = numFlyouts + 1
                button = _G["SpellFlyoutButton" .. numFlyouts]
            end
        end
        SpellFlyout:HookScript("OnShow", checkForFlyoutButtons)
        SpellFlyout:HookScript("OnHide", checkForFlyoutButtons)
    end

    -- LAB SpellFlyout
    if LAB.flyoutHandler then
        for _, button in next, LAB.FlyoutButtons do
            button:SetScale(1)
            styleButton(button)
        end

        LAB.RegisterCallback(LAB.flyoutHandler, "OnButtonUpdate", function(_, button)
            if button:GetParent() ~= LAB.flyoutHandler then return end
            if button:GetParent():GetParent() ~= UIParent then
                button:SetSize(button:GetParent():GetParent():GetSize())
                styleButton(button)
            end
        end)

        LAB.flyoutHandler.Background:Hide()
    end

    -- Equipped border color
    LAB.RegisterCallback(module, "OnButtonUpdate", function(_, button)
        if not button.__bg then return end
        if button.Border and button.Border:IsShown() then
            button.__bg:SetBackdropBorderColor(0, 0.7, 0.1)
        else
            button.__bg:SetBackdropBorderColor(0, 0, 0)
        end
    end)

    -- DarkUI extra buttons
    styleButton(_G["DarkUIExtraButtons_MainLeftButton"])
    styleButton(_G["DarkUIExtraButtons_MainRightButton"])
    styleButton(_G["DarkUIExtraButtons_TopLeftButton"])
    styleButton(_G["DarkUIExtraButtons_TopRightButton"])
end
