local addonTable = select(2, ...)
local core = addonTable

core.MeetingStoneButtonArt = core.MeetingStoneButtonArt or {}
local ButtonArt = core.MeetingStoneButtonArt

local BUTTON_ATLAS_TEXTURE = "Interface\\AddOns\\MyKeyStone\\Art\\Ui\\RedButton.png"
local BUTTON_ATLAS_WIDTH = 456
local BUTTON_ATLAS_HEIGHT = 216
local BUTTON_TEXT_COLOR = { 1, 0.82, 0, 1 }
local BUTTON_HOVER_TEXT_COLOR = { 1, 0.96, 0.58, 1 }
local BUTTON_PRESSED_TEXT_COLOR = { 0.95, 0.68, 0.18, 1 }
local BUTTON_DISABLED_TEXT_COLOR = { 0.48, 0.48, 0.48, 1 }
local BUTTON_DISABLED_TINT = { 0.42, 0.42, 0.42, 1 }
local BUTTON_TEXT_OFFSET_X = 0
local BUTTON_TEXT_OFFSET_Y = 0
local BUTTON_PRESSED_TEXT_OFFSET_X = 1
local BUTTON_PRESSED_TEXT_OFFSET_Y = -1
local BUTTON_REGIONS = {
    default = {
        ["72x26"] = { x = 0, y = 4, width = 144, height = 52 },
        ["64x26"] = { x = 164, y = 4, width = 128, height = 52 },
        ["16x16"] = { x = 352, y = 0, width = 64, height = 64 },
        ["24x24"] = { x = 352, y = 0, width = 64, height = 64 },
        ["32x32"] = { x = 352, y = 0, width = 64, height = 64 },
    },
    hover = {
        ["72x26"] = { x = 0, y = 80, width = 144, height = 52 },
        ["64x26"] = { x = 164, y = 80, width = 128, height = 52 },
        ["16x16"] = { x = 352, y = 76, width = 64, height = 64 },
        ["24x24"] = { x = 352, y = 76, width = 64, height = 64 },
        ["32x32"] = { x = 352, y = 76, width = 64, height = 64 },
    },
    pressed = {
        ["72x26"] = { x = 0, y = 156, width = 144, height = 52 },
        ["64x26"] = { x = 164, y = 156, width = 128, height = 52 },
        ["16x16"] = { x = 352, y = 152, width = 64, height = 64 },
        ["24x24"] = { x = 352, y = 152, width = 64, height = 64 },
        ["32x32"] = { x = 352, y = 152, width = 64, height = 64 },
    },
}

local function HideRegion(region)
    if not region or not region.Hide then
        return
    end

    region:Hide()
    if region.SetAlpha then
        region:SetAlpha(0)
    end
end

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

    local fontString = button.Text or (button.GetFontString and button:GetFontString())
    if button.GetRegions then
        for _, region in ipairs({ button:GetRegions() }) do
            if region ~= fontString and region.GetObjectType and region:GetObjectType() == "Texture" then
                HideRegion(region)
            end
        end
    end

    if button.GetNormalTexture then HideRegion(button:GetNormalTexture()) end
    if button.GetPushedTexture then HideRegion(button:GetPushedTexture()) end
    if button.GetDisabledTexture then HideRegion(button:GetDisabledTexture()) end
    if button.GetHighlightTexture then HideRegion(button:GetHighlightTexture()) end

    HideRegion(button.NineSlice)
    HideRegion(button.Left)
    HideRegion(button.Middle)
    HideRegion(button.Right)
    HideRegion(button.LeftTexture)
    HideRegion(button.MiddleTexture)
    HideRegion(button.RightTexture)
end

local function GetButtonSizeKey(button)
    if not button or not button.GetSize then
        return nil
    end

    local width, height = button:GetSize()
    width = math.floor((tonumber(width) or 0) + 0.5)
    height = math.floor((tonumber(height) or 0) + 0.5)
    local key = width .. "x" .. height
    if BUTTON_REGIONS.default[key] then
        return key
    end
    return nil
end

local function ApplyRegion(texture, region)
    texture:SetTexture(BUTTON_ATLAS_TEXTURE)
    texture:SetTexCoord(
        region.x / BUTTON_ATLAS_WIDTH,
        (region.x + region.width) / BUTTON_ATLAS_WIDTH,
        region.y / BUTTON_ATLAS_HEIGHT,
        (region.y + region.height) / BUTTON_ATLAS_HEIGHT
    )
    texture:Show()
end

local function SetArtState(button, state, enabled)
    local art = button and button.mkMeetingStoneButtonArt
    if not art then
        return
    end

    local sizeKey = GetButtonSizeKey(button)
    if not sizeKey then
        art.texture:Hide()
        return
    end

    if art.state ~= state or art.sizeKey ~= sizeKey then
        ApplyRegion(art.texture, BUTTON_REGIONS[state][sizeKey])
        art.state = state
        art.sizeKey = sizeKey
    end

    if enabled == false then
        if art.texture.SetDesaturated then
            art.texture:SetDesaturated(true)
        end
        art.texture:SetVertexColor(
            BUTTON_DISABLED_TINT[1],
            BUTTON_DISABLED_TINT[2],
            BUTTON_DISABLED_TINT[3],
            BUTTON_DISABLED_TINT[4]
        )
        return
    end

    if art.texture.SetDesaturated then
        art.texture:SetDesaturated(false)
    end
    art.texture:SetVertexColor(1, 1, 1, 1)
end

local function LayoutButtonText(button, pressed)
    local fontString = button and (button.Text or (button.GetFontString and button:GetFontString()))
    if not fontString then
        return
    end

    fontString:ClearAllPoints()
    fontString:SetPoint(
        "CENTER",
        button,
        "CENTER",
        pressed and BUTTON_PRESSED_TEXT_OFFSET_X or BUTTON_TEXT_OFFSET_X,
        pressed and BUTTON_PRESSED_TEXT_OFFSET_Y or BUTTON_TEXT_OFFSET_Y
    )
    if fontString.SetJustifyH then
        fontString:SetJustifyH("CENTER")
    end
    if fontString.SetJustifyV then
        fontString:SetJustifyV("MIDDLE")
    end
end

local function ResetNativeTextOffsets(button)
    if button and button.SetPushedTextOffset then
        button:SetPushedTextOffset(0, 0)
    end
end

local function GetButtonTextColor(enabled, pressed, hovered)
    if not enabled then
        return BUTTON_DISABLED_TEXT_COLOR
    end
    if pressed then
        return BUTTON_PRESSED_TEXT_COLOR
    end
    if hovered then
        return BUTTON_HOVER_TEXT_COLOR
    end
    return BUTTON_TEXT_COLOR
end

local function ApplyButtonTextVisibility(button, fontString)
    if not fontString then
        return
    end

    if button and button.mkFindGroupButtonLoading == true then
        if fontString.SetAlpha then
            fontString:SetAlpha(0)
        end
        if fontString.Hide then
            fontString:Hide()
        end
        return
    end

    if fontString.Show then
        fontString:Show()
    end
    if fontString.SetAlpha then
        fontString:SetAlpha(1)
    end
end

local function QueueButtonTextLayout(button)
    if not (C_Timer and C_Timer.After) then
        return
    end

    C_Timer.After(0, function()
        if button and button.mkMeetingStoneButtonArt then
            ButtonArt.Update(button)
        end
    end)
end

function ButtonArt.Update(button)
    if not button or not button.mkMeetingStoneButtonArt then
        return
    end

    local enabled = not (button.IsEnabled and not button:IsEnabled())
    local pressed = enabled and button.mkMeetingStoneButtonPressed == true
    local hovered = enabled and button.IsMouseOver and button:IsMouseOver()
    local state = pressed and "pressed" or (hovered and "hover" or "default")
    SetArtState(button, state, enabled)
    LayoutButtonText(button, pressed)

    local fontString = button.Text or (button.GetFontString and button:GetFontString())
    if fontString and fontString.SetTextColor then
        local color = GetButtonTextColor(enabled, pressed, hovered)
        fontString:SetTextColor(color[1], color[2], color[3], color[4])
    end
    ApplyButtonTextVisibility(button, fontString)
end

function ButtonArt.Apply(button)
    if not button or button.mkMeetingStoneButtonArtApplied then
        return
    end

    local sizeKey = GetButtonSizeKey(button)
    if not sizeKey then
        return
    end

    HideChrome(button)
    ResetNativeTextOffsets(button)

    local texture = button:CreateTexture(nil, "BACKGROUND")
    texture:SetAllPoints(button)
    button.mkMeetingStoneButtonArt = {
        texture = texture,
        sizeKey = sizeKey,
    }
    button.mkMeetingStoneButtonArtApplied = true

    button:HookScript("OnSizeChanged", function(self)
        local art = self.mkMeetingStoneButtonArt
        if art then
            art.state = nil
            art.sizeKey = nil
        end
        ButtonArt.Update(self)
    end)
    button:HookScript("OnEnter", ButtonArt.Update)
    button:HookScript("OnLeave", function(self)
        self.mkMeetingStoneButtonPressed = nil
        ButtonArt.Update(self)
    end)
    button:HookScript("OnMouseDown", function(self, mouseButton)
        if mouseButton == "LeftButton" then
            ResetNativeTextOffsets(self)
            self.mkMeetingStoneButtonPressed = true
            ButtonArt.Update(self)
        end
    end)
    button:HookScript("OnMouseUp", function(self)
        ResetNativeTextOffsets(self)
        self.mkMeetingStoneButtonPressed = nil
        ButtonArt.Update(self)
        QueueButtonTextLayout(self)
    end)
    button:HookScript("OnEnable", ButtonArt.Update)
    button:HookScript("OnDisable", ButtonArt.Update)
    button:HookScript("OnShow", ButtonArt.Update)

    ButtonArt.Update(button)
end
