-- CastBarConfig.lua
-- Simple addon to configure PlayerCastingBarFrame and its sub-elements

local ADDON_NAME = ...


local defaults = {
    scale = 1.2,
    alpha = 1,
    x = 0,
    y = 0,
    height = 32,
    castTimeTextX = 0,
    castTimeTextY = 0,
    castNameTextX = 0,
    castNameTextY = 0,
    hideTextBorder = false,
    iconSize = 32,
    iconX = -36,
    iconY = 0,
    hideBorderMask = false,
    gradientStart = {r=1, g=0.7, b=0, a=1}, -- default orange
    gradientEnd = {r=1, g=0, b=0, a=1},     -- default red
}

local function ApplyConfig()
    local f = PlayerCastingBarFrame
    if not f then return end
    f:SetScale(CastBarConfigDB.scale or defaults.scale)
    f:SetAlpha(CastBarConfigDB.alpha or defaults.alpha)
    f:SetHeight(CastBarConfigDB.height or defaults.height)
    -- Only set position if not managed by Edit Mode or not user-placed
    local isEditMode = false
    if EditModeManagerFrame and EditModeManagerFrame.ShouldUseEditMode and type(EditModeManagerFrame.ShouldUseEditMode) == "function" then
        if EditModeManagerFrame:ShouldUseEditMode() and f.layoutIndex then
            isEditMode = true
        end
    elseif f.isManagedFrame then
        isEditMode = true
    end
    if not isEditMode and not f:IsUserPlaced() then
        f:ClearAllPoints()
        f:SetPoint("CENTER", UIParent, "CENTER", CastBarConfigDB.x or defaults.x, CastBarConfigDB.y or defaults.y)
    end

    -- Make Spark match castbar height
    if f.Spark then
        f.Spark:SetHeight(f:GetHeight())
    end

    -- Move CastTimeText if it exists
    if f.CastTimeText then
        f.CastTimeText:ClearAllPoints()
        f.CastTimeText:SetPoint("RIGHT", f, "RIGHT", CastBarConfigDB.castTimeTextX or defaults.castTimeTextX, CastBarConfigDB.castTimeTextY or defaults.castTimeTextY)
        -- Set outlined font
        local font, size = f.CastTimeText:GetFont()
        f.CastTimeText:SetFont(font or "Fonts\\FRIZQT__.TTF", size or 12, "OUTLINE")
    end
    -- Move Cast Name Text if it exists
    if f.Text then
        f.Text:ClearAllPoints()
        f.Text:SetPoint("LEFT", f, "LEFT", CastBarConfigDB.castNameTextX or defaults.castNameTextX, CastBarConfigDB.castNameTextY or defaults.castNameTextY)
        -- Set outlined font
        local font, size = f.Text:GetFont()
        f.Text:SetFont(font or "Fonts\\FRIZQT__.TTF", size or 12, "OUTLINE")
    end

    -- Hide or show TextBorder
    if f.TextBorder then
        if CastBarConfigDB.hideTextBorder then
            f.TextBorder:Hide()
        else
            f.TextBorder:Show()
        end
    end
    -- Hide or show Border/Mask
    if f.Border then
        if CastBarConfigDB.hideBorderMask then
            f.Border:Hide()
        else
            f.Border:Show()
        end
    end
    if f.BorderMask then
        if CastBarConfigDB.hideBorderMask then
            f.BorderMask:Hide()
        else
            f.BorderMask:Show()
        end
    end

    -- Custom 1-pixel border
    if not f.CustomBorder then
        local border = CreateFrame("Frame", nil, f, "BackdropTemplate")
        border:SetFrameLevel(f:GetFrameLevel() + 10)
        f.CustomBorder = border
        border:SetBackdrop({
            edgeFile = "Interface/Buttons/WHITE8x8",
            edgeSize = 1,
        })
        border:SetBackdropBorderColor(0, 0, 0, 1)
    end
    f.CustomBorder:SetPoint("TOPLEFT", f, "TOPLEFT", -1, 1)
    f.CustomBorder:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 1, -1)
    if CastBarConfigDB.hideBorderMask then
        f.CustomBorder:Show()
    else
        f.CustomBorder:Hide()
    end

    -- Cast Icon
    if not f.CastIcon then
        local icon = f:CreateTexture(nil, "ARTWORK")
        icon:SetSize(CastBarConfigDB.iconSize or defaults.iconSize, CastBarConfigDB.iconSize or defaults.iconSize)
        icon:SetPoint("LEFT", f, "LEFT", CastBarConfigDB.iconX or defaults.iconX, CastBarConfigDB.iconY or defaults.iconY)
        f.CastIcon = icon
    else
        f.CastIcon:SetSize(CastBarConfigDB.iconSize or defaults.iconSize, CastBarConfigDB.iconSize or defaults.iconSize)
        f.CastIcon:ClearAllPoints()
        f.CastIcon:SetPoint("LEFT", f, "LEFT", CastBarConfigDB.iconX or defaults.iconX, CastBarConfigDB.iconY or defaults.iconY)
    end

    -- Gradient application
    local start = CastBarConfigDB.gradientStart or defaults.gradientStart
    local finish = CastBarConfigDB.gradientEnd or defaults.gradientEnd
    if f:GetStatusBarTexture() and f:GetStatusBarTexture().SetGradient then
        f:GetStatusBarTexture():SetGradient("HORIZONTAL",
            CreateColor(start.r, start.g, start.b, start.a),
            CreateColor(finish.r, finish.g, finish.b, finish.a)
        )
    end
end
-- Color Picker helper
local function CreateColorPicker(parent, labelText, initialColor, onChanged)
    local container = CreateFrame("Frame", nil, parent)
    container:SetSize(210, 28)

    local label = container:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    label:SetPoint("LEFT", 0, 0)
    label:SetText(labelText)
    label:SetTextColor(0.85, 0.7, 1, 1)

    local swatch = CreateFrame("Button", nil, container)
    swatch:SetSize(24, 24)
    swatch:SetPoint("LEFT", label, "RIGHT", 8, 0)
    swatch.bg = swatch:CreateTexture(nil, "BACKGROUND")
    swatch.bg:SetAllPoints()
    swatch.bg:SetColorTexture(initialColor.r, initialColor.g, initialColor.b, initialColor.a)

    swatch:SetScript("OnClick", function()
        local function ColorCallback(restore)
            local r, g, b, a
            if restore then
                r, g, b, a = unpack(restore)
            else
                if ColorPickerFrame.GetColorRGB then
                    r, g, b = ColorPickerFrame:GetColorRGB()
                else
                    r, g, b = ColorPickerFrame.r, ColorPickerFrame.g, ColorPickerFrame.b
                end
                if _G.OpacitySliderFrame and _G.OpacitySliderFrame.GetValue then
                    a = 1 - OpacitySliderFrame:GetValue()
                else
                    a = initialColor.a or 1
                end
            end
            swatch.bg:SetColorTexture(r, g, b, a)
            onChanged({r=r, g=g, b=b, a=a})
        end
        ColorPickerFrame.hasOpacity = true
        ColorPickerFrame.opacity = 1 - (initialColor.a or 1)
        ColorPickerFrame.previousValues = {initialColor.r, initialColor.g, initialColor.b, initialColor.a}
        ColorPickerFrame.func = ColorCallback
        ColorPickerFrame.opacityFunc = ColorCallback
        ColorPickerFrame.cancelFunc = ColorCallback
        ColorPickerFrame.swatchFunc = ColorCallback
        if ColorPickerFrame.SetColorRGB then
            ColorPickerFrame:SetColorRGB(initialColor.r, initialColor.g, initialColor.b)
        else
            ColorPickerFrame.r, ColorPickerFrame.g, ColorPickerFrame.b = initialColor.r, initialColor.g, initialColor.b
        end
        ColorPickerFrame:Show()
    end)

    -- Expose a method to update the color from outside
    container.UpdateValue = function(color)
        swatch.bg:SetColorTexture(color.r, color.g, color.b, color.a)
    end

    return container
end


local function ResetConfig()
    CastBarConfigDB = CopyTable(defaults)
    ApplyConfig()
end


-- Compact slider with input

local function CreateSlider(parent, labelText, min, max, step, initial, onChanged)
    local container = CreateFrame("Frame", nil, parent)
    container:SetSize(320, 44)

    local label = container:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    label:SetPoint("TOPLEFT", 0, 0)
    label:SetText(labelText)
    label:SetTextColor(0.85, 0.7, 1, 1)

    -- Blizzard-style slider
    local slider = CreateFrame("Slider", nil, container, "OptionsSliderTemplate")
    slider:SetPoint("TOPLEFT", 0, -18)
    slider:SetSize(160, 18)
    slider:SetMinMaxValues(min, max)
    slider:SetValueStep(step)
    slider:SetObeyStepOnDrag(true)
    slider:SetValue(initial)

    -- Value label (right of slider, always visible)
    local valueLabel = container:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    valueLabel:SetPoint("LEFT", slider, "RIGHT", 10, 0)
    valueLabel:SetWidth(40)
    valueLabel:SetJustifyH("LEFT")
    valueLabel:SetText(tostring(initial))

    -- Wide EditBox for direct input
    local input = CreateFrame("EditBox", nil, container, "InputBoxTemplate")
    input:SetSize(60, 24)
    input:SetPoint("LEFT", valueLabel, "RIGHT", 8, 0)
    input:SetAutoFocus(false)
    input:SetMaxLetters(8)
    input:SetFontObject(GameFontHighlight)
    input:SetText(tostring(initial))
    input:SetJustifyH("LEFT")
    input:Show()

    -- Style EditBox for clarity
    input.Left:SetVertexColor(0.2,0.2,0.2,1)
    input.Middle:SetVertexColor(0.2,0.2,0.2,1)
    input.Right:SetVertexColor(0.2,0.2,0.2,1)

    local function SetValue(val)
        if step >= 1 then val = math.floor(val + 0.5) end
        slider:SetValue(val)
        valueLabel:SetText(tostring(val))
        input:SetText(tostring(val))
    end
    SetValue(initial)

    slider:SetScript("OnValueChanged", function(self, val)
        valueLabel:SetText(tostring(val))
        input:SetText(tostring(val))
        onChanged(val)
    end)

    input:SetScript("OnEnterPressed", function(self)
        local val = tonumber(self:GetText())
        if val and val >= min and val <= max then
            slider:SetValue(val)
        else
            self:SetText(tostring(slider:GetValue()))
        end
        self:ClearFocus()
    end)

    input:SetScript("OnEscapePressed", function(self)
        self:SetText(tostring(slider:GetValue()))
        self:ClearFocus()
    end)

    input:SetScript("OnEditFocusLost", function(self)
        self:SetText(tostring(slider:GetValue()))
    end)

    -- Keep input and value label in sync if shown
    container:SetScript("OnShow", function()
        valueLabel:SetText(tostring(slider:GetValue()))
        input:SetText(tostring(slider:GetValue()))
    end)

    -- Expose a method to update the value from outside
    container.UpdateValue = function(val)
        SetValue(val)
    end

    return container, slider, input
end

local optionsPanel
function CreateOptionsPanel()
    if optionsPanel then return optionsPanel end
    optionsPanel = CreateFrame("Frame", "CastBarConfigOptionsPanel", InterfaceOptionsFramePanelContainer or UIParent)
    optionsPanel.name = "CastBarConfig"
    optionsPanel:SetSize(400, 600)

    -- ScrollFrame setup
    local scrollFrame = CreateFrame("ScrollFrame", nil, optionsPanel, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", 0, 0)
    scrollFrame:SetPoint("BOTTOMRIGHT", 0, 0)

    local content = CreateFrame("Frame", nil, scrollFrame)
    content:SetSize(380, 1000)
    scrollFrame:SetScrollChild(content)
    optionsPanel._content = content

    local y = -20
    -- Add logo at the top
    local logo = content:CreateTexture(nil, "ARTWORK")
    logo:SetTexture("Interface/AddOns/CastBarConfig/CK_logo.tga")
    logo:SetSize(128, 64)
    logo:SetPoint("TOP", -40, y)
    -- Add note next to logo
    local note = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    note:SetPoint("LEFT", logo, "RIGHT", 16, 0)
    note:SetText("Made by Ckraigfriend")
    note:SetTextColor(0.8, 0.95, 1, 1)
    note:SetJustifyH("LEFT")
    y = y - 70
    local sliders = {}
    local function AddWidget(widget, key)
        widget:SetPoint("TOPLEFT", 20, y)
        y = y - widget:GetHeight() - 12
        if key then sliders[key] = widget end
    end

    local title = content:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOP", 0, y)
    title:SetText("CastBarConfig")
    title:SetTextColor(1, 1, 1, 1)
    y = y - 36

    -- Scale
    AddWidget((function() local w = select(1, CreateSlider(content, "Scale", 0.5, 2, 0.01, CastBarConfigDB.scale or defaults.scale, function(v) CastBarConfigDB.scale = v; ApplyConfig() end)); return w end)(), "scale")
    -- Alpha
    AddWidget((function() local w = select(1, CreateSlider(content, "Alpha", 0.1, 1, 0.01, CastBarConfigDB.alpha or defaults.alpha, function(v) CastBarConfigDB.alpha = v; ApplyConfig() end)); return w end)(), "alpha")
    -- Height
    AddWidget((function() local w = select(1, CreateSlider(content, "Height", 8, 128, 1, CastBarConfigDB.height or defaults.height, function(v) CastBarConfigDB.height = v; ApplyConfig() end)); return w end)(), "height")
    -- X Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "X Offset", -500, 500, 1, CastBarConfigDB.x or defaults.x, function(v) CastBarConfigDB.x = v; ApplyConfig() end)); return w end)(), "x")
    -- Y Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "Y Offset", -500, 500, 1, CastBarConfigDB.y or defaults.y, function(v) CastBarConfigDB.y = v; ApplyConfig() end)); return w end)(), "y")
    -- Cast Time Text X Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "Cast Time Text X", -200, 200, 1, CastBarConfigDB.castTimeTextX or defaults.castTimeTextX, function(v) CastBarConfigDB.castTimeTextX = v; ApplyConfig() end)); return w end)(), "castTimeTextX")
    -- Cast Time Text Y Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "Cast Time Text Y", -200, 200, 1, CastBarConfigDB.castTimeTextY or defaults.castTimeTextY, function(v) CastBarConfigDB.castTimeTextY = v; ApplyConfig() end)); return w end)(), "castTimeTextY")
    -- Cast Name Text X Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "Cast Name Text X", -200, 200, 1, CastBarConfigDB.castNameTextX or defaults.castNameTextX, function(v) CastBarConfigDB.castNameTextX = v; ApplyConfig() end)); return w end)(), "castNameTextX")
    -- Cast Name Text Y Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "Cast Name Text Y", -200, 200, 1, CastBarConfigDB.castNameTextY or defaults.castNameTextY, function(v) CastBarConfigDB.castNameTextY = v; ApplyConfig() end)); return w end)(), "castNameTextY")
    -- Cast Icon Size
    AddWidget((function() local w = select(1, CreateSlider(content, "Cast Icon Size", 8, 64, 1, CastBarConfigDB.iconSize or defaults.iconSize, function(v) CastBarConfigDB.iconSize = v; ApplyConfig() end)); return w end)(), "iconSize")
    -- Cast Icon X Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "Cast Icon X", -200, 200, 1, CastBarConfigDB.iconX or defaults.iconX, function(v) CastBarConfigDB.iconX = v; ApplyConfig() end)); return w end)(), "iconX")
    -- Cast Icon Y Offset
    AddWidget((function() local w = select(1, CreateSlider(content, "Cast Icon Y", -200, 200, 1, CastBarConfigDB.iconY or defaults.iconY, function(v) CastBarConfigDB.iconY = v; ApplyConfig() end)); return w end)(), "iconY")


    -- Gradient Color Pickers
    AddWidget((function()
        local w = CreateColorPicker(content, "Gradient Start Color", CastBarConfigDB.gradientStart or defaults.gradientStart, function(color)
            CastBarConfigDB.gradientStart = color
            ApplyConfig()
        end)
        return w
    end)(), "gradientStart")
    AddWidget((function()
        local w = CreateColorPicker(content, "Gradient End Color", CastBarConfigDB.gradientEnd or defaults.gradientEnd, function(color)
            CastBarConfigDB.gradientEnd = color
            ApplyConfig()
        end)
        return w
    end)(), "gradientEnd")

    -- Hide Text Border checkbox
    local function CreateCheckbox(parent, labelText, initial, onChanged)
        local container = CreateFrame("Frame", nil, parent)
        container:SetSize(210, 24)

        local checkbox = CreateFrame("CheckButton", nil, container, "UICheckButtonTemplate")
        checkbox:SetPoint("LEFT", 0, 0)
        checkbox:SetSize(20, 20)
        checkbox:SetChecked(initial)

        local label = container:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        label:SetPoint("LEFT", checkbox, "RIGHT", 8, 0)
        label:SetText(labelText)
        label:SetTextColor(0.85, 0.7, 1, 1)

        checkbox:SetScript("OnClick", function(self)
            local checked = self:GetChecked() and true or false
            onChanged(checked)
        end)

        return container
    end

    AddWidget(CreateCheckbox(content, "Hide Text Border", CastBarConfigDB.hideTextBorder == true, function(v)
        CastBarConfigDB.hideTextBorder = v
        ApplyConfig()
    end))


    -- Hide Border/Mask checkbox
    AddWidget(CreateCheckbox(content, "Hide Border/Mask", CastBarConfigDB.hideBorderMask or defaults.hideBorderMask, function(v)
        CastBarConfigDB.hideBorderMask = v
        ApplyConfig()
    end))

    -- Reset button
    local resetBtn = CreateFrame("Button", nil, content, "UIPanelButtonTemplate")
    resetBtn:SetSize(140, 24)
    resetBtn:SetPoint("TOPLEFT", 20, y)
    resetBtn:SetText("Reset to Defaults")
    resetBtn:SetScript("OnClick", function()
        ResetConfig()
        for _, child in ipairs({content:GetChildren()}) do
            if child:IsObjectType("Frame") and child:GetScript("OnShow") then
                child:GetScript("OnShow")(child)
            end
        end
    end)

    -- When the panel is shown, update all slider values from saved variables
    optionsPanel:HookScript("OnShow", function()
        sliders.scale.UpdateValue(CastBarConfigDB.scale or defaults.scale)
        sliders.alpha.UpdateValue(CastBarConfigDB.alpha or defaults.alpha)
        sliders.height.UpdateValue(CastBarConfigDB.height or defaults.height)
        sliders.x.UpdateValue(CastBarConfigDB.x or defaults.x)
        sliders.y.UpdateValue(CastBarConfigDB.y or defaults.y)
        sliders.castTimeTextX.UpdateValue(CastBarConfigDB.castTimeTextX or defaults.castTimeTextX)
        sliders.castTimeTextY.UpdateValue(CastBarConfigDB.castTimeTextY or defaults.castTimeTextY)
        sliders.castNameTextX.UpdateValue(CastBarConfigDB.castNameTextX or defaults.castNameTextX)
        sliders.castNameTextY.UpdateValue(CastBarConfigDB.castNameTextY or defaults.castNameTextY)
        sliders.iconSize.UpdateValue(CastBarConfigDB.iconSize or defaults.iconSize)
        sliders.iconX.UpdateValue(CastBarConfigDB.iconX or defaults.iconX)
        sliders.iconY.UpdateValue(CastBarConfigDB.iconY or defaults.iconY)
        if sliders.gradientStart then sliders.gradientStart.UpdateValue(CastBarConfigDB.gradientStart or defaults.gradientStart) end
        if sliders.gradientEnd then sliders.gradientEnd.UpdateValue(CastBarConfigDB.gradientEnd or defaults.gradientEnd) end
    end)

    -- Register options panel for both Dragonflight+ and Classic/older
    if Settings and Settings.RegisterCanvasLayoutCategory and Settings.RegisterAddOnCategory then
        local category = Settings.RegisterCanvasLayoutCategory(optionsPanel, "CastBarConfig")
        Settings.RegisterAddOnCategory(category)
    elseif InterfaceOptions_AddCategory then
        InterfaceOptions_AddCategory(optionsPanel)
    end
    return optionsPanel
end

local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("ZONE_CHANGED_NEW_AREA")
f:RegisterEvent("PLAYER_ENTERING_BATTLEGROUND")
f:SetScript("OnEvent", function(self, event, ...)
    if event == "PLAYER_LOGIN" then
        if type(CastBarConfigDB) ~= "table" then
            CastBarConfigDB = CopyTable(defaults)
        end
        CreateOptionsPanel()
    end
    -- Always re-apply config on any registered event
    ApplyConfig()
    -- Hook cast events to update icon
    local bar = PlayerCastingBarFrame
    if bar and bar.CastIcon == nil then
        ApplyConfig() -- ensure icon exists
    end
    if bar and not bar._iconHooked then
        bar:HookScript("OnEvent", function(self, event, unit, ...)
            if (event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START") and unit == "player" then
                local name, _, icon = UnitCastingInfo("player")
                if not name then
                    name, _, icon = UnitChannelInfo("player")
                end
                -- Try to get empower spell icon if still nil
                if (not icon or icon == 0) and C_CastingBar and C_CastingBar.GetEmpowerSpellInfo then
                    local empowerInfo = C_CastingBar.GetEmpowerSpellInfo("player")
                    if empowerInfo and empowerInfo.icon then
                        icon = empowerInfo.icon
                    end
                end
                if icon and self.CastIcon then
                    self.CastIcon:SetTexture(icon)
                    self.CastIcon:Show()
                end
            elseif (event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP") and unit == "player" then
                if self.CastIcon then
                    self.CastIcon:Hide()
                end
            end
        end)
        bar._iconHooked = true
    end
end)
