require "ISUI/ISPanel"
require "ISUI/ISLabel"
require "ISUI/ISTextEntryBox"
require "MS/SettingPanel/MS_LongButton"
require "MS/SettingPanel/MS_SquareButton"
require "MS/SettingPanel/MS_PanelCollapseHelper"
require "MS/SettingPanel/MS_TitlePanelButtons"
require "MS/SettingPanel/MS_PresetsPanel"
require "MS/MS_GridManager"
require "MS/MS_IndicatorTooltipManager"
require "MS/MS_PerformanceManager"
require "MS/MS_MatrixManager"

MS_GlobalSettingsPanel = ISPanel:derive("MS_GlobalSettingsPanel")
MS_GlobalSettingsPanel.CurrentPanel = nil

local FONT_HGT_SMALL = getTextManager():getFontHeight(UIFont.Small)
local ACTIVE_ORANGE = { r = 0.95, g = 0.50, b = 0.10 }
local ACTIVE_GREEN = { r = 0.12, g = 0.75, b = 0.25 }

local function tr(key, fallback)
    if key and getTextOrNull then
        local value = getTextOrNull(key)
        if value and value ~= key then return value end
    end
    if key and getText then
        local ok, value = pcall(getText, key)
        if ok and value and value ~= key then return value end
    end
    return fallback or tostring(key or "")
end

local function applyTooltip(control, key, fallback)
    if control and control.setTooltip then
        local tooltip = tr(key, fallback)
        if tooltip and tooltip ~= "" then
            control:setTooltip(tooltip)
        end
    end
end

local function getIndicators(playerIndex)
    return StatusWidget and StatusWidget.indicators and StatusWidget.indicators[playerIndex] or nil
end

local function getIndicatorType(indicator, fallbackKey)
    if indicator and indicator.getType then
        return indicator:getType()
    end
    return fallbackKey
end

local function getGlobalControl(playerIndex)
    return StatusWidget and StatusWidget.globalControls and StatusWidget.globalControls[playerIndex] or nil
end

local function cloneTable(source)
    local result = {}
    for key, value in pairs(source or {}) do
        if type(value) == "table" then
            result[key] = cloneTable(value)
        else
            result[key] = value
        end
    end
    return result
end

local function getRestoreContainer(playerIndex)
    local globalConfig = MSConfig.getGlobalConfig(playerIndex) or {}
    if type(globalConfig.globalSettingsPanelRestore) ~= "table" then
        return {}
    end
    return globalConfig.globalSettingsPanelRestore
end

local function saveRestoreContainer(playerIndex, restore)
    MSConfig.updateGlobalConfig(playerIndex, "globalSettingsPanelRestore", restore or {})
end

local function captureVisibilityState(playerIndex)
    local state = {}
    local indicators = getIndicators(playerIndex)
    if indicators then
        for key, indicator in pairs(indicators) do
            local indicatorType = getIndicatorType(indicator, key)
            if indicatorType then
                state[indicatorType] = not (indicator.hidden == true)
            end
        end
    end
    return state
end

local function captureLockState(playerIndex)
    local state = {}
    local indicators = getIndicators(playerIndex)
    if indicators then
        for key, indicator in pairs(indicators) do
            local indicatorType = getIndicatorType(indicator, key)
            if indicatorType then
                state[indicatorType] = indicator.locked == true
            end
        end
    end
    return state
end

local function hasEntries(value)
    if type(value) ~= "table" then return false end
    for _ in pairs(value) do return true end
    return false
end

local function applyVisibilityMap(playerIndex, visibilityMap)
    if type(visibilityMap) ~= "table" then return end
    local indicators = getIndicators(playerIndex)
    if not indicators then return end

    for key, indicator in pairs(indicators) do
        local indicatorType = getIndicatorType(indicator, key)
        local visible = visibilityMap[indicatorType]
        if visible ~= nil then
            local hidden = not (visible == true)
            indicator.hidden = hidden
            indicator:setVisible(not hidden)
            MSConfig.updateIndicatorConfig(playerIndex, indicatorType, "hidden", hidden)
        end
    end

    if MS_MatrixManager and MS_MatrixManager.applyMatrixLayout then
        MS_MatrixManager.applyMatrixLayout(playerIndex)
    end
end

local function applyLockMap(playerIndex, lockMap)
    if type(lockMap) ~= "table" then return end
    local indicators = getIndicators(playerIndex)
    if not indicators then return end

    for key, indicator in pairs(indicators) do
        local indicatorType = getIndicatorType(indicator, key)
        local locked = lockMap[indicatorType]
        if locked ~= nil then
            indicator.locked = locked == true
            indicator.moveWithMouse = not indicator.locked
            MSConfig.updateIndicatorConfig(playerIndex, indicatorType, "locked", indicator.locked)
        end
    end
end

local function saveCurrentVisibilityRestore(playerIndex)
    local restore = cloneTable(getRestoreContainer(playerIndex))
    restore.visibility = captureVisibilityState(playerIndex)
    saveRestoreContainer(playerIndex, restore)
end

local function saveCurrentLockRestore(playerIndex)
    local restore = cloneTable(getRestoreContainer(playerIndex))
    restore.lock = captureLockState(playerIndex)
    saveRestoreContainer(playerIndex, restore)
end

function MS_GlobalSettingsPanel.storePresetRestoreState(playerIndex, config)
    -- Called by the preset manager after a preset has been merged into the saved config.
    -- This gives the panel Restore buttons a safe reference state from the last loaded preset.
    if type(config) ~= "table" or type(config.indicators) ~= "table" then return end

    local restore = cloneTable((config.globalSettings and config.globalSettings.globalSettingsPanelRestore) or {})
    restore.visibility = {}
    restore.lock = {}

    for indicatorType, indicatorConfig in pairs(config.indicators) do
        if type(indicatorConfig) == "table" then
            restore.visibility[indicatorType] = not (indicatorConfig.hidden == true)
            restore.lock[indicatorType] = indicatorConfig.locked == true
        end
    end

    config.globalSettings = config.globalSettings or {}
    config.globalSettings.globalSettingsPanelRestore = restore
end

function MS_GlobalSettingsPanel.openForPlayer(playerIndex, x, y)
    playerIndex = playerIndex or 0
    if MS_GlobalSettingsPanel.CurrentPanel and MS_GlobalSettingsPanel.CurrentPanel:getIsVisible() then
        MS_GlobalSettingsPanel.CurrentPanel:close()
        return nil
    end

    local width = 560
    local height = MS_GlobalSettingsPanel.calculateRequiredHeight and MS_GlobalSettingsPanel.calculateRequiredHeight() or 430
    local screenWidth = getCore():getScreenWidth()
    local screenHeight = getCore():getScreenHeight()
    x = x or math.floor((screenWidth - width) / 2)
    y = y or math.floor((screenHeight - height) / 2)
    x = math.max(10, math.min(screenWidth - width - 10, x))
    y = math.max(10, math.min(screenHeight - height - 10, y))

    local panel = MS_GlobalSettingsPanel:new(x, y, width, height, playerIndex)
    panel:initialise()
    panel:addToUIManager()
    panel:setVisible(true)
    MS_GlobalSettingsPanel.CurrentPanel = panel
    return panel
end

function MS_GlobalSettingsPanel.calculateRequiredHeight()
    return 474
end

function MS_GlobalSettingsPanel.isOpen()
    local panel = MS_GlobalSettingsPanel.CurrentPanel
    return panel ~= nil and panel.getIsVisible and panel:getIsVisible() == true
end

function MS_GlobalSettingsPanel:initialise()
    ISPanel.initialise(self)
end

function MS_GlobalSettingsPanel:new(x, y, width, height, playerIndex)
    local o = ISPanel:new(x, y, width, height)
    setmetatable(o, self)
    self.__index = self
    o:noBackground(true)
    o.playerIndex = playerIndex or 0
    o.padding = math.floor(FONT_HGT_SMALL * 0.2)
    o.titleBarHeight = math.floor(FONT_HGT_SMALL * 1.2)
    o.rowHeight = math.floor(FONT_HGT_SMALL * 1.55)
    o.buttonHeight = math.floor(FONT_HGT_SMALL * 1.2)
    o.buttonSize = math.floor(FONT_HGT_SMALL * 1.2)
    o.moveWithMouse = true
    o.moving = false
    o.MinusButton = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Minus.png")
    o.PlusButton = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Plus.png")
    o.closeIcon = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Close.png")
    return o
end

function MS_GlobalSettingsPanel:createChildren()
    local titleButtonSize = self.buttonSize
    MS_PanelCollapseHelper.addTitleBarButton(self, titleButtonSize, "GlobalSettingsPanel")

    self.titleLabel = ISLabel:new(MS_PanelCollapseHelper.getTitleLabelX(self, titleButtonSize), (self.titleBarHeight - FONT_HGT_SMALL) / 2, self.titleBarHeight, "Modern Status Global Settings", 1, 1, 1, 1, UIFont.Small, true)
    self.titleLabel:initialise()
    self:addChild(self.titleLabel)

    local closeSize = self.buttonSize
    self.closeButton = MS_SquareButton:new(self.width - closeSize - self.padding, (self.titleBarHeight - closeSize) / 2, closeSize, self.closeIcon, self, MS_GlobalSettingsPanel.close)
    self.closeButton:initialise()
    self.closeButton:setActive(true)
    self.closeButton:setActiveColor(0.8, 0.2, 0.2)
    applyTooltip(self.closeButton, "IGUI_ModernStatus_Tooltip_ClosePanel", "Close this panel.")
    self:addChild(self.closeButton)

    MS_TitlePanelButtons.addButtons(self, {"all", "matrix", "unlinked"}, closeSize)

    local y = self.titleBarHeight + self.padding
    y = self:createContent(y)
    self:setHeight(y + self.padding)
end

function MS_GlobalSettingsPanel:createLabel(text, x, y, width, tooltipKey, tooltipFallback)
    -- ISLabel already centers its text inside its height; using the row top keeps labels aligned with buttons.
    local label = ISLabel:new(x, y, self.rowHeight, text, 1, 1, 1, 1, UIFont.Small, true)
    label:initialise()
    label:setWidth(width or getTextManager():MeasureStringX(UIFont.Small, text))
    applyTooltip(label, tooltipKey, tooltipFallback)
    self:addChild(label)
    return label
end

function MS_GlobalSettingsPanel:createLongButton(x, y, width, text, callback, isActiveFn, tooltipKey, tooltipFallback)
    local button = MS_LongButton:new(x, y + ((self.rowHeight - self.buttonHeight) / 2), width, self.buttonHeight, text, self, callback)
    button:initialise()
    button:setActiveColor(ACTIVE_ORANGE.r, ACTIVE_ORANGE.g, ACTIVE_ORANGE.b)
    if isActiveFn then
        button.prerender = function(control)
            control:setActive(isActiveFn() == true)
            control:setActiveColor(ACTIVE_ORANGE.r, ACTIVE_ORANGE.g, ACTIVE_ORANGE.b)
        end
    end
    applyTooltip(button, tooltipKey, tooltipFallback)
    self:addChild(button)
    return button
end

function MS_GlobalSettingsPanel:createSquareButton(x, y, icon, callback, tooltipKey, tooltipFallback)
    local button = MS_SquareButton:new(x, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, icon, self, callback)
    button:initialise()
    button:setActiveColor(ACTIVE_ORANGE.r, ACTIVE_ORANGE.g, ACTIVE_ORANGE.b)
    applyTooltip(button, tooltipKey, tooltipFallback)
    self:addChild(button)
    return button
end

function MS_GlobalSettingsPanel:createNumberEntry(x, y, width, getValue, setValue, minValue, maxValue, step, tooltipKey, tooltipFallback)
    local function sanitize(value)
        value = tonumber(value) or getValue()
        value = math.floor(value + 0.0001)
        if value < minValue then value = minValue end
        if value > maxValue then value = maxValue end
        return value
    end

    local entry = ISTextEntryBox:new(tostring(sanitize(getValue())), x, y + ((self.rowHeight - self.buttonHeight) / 2), width, self.buttonHeight)
    entry:initialise()
    entry:instantiate()
    entry:setOnlyNumbers(true)
    entry.borderColor = { r = 0.4, g = 0.4, b = 0.4, a = 0.0 }
    entry.backgroundColor = { r = 0.1, g = 0.1, b = 0.1, a = 0.0 }

    local function submit()
        local value = sanitize(entry:getText())
        entry:setText(tostring(value))
        setValue(value)
    end

    entry.onCommandEntered = submit
    entry.onOtherKey = function(key) if key == 28 then submit() end end
    entry.onMouseDownOutside = submit
    entry.prerender = function(control)
        ISTextEntryBox.prerender(control)
        local current = tostring(sanitize(getValue()))
        if not control:isFocused() and control:getText() ~= current then
            control:setText(current)
        end
    end

    applyTooltip(entry, tooltipKey, tooltipFallback)
    self:addChild(entry)
    return entry
end

function MS_GlobalSettingsPanel:createStepper(x, y, getValue, setValue, minValue, maxValue, step, tooltipKey, tooltipFallback)
    local entryWidth = 42
    local dec = self:createSquareButton(x, y, self.MinusButton, function()
        setValue(math.max(minValue, getValue() - step))
    end, tooltipKey, tooltipFallback)
    local entry = self:createNumberEntry(x + self.buttonSize + self.padding, y, entryWidth, getValue, setValue, minValue, maxValue, step, tooltipKey, tooltipFallback)
    local inc = self:createSquareButton(x + self.buttonSize + self.padding + entryWidth + self.padding, y, self.PlusButton, function()
        setValue(math.min(maxValue, getValue() + step))
    end, tooltipKey, tooltipFallback)
    return { dec = dec, entry = entry, inc = inc, width = self.buttonSize * 2 + entryWidth + self.padding * 2 }
end

function MS_GlobalSettingsPanel:createContent(y)
    local left = self.padding
    local labelWidth = 112
    local x = left + labelWidth
    local fullWidth = self.width - self.padding * 2
    local contentWidth = self.width - x - self.padding
    local smallGap = self.padding

    self:createLongButton(left, y, fullWidth, tr("IGUI_ModernStatus_Presets", "Presets"), MS_GlobalSettingsPanel.onPresetsClick, nil, "IGUI_ModernStatus_Tooltip_GlobalSettingsPresets", "Open the Presets panel to load, save, overwrite, or delete Modern Status presets.")
    y = y + self.rowHeight

    self:createLabel(tr("IGUI_ModernStatus_Visibility", "Visibility"), left, y, labelWidth, "IGUI_ModernStatus_Tooltip_GlobalSettingsVisibility", "Show, hide, or restore indicator visibility in bulk.")
    local buttonW = math.floor((contentWidth - smallGap * 2) / 3)
    self:createLongButton(x, y, buttonW, tr("IGUI_ModernStatus_ShowAll", "Show All"), MS_GlobalSettingsPanel.onShowAllClick, nil, "IGUI_ModernStatus_Tooltip_GlobalSettingsShowAll", "Show all indicators and remember the previous visibility state for Restore.")
    self:createLongButton(x + buttonW + smallGap, y, buttonW, tr("IGUI_ModernStatus_HideAll", "Hide All"), MS_GlobalSettingsPanel.onHideAllClick, nil, "IGUI_ModernStatus_Tooltip_GlobalSettingsHideAll", "Hide all indicators and remember the previous visibility state for Restore.")
    self.visibilityRestoreButton = self:createLongButton(x + (buttonW + smallGap) * 2, y, buttonW, tr("IGUI_ModernStatus_Restore", "Restore"), MS_GlobalSettingsPanel.onRestoreVisibilityClick, function()
        return hasEntries(getRestoreContainer(self.playerIndex).visibility)
    end, "IGUI_ModernStatus_Tooltip_GlobalSettingsRestoreVisibility", "Restore the visibility saved before Show All/Hide All or from the last loaded preset.")
    y = y + self.rowHeight

    self:createLabel(tr("IGUI_ModernStatus_Lock", "Lock"), left, y, labelWidth, "IGUI_ModernStatus_Tooltip_GlobalSettingsLock", "Lock, unlock, or restore indicator movement lock states in bulk.")
    self:createLongButton(x, y, buttonW, tr("IGUI_ModernStatus_LockAll", "Lock All"), MS_GlobalSettingsPanel.onLockAllClick, nil, "IGUI_ModernStatus_Tooltip_GlobalSettingsLockAll", "Lock all indicators and remember the previous lock state for Restore.")
    self:createLongButton(x + buttonW + smallGap, y, buttonW, tr("IGUI_ModernStatus_UnlockAll", "Unlock All"), MS_GlobalSettingsPanel.onUnlockAllClick, nil, "IGUI_ModernStatus_Tooltip_GlobalSettingsUnlockAll", "Unlock all indicators and remember the previous lock state for Restore.")
    self.lockRestoreButton = self:createLongButton(x + (buttonW + smallGap) * 2, y, buttonW, tr("IGUI_ModernStatus_Restore", "Restore"), MS_GlobalSettingsPanel.onRestoreLockClick, function()
        return hasEntries(getRestoreContainer(self.playerIndex).lock)
    end, "IGUI_ModernStatus_Tooltip_GlobalSettingsRestoreLock", "Restore the lock states saved before Lock All/Unlock All or from the last loaded preset.")
    y = y + self.rowHeight

    self:createLabel(tr("IGUI_ModernStatus_Grid", "Grid"), left, y, labelWidth, "IGUI_ModernStatus_Tooltip_GlobalSettingsGrid", "Toggle grid snapping and adjust the grid size used when moving indicators.")
    local gridButtonW = 80
    self:createLongButton(x, y, gridButtonW, tr("IGUI_ModernStatus_Grid", "Grid"), MS_GlobalSettingsPanel.onGridToggleClick, function()
        return MS_GridManager.isEnabled(self.playerIndex)
    end, "IGUI_ModernStatus_Tooltip_GlobalSettingsGridToggle", "Enable or disable grid snapping when moving indicators.")
    local gx = x + gridButtonW + smallGap * 2
    self:createLabel("X", gx, y, 10, "IGUI_ModernStatus_Tooltip_GlobalSettingsGridX", "Horizontal grid size.")
    gx = gx + 16
    self:createStepper(gx, y,
        function() local sx = MS_GridManager.getDisplaySize(self.playerIndex); return sx end,
        function(value) local _, sy = MS_GridManager.getDisplaySize(self.playerIndex); MS_GridManager.setSize(self.playerIndex, value, sy) end,
        1, 50, 1, "IGUI_ModernStatus_Tooltip_GlobalSettingsGridX", "Horizontal grid size.")
    gx = gx + self.buttonSize * 2 + 42 + self.padding * 2 + smallGap * 2
    self:createLabel("Y", gx, y, 10, "IGUI_ModernStatus_Tooltip_GlobalSettingsGridY", "Vertical grid size.")
    gx = gx + 16
    self:createStepper(gx, y,
        function() local _, sy = MS_GridManager.getDisplaySize(self.playerIndex); return sy end,
        function(value) local sx = MS_GridManager.getDisplaySize(self.playerIndex); MS_GridManager.setSize(self.playerIndex, sx, value) end,
        1, 50, 1, "IGUI_ModernStatus_Tooltip_GlobalSettingsGridY", "Vertical grid size.")
    y = y + self.rowHeight

    self:createLongButton(left, y, fullWidth, tr("IGUI_ModernStatus_MultiDrag", "Multi Drag"), MS_GlobalSettingsPanel.onMultiDragClick, function()
        local config = MSConfig.getGlobalConfig(self.playerIndex) or {}
        return config.multiDragMode == true
    end, "IGUI_ModernStatus_Tooltip_GlobalSettingsMultiDrag", "Move multiple unlocked indicators together.")
    y = y + self.rowHeight

    self:createLabel(tr("IGUI_ModernStatus_PerformanceMode", "Performance Mode"), left, y, fullWidth, "IGUI_ModernStatus_Tooltip_GlobalSettingsPerformanceMode", "Choose how often Modern Status indicators update.")
    y = y + self.rowHeight
    local modes = {"responsive", "balanced", "lightweight", "custom"}
    local modeButtonW = math.floor((fullWidth - smallGap * 3) / 4)
    for index, mode in ipairs(modes) do
        local label = MS_PerformanceManager and MS_PerformanceManager.getModeLabel and MS_PerformanceManager.getModeLabel(mode) or mode
        local bx = left + (index - 1) * (modeButtonW + smallGap)
        self:createLongButton(bx, y, modeButtonW, label, function(panel)
            panel:onPerformanceModeClick(mode)
        end, function()
            return MS_PerformanceManager and MS_PerformanceManager.getCurrentMode and MS_PerformanceManager.getCurrentMode(self.playerIndex) == mode
        end, "IGUI_ModernStatus_Tooltip_GlobalSettingsPerformanceMode", "Choose how often Modern Status indicators update.")
    end
    y = y + self.rowHeight

    self:createLabel(tr("IGUI_ModernStatus_IndicatorTooltips", "Tooltips"), left, y, fullWidth, "IGUI_ModernStatus_Tooltip_GlobalSettingsTooltips", "Choose which sections appear in indicator tooltips.")
    y = self:createTooltipSection(y + self.rowHeight, left, fullWidth)

    self:createLongButton(left, y, fullWidth, tr("IGUI_ModernStatus_HideVanillaMoodles", "Hide Vanilla Moodles"), MS_GlobalSettingsPanel.onHideVanillaMoodlesClick, function()
        local config = MSConfig.getGlobalConfig(self.playerIndex) or {}
        return config.hideOriginalMoodles == true or config.hideVanillaMoodles == true or config.hideOriginalMoodle == true
    end, "IGUI_ModernStatus_Tooltip_GlobalSettingsHideVanillaMoodles", "Hide or show vanilla moodles while keeping Modern Status indicators visible.")
    y = y + self.rowHeight

    return y
end

function MS_GlobalSettingsPanel:createTooltipToggleButton(x, y, width, label, key, defaultValue, tooltipKey, tooltipFallback)
    self:createLongButton(x, y, width, label, function(panel)
        panel:onTooltipToggleClick(key, defaultValue)
    end, function()
        local settings = MS_IndicatorTooltipManager.getSettings(self.playerIndex)
        local value = settings[key]
        if value == nil then value = defaultValue end
        return value == true
    end, tooltipKey, tooltipFallback)
end

function MS_GlobalSettingsPanel:createTooltipSection(y, left, fullWidth)
    local gap = self.padding
    local buttonW = math.floor((fullWidth - gap * 2) / 3)
    local x = left

    self:createTooltipToggleButton(x, y, buttonW, tr("IGUI_ModernStatus_TooltipName", "Name"), "showName", true, "IGUI_ModernStatus_Tooltip_GlobalSettingsTooltipName", "Show the indicator name in tooltips.")
    self:createTooltipToggleButton(x + (buttonW + gap), y, buttonW, tr("IGUI_ModernStatus_TooltipValue", "Value"), "showValue", true, "IGUI_ModernStatus_Tooltip_GlobalSettingsTooltipValue", "Show the current indicator value in tooltips.")
    self:createTooltipToggleButton(x + (buttonW + gap) * 2, y, buttonW, tr("IGUI_ModernStatus_TooltipThresholds", "Thresholds"), "showThresholds", false, "IGUI_ModernStatus_Tooltip_GlobalSettingsTooltipThresholds", "Show relevant threshold information in tooltips.")
    y = y + self.rowHeight

    self:createTooltipToggleButton(x, y, buttonW, tr("IGUI_ModernStatus_AssociatedMoodle", "Associated Moodle"), "showAssociatedMoodle", true, "IGUI_ModernStatus_Tooltip_GlobalSettingsAssociatedMoodle", "Show the moodle associated with this indicator when available.")
    self:createTooltipToggleButton(x + (buttonW + gap), y, buttonW, tr("IGUI_ModernStatus_MoodleTitle", "Moodle Title"), "showMoodleTitle", true, "IGUI_ModernStatus_Tooltip_GlobalSettingsMoodleTitle", "Show the associated moodle title when available.")
    self:createTooltipToggleButton(x + (buttonW + gap) * 2, y, buttonW, tr("IGUI_ModernStatus_MoodleDescription", "Moodle Description"), "showMoodleDescription", true, "IGUI_ModernStatus_Tooltip_GlobalSettingsMoodleDescription", "Show the associated moodle description when available.")
    y = y + self.rowHeight

    self:createTooltipToggleButton(x, y, buttonW, tr("IGUI_ModernStatus_DetailedInfo", "Detailed Info"), "showDetailedInfo", true, "IGUI_ModernStatus_Tooltip_GlobalSettingsDetailedInfo", "Show extra contextual information when available.")
    y = y + self.rowHeight

    local labelWidth = math.floor(fullWidth * 0.45)
    self:createLabel(tr("IGUI_ModernStatus_SetOpacity", "Background Opacity"), x, y, labelWidth, "IGUI_ModernStatus_Tooltip_GlobalSettingsTooltipBackgroundOpacity", "Change the tooltip background opacity. Text remains fully readable.")
    self:createStepper(x + labelWidth + gap, y,
        function() return MS_IndicatorTooltipManager.getOpacity(self.playerIndex) end,
        function(value) MS_IndicatorTooltipManager.updateSetting(self.playerIndex, "opacity", value) end,
        0, 100, 5, "IGUI_ModernStatus_Tooltip_GlobalSettingsTooltipBackgroundOpacity", "Change the tooltip background opacity. Text remains fully readable.")
    y = y + self.rowHeight

    return y
end

function MS_GlobalSettingsPanel:onPresetsClick()
    if MS_PresetsPanel and MS_PresetsPanel.openForPlayer then
        MS_PresetsPanel.openForPlayer(self.playerIndex)
    end
end

function MS_GlobalSettingsPanel:onShowAllClick()
    saveCurrentVisibilityRestore(self.playerIndex)
    applyVisibilityMap(self.playerIndex, {})
    local indicators = getIndicators(self.playerIndex)
    if not indicators then return end
    for key, indicator in pairs(indicators) do
        local indicatorType = getIndicatorType(indicator, key)
        indicator.hidden = false
        indicator:setVisible(true)
        MSConfig.updateIndicatorConfig(self.playerIndex, indicatorType, "hidden", false)
    end
    if MS_MatrixManager and MS_MatrixManager.applyMatrixLayout then
        MS_MatrixManager.applyMatrixLayout(self.playerIndex)
    end
end

function MS_GlobalSettingsPanel:onHideAllClick()
    saveCurrentVisibilityRestore(self.playerIndex)
    local indicators = getIndicators(self.playerIndex)
    if not indicators then return end
    for key, indicator in pairs(indicators) do
        local indicatorType = getIndicatorType(indicator, key)
        indicator.hidden = true
        indicator:setVisible(false)
        MSConfig.updateIndicatorConfig(self.playerIndex, indicatorType, "hidden", true)
    end
    if MS_MatrixManager and MS_MatrixManager.applyMatrixLayout then
        MS_MatrixManager.applyMatrixLayout(self.playerIndex)
    end
end

function MS_GlobalSettingsPanel:onRestoreVisibilityClick()
    local restore = getRestoreContainer(self.playerIndex)
    if hasEntries(restore.visibility) then
        applyVisibilityMap(self.playerIndex, restore.visibility)
    end
end

function MS_GlobalSettingsPanel:onLockAllClick()
    saveCurrentLockRestore(self.playerIndex)
    local indicators = getIndicators(self.playerIndex)
    if not indicators then return end
    for key, indicator in pairs(indicators) do
        local indicatorType = getIndicatorType(indicator, key)
        indicator.locked = true
        indicator.moveWithMouse = false
        MSConfig.updateIndicatorConfig(self.playerIndex, indicatorType, "locked", true)
    end
end

function MS_GlobalSettingsPanel:onUnlockAllClick()
    saveCurrentLockRestore(self.playerIndex)
    local indicators = getIndicators(self.playerIndex)
    if not indicators then return end
    for key, indicator in pairs(indicators) do
        local indicatorType = getIndicatorType(indicator, key)
        indicator.locked = false
        indicator.moveWithMouse = true
        MSConfig.updateIndicatorConfig(self.playerIndex, indicatorType, "locked", false)
    end
end

function MS_GlobalSettingsPanel:onRestoreLockClick()
    local restore = getRestoreContainer(self.playerIndex)
    if hasEntries(restore.lock) then
        applyLockMap(self.playerIndex, restore.lock)
    end
end

function MS_GlobalSettingsPanel:onGridToggleClick()
    MS_GridManager.setEnabled(self.playerIndex, not MS_GridManager.isEnabled(self.playerIndex))
end

function MS_GlobalSettingsPanel:onMultiDragClick()
    local config = MSConfig.getGlobalConfig(self.playerIndex) or {}
    MSConfig.updateGlobalConfig(self.playerIndex, "multiDragMode", not (config.multiDragMode == true))
end

function MS_GlobalSettingsPanel:onPerformanceModeClick(mode)
    if MS_PerformanceManager and MS_PerformanceManager.setMode then
        MS_PerformanceManager.setMode(self.playerIndex, mode)
    end
end

function MS_GlobalSettingsPanel:onTooltipToggleClick(key, defaultValue)
    local settings = MS_IndicatorTooltipManager.getSettings(self.playerIndex)
    local value = settings[key]
    if value == nil then value = defaultValue end
    MS_IndicatorTooltipManager.updateSetting(self.playerIndex, key, not (value == true))
end

function MS_GlobalSettingsPanel:onHideVanillaMoodlesClick()
    local config = MSConfig.getGlobalConfig(self.playerIndex) or {}
    local newValue = not (config.hideOriginalMoodles == true or config.hideVanillaMoodles == true or config.hideOriginalMoodle == true)
    MSConfig.updateGlobalConfig(self.playerIndex, "hideOriginalMoodles", newValue)
    MSConfig.updateGlobalConfig(self.playerIndex, "hideVanillaMoodles", newValue)

    local control = getGlobalControl(self.playerIndex)
    if control and control.applyHideOriginalMoodles then
        control:applyHideOriginalMoodles(newValue)
    end
    if StatusWidget and StatusWidget.pendingMoodleVisibility then
        StatusWidget.pendingMoodleVisibility[self.playerIndex] = { control = control, shouldHide = newValue, ticks = 180 }
    end
end

function MS_GlobalSettingsPanel:close()
    self:setVisible(false)
    self:removeFromUIManager()
    if MS_GlobalSettingsPanel.CurrentPanel == self then
        MS_GlobalSettingsPanel.CurrentPanel = nil
    end
end

function MS_GlobalSettingsPanel:onMouseDown(x, y)
    if y <= self.titleBarHeight then
        self.moving = true
        self:setCapture(true)
        return true
    end
    return ISPanel.onMouseDown(self, x, y)
end

function MS_GlobalSettingsPanel:onMouseUp(x, y)
    if self.moving then
        self.moving = false
        self:setCapture(false)
        return true
    end
    return ISPanel.onMouseUp(self, x, y)
end

function MS_GlobalSettingsPanel:onMouseMove(dx, dy)
    if self.moving then
        self:setX(self.x + dx)
        self:setY(self.y + dy)
        return true
    end
    return ISPanel.onMouseMove(self, dx, dy)
end

function MS_GlobalSettingsPanel:onMouseMoveOutside(dx, dy)
    if self.moving then
        self:setX(self.x + dx)
        self:setY(self.y + dy)
        return true
    end
    return ISPanel.onMouseMoveOutside(self, dx, dy)
end

function MS_GlobalSettingsPanel:onMouseUpOutside(x, y)
    if self.moving then
        self.moving = false
        self:setCapture(false)
    end
    if MS_PanelCollapseHelper.onMouseUpOutside(self) then
        return true
    end
    return ISPanel.onMouseUpOutside(self, x, y)
end

function MS_GlobalSettingsPanel:prerender()
    MS_PanelCollapseHelper.updateCollapsedPreviewFromMouse(self)

    local titlebarbkg = self.titlebarbkg
    if not titlebarbkg and NinePatchTexture and NinePatchTexture.getSharedTexture then
        titlebarbkg = NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/TitleBarBG.png")
        self.titlebarbkg = titlebarbkg
    end

    if titlebarbkg and titlebarbkg.render then
        titlebarbkg:render(self:getAbsoluteX(), self:getAbsoluteY(), self:getWidth(), self.titleBarHeight, 0.5, 0.5, 0.5, 0.95)
    else
        self:drawRect(0, 0, self:getWidth(), self.titleBarHeight, 0.95, 0.5, 0.5, 0.5)
    end

    if MS_PanelCollapseHelper.isContentHidden(self) then
        return
    end

    local contentbkg = self.contentbkg
    if not contentbkg and NinePatchTexture and NinePatchTexture.getSharedTexture then
        contentbkg = NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/ContentBackground.png")
        self.contentbkg = contentbkg
    end

    if contentbkg and contentbkg.render then
        contentbkg:render(self:getAbsoluteX(), self:getAbsoluteY() + self.titleBarHeight, self:getWidth(), self:getHeight() - self.titleBarHeight, 0.1, 0.1, 0.1, 0.8)
    else
        self:drawRect(0, self.titleBarHeight, self:getWidth(), self:getHeight() - self.titleBarHeight, 0.8, 0.1, 0.1, 0.1)
    end
end

return MS_GlobalSettingsPanel
