require "ISUI/ISPanel"
require "ISUI/ISButton"
require "ISUI/ISTickBox"
require "ISUI/ISTextEntryBox"
require "ISUI/ISLabel"
require "MS/SettingPanel/MS_SquareButton"
require "MS/SettingPanel/MS_PanelCollapseHelper"
require "MS/SettingPanel/MS_TitlePanelButtons"
require "MS/SettingPanel/MS_LongButton"
require "MS/MS_MatrixManager"
require "MS/MS_IndicatorVisualConfig"
require "MS/MS_PerformanceManager"

MS_UnlinkedStylePanel = ISPanel:derive("MS_UnlinkedStylePanel")

local FONT_HGT_SMALL = getTextManager():getFontHeight(UIFont.Small)
local EPSILON = 0.0001


local function applyOptionTooltip(control, tooltip)
    if control and tooltip and control.setTooltip then
        control:setTooltip(tooltip)
    end
end

MS_UnlinkedStylePanel.CurrentPanel = nil

local function supportsThresholdsForIndicator(indicator)
    if not indicator or not indicator.getType then return false end
    local indicatorType = indicator:getType()
    if indicatorType == "MS_CarryWeightIndicator" or indicatorType == "ISCarryWeightStatusIndicator" then
        return true
    end
    return MS_IndicatorVisualConfig and MS_IndicatorVisualConfig.supportsThresholds(indicatorType) or false
end

local function supportsDirectionInversionForIndicator(indicator)
    if not indicator or not indicator.getType then return false end
    return MS_IndicatorVisualConfig and MS_IndicatorVisualConfig.supportsDirectionInversion and MS_IndicatorVisualConfig.supportsDirectionInversion(indicator:getType()) or false
end

local function getIndicatorMode(indicator)
    if not indicator then return "circular" end
    if not indicator.isBarStyle then return "circular" end
    return indicator.isVertical and "vertical" or "horizontal"
end

local function getOrderedIndicators(playerIndex)
    local indicators = StatusWidget and StatusWidget.indicators and StatusWidget.indicators[playerIndex]
    if not indicators then return {} end

    local ordered = {}
    for _, indicator in pairs(indicators) do
        table.insert(ordered, indicator)
    end

    table.sort(ordered, function(a, b)
        local aType = a:getType()
        local bType = b:getType()
        local aTitle = MS_MatrixManager.getIndicatorDisplayTitle(aType, aType)
        local bTitle = MS_MatrixManager.getIndicatorDisplayTitle(bType, bType)
        if aTitle == bTitle then
            return aType < bType
        end
        return aTitle < bTitle
    end)

    return ordered
end

local function getVisibleUnlinkedIndicators(playerIndex)
    local result = {}
    for _, indicator in ipairs(getOrderedIndicators(playerIndex)) do
        if not indicator.matrixLinked and not indicator.hidden then
            table.insert(result, indicator)
        end
    end
    return result
end

local function getUnlockedUnlinkedIndicators(playerIndex)
    local result = {}
    for _, indicator in ipairs(getOrderedIndicators(playerIndex)) do
        if not indicator.matrixLinked and not indicator.locked then
            table.insert(result, indicator)
        end
    end
    return result
end

local function getEditableUnlinkedIndicators(playerIndex)
    local result = {}
    for _, indicator in ipairs(getOrderedIndicators(playerIndex)) do
        if not indicator.matrixLinked and not indicator.hidden and not indicator.locked then
            table.insert(result, indicator)
        end
    end
    return result
end

local function getEditableThresholdIndicators(playerIndex)
    local result = {}
    for _, indicator in ipairs(getEditableUnlinkedIndicators(playerIndex)) do
        if supportsThresholdsForIndicator(indicator) then
            table.insert(result, indicator)
        end
    end
    return result
end

local function getEditableInvertibleUnlinkedIndicators(playerIndex)
    local result = {}
    for _, indicator in ipairs(getEditableUnlinkedIndicators(playerIndex)) do
        if supportsDirectionInversionForIndicator(indicator) then
            table.insert(result, indicator)
        end
    end
    return result
end

local function valuesEqual(a, b)
    if type(a) == "number" and type(b) == "number" then
        return math.abs(a - b) < EPSILON
    end
    return a == b
end

local function summarizeIndicators(indicators, getter, defaultValue)
    if not indicators or #indicators == 0 then
        return defaultValue, true, 0
    end

    local firstValue = getter(indicators[1])
    for i = 2, #indicators do
        if not valuesEqual(firstValue, getter(indicators[i])) then
            return defaultValue, true, #indicators
        end
    end

    return firstValue, false, #indicators
end

local function summarizeColor(playerIndex)
    local indicators = getEditableUnlinkedIndicators(playerIndex)
    if #indicators == 0 then
        return { r = 0.35, g = 0.35, b = 0.35 }, true, 0
    end

    local color = indicators[1].indicatorColor or { r = 1, g = 1, b = 1 }
    for i = 2, #indicators do
        local other = indicators[i].indicatorColor or { r = 1, g = 1, b = 1 }
        if math.abs((color.r or 0) - (other.r or 0)) >= EPSILON or math.abs((color.g or 0) - (other.g or 0)) >= EPSILON or math.abs((color.b or 0) - (other.b or 0)) >= EPSILON then
            return { r = 0.35, g = 0.35, b = 0.35 }, true, #indicators
        end
    end

    return { r = color.r or 1, g = color.g or 1, b = color.b or 1 }, false, #indicators
end

local function getModeSummary(playerIndex)
    return summarizeIndicators(getEditableUnlinkedIndicators(playerIndex), getIndicatorMode, "horizontal")
end

local function getLayoutMode(playerIndex)
    local mode, mixed, count = getModeSummary(playerIndex)
    if count > 0 and not mixed and mode == "circular" then
        return "circular"
    end
    return "bar"
end

local function areAllUnlockedUnlinkedHidden(playerIndex)
    local indicators = getUnlockedUnlinkedIndicators(playerIndex)
    if #indicators == 0 then return false end
    for _, indicator in ipairs(indicators) do
        if not indicator.hidden then
            return false
        end
    end
    return true
end


local function getVisibleUnlinkedLockState(playerIndex)
    local indicators = getVisibleUnlinkedIndicators(playerIndex)
    if not indicators or #indicators == 0 then
        return false, false, 0
    end

    local first = indicators[1].locked == true
    for i = 2, #indicators do
        if (indicators[i].locked == true) ~= first then
            return false, true, #indicators
        end
    end

    return first, false, #indicators
end

local function areAllVisibleUnlinkedLocked(playerIndex)
    local indicators = getVisibleUnlinkedIndicators(playerIndex)
    if #indicators == 0 then return false end
    for _, indicator in ipairs(indicators) do
        if not indicator.locked then
            return false
        end
    end
    return true
end

local function updateIndicatorPosition(indicator)
    MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "position", { x = indicator:getX(), y = indicator:getY() })
end

local function updateIndicatorBarSize(indicator)
    MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "barSize", { width = indicator:getWidth(), height = indicator:getHeight() })
end

local function applyIndicatorMode(indicator, mode)
    if not indicator then return end

    if mode == "circular" then
        indicator:switchStyle(false)
        return
    end

    indicator:switchStyle(true)
    local wantVertical = mode == "vertical"
    if (indicator.isVertical == true) ~= wantVertical then
        local oldWidth = indicator:getWidth()
        local oldHeight = indicator:getHeight()
        indicator:setWidth(oldHeight)
        indicator:setHeight(oldWidth)
    end
    indicator.isVertical = wantVertical
    MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "isVertical", wantVertical)
    updateIndicatorBarSize(indicator)
end

local function getNextMode(playerIndex)
    local indicators = getEditableUnlinkedIndicators(playerIndex)
    if #indicators == 0 then
        return "circular"
    end

    local allCircular = true
    local allHorizontal = true
    for _, indicator in ipairs(indicators) do
        local mode = getIndicatorMode(indicator)
        if mode ~= "circular" then
            allCircular = false
        end
        if mode ~= "horizontal" then
            allHorizontal = false
        end
    end

    if allCircular then
        return "horizontal"
    end
    if allHorizontal then
        return "vertical"
    end
    return "circular"
end

local function applyToEditable(playerIndex, callback)
    for _, indicator in ipairs(getEditableUnlinkedIndicators(playerIndex)) do
        callback(indicator)
    end
    MS_MatrixManager.applyMatrixLayout(playerIndex)
end

local function applyBooleanToEditable(playerIndex, key, selected)
    applyToEditable(playerIndex, function(indicator)
        indicator[key] = selected
        if key == "useMonoIcon" and indicator.updateIconPath then
            indicator:updateIconPath()
        end
        MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), key, selected)
    end)
end

local function applyThresholdsToEditable(playerIndex, selected)
    for _, indicator in ipairs(getEditableThresholdIndicators(playerIndex)) do
        indicator.displayThresholds = selected
        MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "displayThresholds", selected)
    end
end

local function setUnlockedUnlinkedHidden(playerIndex, hidden)
    for _, indicator in ipairs(getUnlockedUnlinkedIndicators(playerIndex)) do
        indicator.hidden = hidden
        indicator:setVisible(not hidden)
        MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "hidden", hidden)
    end
    MS_MatrixManager.applyMatrixLayout(playerIndex)
end

local function setVisibleUnlinkedLocked(playerIndex, locked)
    for _, indicator in ipairs(getVisibleUnlinkedIndicators(playerIndex)) do
        indicator.locked = locked
        indicator.moveWithMouse = not locked
        MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "locked", locked)
    end
end

function MS_UnlinkedStylePanel.calculateRequiredSize(mode)
    local rowHeight = math.floor(FONT_HGT_SMALL * 1.5)
    local titleBarHeight = math.floor(FONT_HGT_SMALL * 1.2)
    local buttonHeight = math.floor(FONT_HGT_SMALL * 1.3)
    local padding = math.floor(FONT_HGT_SMALL * 0.2)
    local rowCount = mode == "circular" and 14 or 17
    local width = 420
    local height = titleBarHeight + padding + rowHeight * rowCount + padding * 6 + buttonHeight * 3
    return { width = width, height = height }
end

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

function MS_UnlinkedStylePanel:new(x, y, width, height, playerIndex)
    local mode = getLayoutMode(playerIndex)
    local screenHeight = getCore():getScreenHeight()
    local maxHeight = math.max(220, screenHeight - 40)
    local size = MS_UnlinkedStylePanel.calculateRequiredSize(mode)
    width = width or size.width
    height = math.min(height or size.height, maxHeight)

    local o = ISPanel:new(x, y, width, height)
    setmetatable(o, self)
    self.__index = self

    o.playerIndex = playerIndex
    o.layoutMode = mode
    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.5)
    o.buttonHeight = math.floor(FONT_HGT_SMALL * 1.2)
    o.contentButtonHeight = math.floor(FONT_HGT_SMALL * 1.2)
    o.buttonSize = math.floor(FONT_HGT_SMALL * 1.2)
    o.moving = false
    o.moveWithMouse = false
    o:noBackground(true)

    o.minusIcon = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Minus.png")
    o.plusIcon = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Plus.png")
    o.closeIcon = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Close.png")
    o.resetIcon = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Reset.png")

    o.circularIcon = MS_MatrixManager.getModeIconTexture("circular")
    o.horizontalBarIcon = MS_MatrixManager.getModeIconTexture("horizontal")
    o.verticalBarIcon = MS_MatrixManager.getModeIconTexture("vertical")

    o.titlebarbkg = NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/TitleBarBG.png")
    o.contentbkg = NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/ContentBackground.png")

    return o
end

function MS_UnlinkedStylePanel.openForPlayer(playerIndex, anchorX, anchorY)
    if MS_UnlinkedStylePanel.CurrentPanel then
        MS_UnlinkedStylePanel.CurrentPanel:close()
    end

    local layoutMode = getLayoutMode(playerIndex)
    local size = MS_UnlinkedStylePanel.calculateRequiredSize(layoutMode)
    local screenWidth = getCore():getScreenWidth()
    local screenHeight = getCore():getScreenHeight()
    local x = anchorX or math.floor((screenWidth - size.width) / 2)
    local y = anchorY or math.floor((screenHeight - math.min(size.height, screenHeight - 40)) / 2)

    local panel = MS_UnlinkedStylePanel:new(x, y, nil, nil, playerIndex)
    panel:initialise()
    panel:addToUIManager()
    panel:setVisible(true)
    panel:setX(math.max(10, math.min(screenWidth - panel:getWidth() - 10, panel:getX())))
    panel:setY(math.max(10, math.min(screenHeight - panel:getHeight() - 10, panel:getY())))

    MS_UnlinkedStylePanel.CurrentPanel = panel
    return panel
end

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

function MS_UnlinkedStylePanel:reopenPreservingPosition()
    local x = self:getX()
    local y = self:getY()
    self:close()
    return MS_UnlinkedStylePanel.openForPlayer(self.playerIndex, x, y)
end

function MS_UnlinkedStylePanel:createChildren()
    self:createTitleBar()
    self:createScrollView()
    self:createContent()
    self:createBottomButtons()
end

function MS_UnlinkedStylePanel:createTitleBar()
    local titleButtonSize = FONT_HGT_SMALL
    MS_PanelCollapseHelper.addTitleBarButton(self, titleButtonSize, "UnlinkedStylePanel")

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

    local closeSize = FONT_HGT_SMALL
    self.closeButton = MS_SquareButton:new(self.width - closeSize - self.padding, (self.titleBarHeight - FONT_HGT_SMALL) / 2, closeSize, self.closeIcon, self, MS_UnlinkedStylePanel.close)
    self.closeButton:initialise()
    self.closeButton:setActive(true)
    self.closeButton:setActiveColor(0.8, 0.2, 0.2)
    self:addChild(self.closeButton)

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

function MS_UnlinkedStylePanel:createScrollView()
    local contentY = self.titleBarHeight + self.padding
    local contentHeight = self.height - contentY - (self.buttonHeight * 2) - self.padding * 4
    self.scrollView = NIScrollView:new(0, contentY, self.width, contentHeight)
    self.scrollView:initialise()
    self.scrollView:setScrollDirection("vertical")
    self.scrollView:setScrollSensitivity(self.rowHeight)
    self:addChild(self.scrollView)
end

function MS_UnlinkedStylePanel:createContent()
    if self.layoutMode == "circular" then
        self:createCircularContent()
    else
        self:createBarContent()
    end
end


function MS_UnlinkedStylePanel:getOptionTooltip(optionKey)
    if not MS_IndicatorVisualConfig or not MS_IndicatorVisualConfig.getOptionTooltip then
        return nil
    end
    return MS_IndicatorVisualConfig.getOptionTooltip(nil, optionKey, true)
end

function MS_UnlinkedStylePanel:createLabel(text, y, tooltip)
    local label = ISLabel:new(self.padding, y + ((self.rowHeight - FONT_HGT_SMALL) / 2), 0, text, 1, 1, 1, 1, UIFont.Small, true)
    label:initialise()
    applyOptionTooltip(label, tooltip)
    self.scrollView:addScrollChild(label)
    return label
end

function MS_UnlinkedStylePanel:getModeIcon(mode)
    if mode == "horizontal" then return self.horizontalBarIcon end
    if mode == "vertical" then return self.verticalBarIcon end
    return self.circularIcon
end

function MS_UnlinkedStylePanel:createModeButton(x, y)
    self:createLabel(getText("IGUI_ModernStatus_Style"), y, self:getOptionTooltip("mode"))

    local button = MS_SquareButton:new(x, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, self.circularIcon, self, MS_UnlinkedStylePanel.onCycleMode)
    button:initialise()
    button:setIconSizeRatio(0.7)
    button.prerender = function(control)
        local mode, mixed, count = getModeSummary(self.playerIndex)
        local displayMode = mode or (self.layoutMode == "circular" and "circular" or "horizontal")
        control:setIcon(self:getModeIcon(displayMode))
        control:setActive(not mixed and count > 0 and displayMode ~= "circular")
        control.activeColor = mixed and { r = 0.45, g = 0.45, b = 0.45 } or { r = 0.95, g = 0.5, b = 0.1 }
    end
    self.scrollView:addScrollChild(button)
    return button
end

function MS_UnlinkedStylePanel:createMixedAwareTickBox(x, y, labelText, getSummary, onApply, optionKey)
    self:createLabel(labelText, y, self:getOptionTooltip(optionKey))
    local box = ISTickBox:new(x, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, self.buttonSize, "", self, function(_, _, selected)
        onApply(selected)
    end)
    box:initialise()
    box:addOption("")
    box._mixed = false
    box.prerender = function(control)
        control._mixed = false
        local value, mixed = getSummary()
        control._mixed = mixed
        control:setSelected(1, value == true and not mixed)
        if mixed then
            control:drawRect(0, 0, control.width, control.height, 0.28, 0.45, 0.45, 0.45)
        end
        ISTickBox.prerender(control)
    end
    applyOptionTooltip(box, self:getOptionTooltip(optionKey))
    self.scrollView:addScrollChild(box)
    return box
end

function MS_UnlinkedStylePanel:createStepperControl(x, y, labelText, getSummary, onApply, minValue, maxValue, step, isPercentage, precision, optionKey)
    local tooltip = self:getOptionTooltip(optionKey)
    self:createLabel(labelText, y, tooltip)

    local textDigits = math.max(4, string.len(tostring(math.floor(maxValue or 9999))))
    local textBoxWidth = getTextManager():MeasureStringX(UIFont.Small, string.rep("9", textDigits)) + self.padding * 6

    local function formatDisplayValue(rawValue)
        local displayValue = rawValue
        if isPercentage then
            displayValue = displayValue * 100
        end

        if precision and precision > 0 then
            return string.format("%." .. tostring(precision) .. "f", displayValue)
        end

        return tostring(math.floor(displayValue + 0.00001))
    end
    local value = getSummary()
    local currentValue = value
    local textBox = ISTextEntryBox:new(formatDisplayValue(currentValue), x + self.buttonSize + self.padding, y + ((self.rowHeight - self.buttonSize) / 2), textBoxWidth, self.buttonSize)
    textBox:initialise()
    textBox:instantiate()
    textBox:setOnlyNumbers(not (precision and precision > 0))
    textBox.borderColor = { r = 0.4, g = 0.4, b = 0.4, a = 0.0 }
    textBox.backgroundColor = { r = 0.1, g = 0.1, b = 0.1, a = 0.0 }

    textBox._userEdited = false
    textBox._suppressTextChange = false

    local function setDisplayText(value)
        textBox._suppressTextChange = true
        textBox:setText(tostring(value))
        textBox._suppressTextChange = false
    end

    local function updateDisplay(forceDefault)
        local newValue, mixed = getSummary()
        setDisplayText(formatDisplayValue(newValue))
        textBox._userEdited = false

        if forceDefault or mixed then
            textBox.backgroundColor = { r = 0.35, g = 0.35, b = 0.35, a = 0.22 }
        else
            textBox.backgroundColor = { r = 0.1, g = 0.1, b = 0.1, a = 0.0 }
        end
    end

    local function readValueFromControl()
        local text = textBox:getText()
        local raw = tonumber(text)
        if not raw then
            local fallback = getSummary()
            raw = isPercentage and (fallback * 100) or fallback
        end
        if isPercentage then
            raw = math.max(minValue * 100, math.min(maxValue * 100, raw))
            textBox:setText(formatDisplayValue(raw / 100))
            return raw / 100
        end
        raw = math.max(minValue, math.min(maxValue, raw))
        textBox:setText(formatDisplayValue(raw))
        return raw
    end

    textBox.onTextChange = function(control)
        if not control._suppressTextChange then
            control._userEdited = true
        end
    end
    textBox.onCommandEntered = function()
        onApply(readValueFromControl())
        updateDisplay(false)
    end
    textBox.onOtherKey = function(key)
        if key == 28 then
            onApply(readValueFromControl())
            updateDisplay(false)
        end
    end
    textBox.onMouseDownOutside = function(control)
        if control._userEdited then
            onApply(readValueFromControl())
        end
        updateDisplay(false)
    end
    textBox.prerender = function(control)
        ISTextEntryBox.prerender(control)
        if not control:isFocused() then
            updateDisplay(false)
        end
    end
    applyOptionTooltip(textBox, tooltip)
    self.scrollView:addScrollChild(textBox)

    local decBtn = MS_SquareButton:new(x, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, self.minusIcon, self, function()
        local value = readValueFromControl()
        onApply(math.max(minValue, value - step))
        updateDisplay(true)
    end)
    decBtn:initialise()
    applyOptionTooltip(decBtn, tooltip)
    self.scrollView:addScrollChild(decBtn)

    local incBtn = MS_SquareButton:new(x + self.buttonSize + self.padding + textBoxWidth + self.padding, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, self.plusIcon, self, function()
        local value = readValueFromControl()
        onApply(math.min(maxValue, value + step))
        updateDisplay(true)
    end)
    incBtn:initialise()
    applyOptionTooltip(incBtn, tooltip)
    self.scrollView:addScrollChild(incBtn)

    return { decBtn = decBtn, textBox = textBox, incBtn = incBtn }
end

function MS_UnlinkedStylePanel:createColorControl(x, y, resetOnly)
    self:createLabel(getText("IGUI_ModernStatus_Color"), y)
    if not resetOnly then
        local button = ISButton:new(x, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize * 2, self.buttonSize, "", self, MS_UnlinkedStylePanel.openColorPicker)
        button:initialise()
        button:instantiate()
        button.prerender = function(control)
            local color, mixed = summarizeColor(self.playerIndex)
            control.backgroundColor = { r = color.r, g = color.g, b = color.b, a = 1 }
            control.borderColor = mixed and { r = 0.8, g = 0.8, b = 0.8, a = 1 } or { r = 1, g = 1, b = 1, a = 1 }
        end
        self.scrollView:addScrollChild(button)
        self.colorPreview = button
    else
        self.colorPreview = nil
    end

    local resetX = resetOnly and x or (x + self.buttonSize * 2 + 10)
    self.resetColorBtn = MS_SquareButton:new(resetX, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, self.resetIcon, self, MS_UnlinkedStylePanel.onResetColor)
    self.resetColorBtn:initialise()
    self.scrollView:addScrollChild(self.resetColorBtn)
end

local function applyInvertDirectionToEditable(playerIndex, selected)
    for _, indicator in ipairs(getEditableInvertibleUnlinkedIndicators(playerIndex)) do
        indicator.invertDirection = selected == true
        MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "invertDirection", indicator.invertDirection)
    end
    MS_MatrixManager.applyMatrixLayout(playerIndex)
end

function MS_UnlinkedStylePanel:createBarContent()
    local y = self.padding
    local middleX = math.floor(self.width / 2)
    local controlX = middleX + self.padding

    self.styleButton = self:createModeButton(controlX, y)
    y = y + self.rowHeight

    self.xControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_X"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator:getX() end, 0) end,
        function(value)
            applyToEditable(self.playerIndex, function(indicator)
                indicator:setX(value)
                updateIndicatorPosition(indicator)
            end)
        end,
        0, getCore():getScreenWidth(), 1, false)
    y = y + self.rowHeight

    self.yControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Y"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator:getY() end, 0) end,
        function(value)
            applyToEditable(self.playerIndex, function(indicator)
                indicator:setY(value)
                updateIndicatorPosition(indicator)
            end)
        end,
        0, getCore():getScreenHeight(), 1, false)
    y = y + self.rowHeight

    self.widthControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Wide"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator:getWidth() end, 64) end,
        function(value)
            applyToEditable(self.playerIndex, function(indicator)
                indicator:setWidth(value)
                updateIndicatorBarSize(indicator)
            end)
        end,
        6, 500, 10, false)
    y = y + self.rowHeight

    self.heightControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Height"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator:getHeight() end, 16) end,
        function(value)
            applyToEditable(self.playerIndex, function(indicator)
                indicator:setHeight(value)
                updateIndicatorBarSize(indicator)
            end)
        end,
        6, 500, 2, false)
    y = y + self.rowHeight

    self:createColorControl(controlX, y, true)
    y = y + self.rowHeight

    self.opacityControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Opacity"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.opacity or 1.0 end, 1.0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.opacity = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "opacity", value) end) end,
        0.1, 1.0, 0.1, true)
    y = y + self.rowHeight

    self.useGradientBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_UseGradient"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.useGradient == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "useGradient", selected) end, "useGradient")
    y = y + self.rowHeight

    self.showIconBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_ShowIcon"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.showIcon == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "showIcon", selected) end, "showIcon")
    y = y + self.rowHeight

    self.iconPositionBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_IconPosition"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.iconPosition == "right" or indicator.iconPosition == "top" end, false) end,
        function(selected) applyToEditable(self.playerIndex, function(indicator) local pos = selected and ((indicator.isVertical == true) and "top" or "right") or ((indicator.isVertical == true) and "bottom" or "left"); indicator.iconPosition = pos; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "iconPosition", pos) end) end, "iconPosition")
    y = y + self.rowHeight

    self.useMonoIconBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_MonoIcon"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.useMonoIcon == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "useMonoIcon", selected) end, "useMonoIcon")
    y = y + self.rowHeight

    self.iconOpacityControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_IconOpacity"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.iconOpacity or 1.0 end, 1.0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.iconOpacity = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "iconOpacity", value) end) end,
        0.1, 1.0, 0.1, true)
    y = y + self.rowHeight

    self.textScaleControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_TextSize"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.textScale or 1.0 end, 1.0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.textScale = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "textScale", value) end) end,
        0.5, 2.0, 0.1, true)
    y = y + self.rowHeight

    self.updateIntervalControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_UpdateIntervalMs"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.updateIntervalMs or MS_IndicatorVisualConfig.getDefaultUpdateIntervalMs(indicator:getType()) end, 1000) end,
        function(value)
            applyToEditable(self.playerIndex, function(indicator) indicator.updateIntervalMs = math.floor(value); if indicator.invalidateRenderSnapshot then indicator:invalidateRenderSnapshot() end; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "updateIntervalMs", indicator.updateIntervalMs) end)
            if MS_PerformanceManager and MS_PerformanceManager.markCustomMode then
                MS_PerformanceManager.markCustomMode(self.playerIndex)
            end
        end,
        50, 10000, 50, false, 0, "updateIntervalMs")
    y = y + self.rowHeight

    self.showNameBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_ShowName"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.showName ~= false end, true) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "showName", selected) end, "showName")
    y = y + self.rowHeight

    self.alwaysShowValueBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_AlwaysShowValue"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.alwaysShowValue == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "alwaysShowValue", selected) end, "alwaysShowValue")
    y = y + self.rowHeight

    if #getEditableThresholdIndicators(self.playerIndex) > 0 then
        self.displayThresholdsBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_DisplayThresholds"),
            function() return summarizeIndicators(getEditableThresholdIndicators(self.playerIndex), function(indicator) return indicator.displayThresholds == true end, false) end,
            function(selected) applyThresholdsToEditable(self.playerIndex, selected) end, "displayThresholds")
        y = y + self.rowHeight
    end

    if #getEditableInvertibleUnlinkedIndicators(self.playerIndex) > 0 then
        self.invertDirectionBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_InvertDirection"),
            function() return summarizeIndicators(getEditableInvertibleUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.invertDirection == true end, false) end,
            function(selected) applyInvertDirectionToEditable(self.playerIndex, selected) end, "invertDirection")
        y = y + self.rowHeight
    end

    self.alarmEnabledBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_EnableAlarm"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.alarmEnabled ~= false end, true) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "alarmEnabled", selected) end, "alarmEnabled")
    y = y + self.rowHeight

    self.thresholdControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_AnimThreshold"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.animationThreshold or 20 end, 20) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.animationThreshold = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "animationThreshold", value) end) end,
        0, 100, 1, false, 1, "animationThreshold")
    y = y + self.rowHeight

    self.autoHideBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_AutoHide"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.autoHide == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "autoHide", selected) end, "autoHide")
    y = y + self.rowHeight

    self.autoHideThresholdControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_AutoHideThreshold"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.autoHideThreshold or 0 end, 0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.autoHideThreshold = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "autoHideThreshold", value) end) end,
        0, 100, 1, false, 1, "autoHideThreshold")
    y = y + self.rowHeight

    self.scrollView:setScrollHeight(y + self.padding)
end

function MS_UnlinkedStylePanel:createCircularContent()
    local y = self.padding
    local middleX = math.floor(self.width / 2)
    local controlX = middleX + self.padding

    self.styleButton = self:createModeButton(controlX, y)
    y = y + self.rowHeight

    self.xControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_X"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator:getX() end, 0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator:setX(value); updateIndicatorPosition(indicator) end) end,
        0, getCore():getScreenWidth(), 1, false)
    y = y + self.rowHeight

    self.yControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Y"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator:getY() end, 0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator:setY(value); updateIndicatorPosition(indicator) end) end,
        0, getCore():getScreenHeight(), 1, false)
    y = y + self.rowHeight

    self.sizeControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Size"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator:getWidth() end, 48) end,
        function(value)
            applyToEditable(self.playerIndex, function(indicator)
                indicator:setWidth(value)
                indicator:setHeight(value)
                MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "circularSize", value)
            end)
        end,
        1, 500, 4, false)
    y = y + self.rowHeight

    self:createColorControl(controlX, y, true)
    y = y + self.rowHeight

    self.opacityControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Opacity"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.opacity or 1.0 end, 1.0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.opacity = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "opacity", value) end) end,
        0.1, 1.0, 0.1, true)
    y = y + self.rowHeight

    self.useGradientBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_UseGradient"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.useGradient == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "useGradient", selected) end, "useGradient")
    y = y + self.rowHeight

    self.showIconBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_ShowIcon"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.showIcon == true end, true) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "showIcon", selected) end, "showIcon")
    y = y + self.rowHeight

    self.useMonoIconBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_MonoIcon"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.useMonoIcon == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "useMonoIcon", selected) end, "useMonoIcon")
    y = y + self.rowHeight

    self.iconOpacityControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_IconOpacity"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.iconOpacity or 1.0 end, 1.0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.iconOpacity = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "iconOpacity", value) end) end,
        0.1, 1.0, 0.1, true)
    y = y + self.rowHeight

    self.textScaleControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_TextSize"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.textScale or 1.0 end, 1.0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.textScale = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "textScale", value) end) end,
        0.5, 2.0, 0.1, true)
    y = y + self.rowHeight

    self.updateIntervalControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_UpdateIntervalMs"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.updateIntervalMs or MS_IndicatorVisualConfig.getDefaultUpdateIntervalMs(indicator:getType()) end, 1000) end,
        function(value)
            applyToEditable(self.playerIndex, function(indicator) indicator.updateIntervalMs = math.floor(value); if indicator.invalidateRenderSnapshot then indicator:invalidateRenderSnapshot() end; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "updateIntervalMs", indicator.updateIntervalMs) end)
            if MS_PerformanceManager and MS_PerformanceManager.markCustomMode then
                MS_PerformanceManager.markCustomMode(self.playerIndex)
            end
        end,
        50, 10000, 50, false, 0, "updateIntervalMs")
    y = y + self.rowHeight

    self.alwaysShowValueBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_AlwaysShowValue"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.alwaysShowValue == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "alwaysShowValue", selected) end, "alwaysShowValue")
    y = y + self.rowHeight

    if #getEditableThresholdIndicators(self.playerIndex) > 0 then
        self.displayThresholdsBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_DisplayThresholds"),
            function() return summarizeIndicators(getEditableThresholdIndicators(self.playerIndex), function(indicator) return indicator.displayThresholds == true end, false) end,
            function(selected) applyThresholdsToEditable(self.playerIndex, selected) end, "displayThresholds")
        y = y + self.rowHeight
    end

    if #getEditableInvertibleUnlinkedIndicators(self.playerIndex) > 0 then
        self.invertDirectionBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_InvertDirection"),
            function() return summarizeIndicators(getEditableInvertibleUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.invertDirection == true end, false) end,
            function(selected) applyInvertDirectionToEditable(self.playerIndex, selected) end, "invertDirection")
        y = y + self.rowHeight
    end

    self.alarmEnabledBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_EnableAlarm"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.alarmEnabled ~= false end, true) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "alarmEnabled", selected) end, "alarmEnabled")
    y = y + self.rowHeight

    self.thresholdControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_AnimThreshold"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.animationThreshold or 20 end, 20) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.animationThreshold = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "animationThreshold", value) end) end,
        0, 100, 1, false, 1, "animationThreshold")
    y = y + self.rowHeight

    self.autoHideBox = self:createMixedAwareTickBox(controlX, y, getText("IGUI_ModernStatus_AutoHide"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.autoHide == true end, false) end,
        function(selected) applyBooleanToEditable(self.playerIndex, "autoHide", selected) end, "autoHide")
    y = y + self.rowHeight

    self.autoHideThresholdControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_AutoHideThreshold"),
        function() return summarizeIndicators(getEditableUnlinkedIndicators(self.playerIndex), function(indicator) return indicator.autoHideThreshold or 0 end, 0) end,
        function(value) applyToEditable(self.playerIndex, function(indicator) indicator.autoHideThreshold = value; MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "autoHideThreshold", value) end) end,
        0, 100, 1, false, 1, "autoHideThreshold")
    y = y + self.rowHeight

    self.scrollView:setScrollHeight(y + self.padding)
end

function MS_UnlinkedStylePanel:createBottomButtons()
    local buttonWidth = math.floor((self:getWidth() - self.padding * 3) / 2)
    local fullWidth = self:getWidth() - self.padding * 2
    local bottomY = self:getHeight() - self.buttonHeight - self.padding
    local topY = bottomY - self.buttonHeight - self.padding

    self.addAllToMatrixButton = MS_LongButton:new(self.padding, topY, fullWidth, self.buttonHeight, getText("IGUI_ModernStatus_AddAllToMatrix"), self, MS_UnlinkedStylePanel.onAddAllToMatrixClick)
    self.addAllToMatrixButton:initialise()
    self.addAllToMatrixButton.prerender = function(button)
        button:setActive(#getUnlockedUnlinkedIndicators(self.playerIndex) == 0)
    end
    self:addChild(self.addAllToMatrixButton)

    self.hideButton = MS_LongButton:new(self.padding, bottomY, buttonWidth, self.buttonHeight, getText("IGUI_ModernStatus_Hide"), self, MS_UnlinkedStylePanel.onHideUnlockedClick)
    self.hideButton:initialise()
    self.hideButton.prerender = function(button)
        local allHidden = areAllUnlockedUnlinkedHidden(self.playerIndex)
        button:setTitle(allHidden and getText("IGUI_ModernStatus_Show") or getText("IGUI_ModernStatus_Hide"))
        button:setActive(allHidden)
    end
    self:addChild(self.hideButton)

    self.lockButton = MS_LongButton:new(self:getWidth() - buttonWidth - self.padding, bottomY, buttonWidth, self.buttonHeight, getText("IGUI_ModernStatus_Lock"), self, MS_UnlinkedStylePanel.onToggleLockClick)
    self.lockButton:initialise()
    self.lockButton.prerender = function(button)
        local allLocked, mixed, count = getVisibleUnlinkedLockState(self.playerIndex)
        if count == 0 then
            button:setTitle(getText("IGUI_ModernStatus_Lock"))
            button:setActive(false)
            button:setActiveColor(0.95, 0.5, 0.1)
            return
        end

        if allLocked then
            button:setTitle(getText("IGUI_ModernStatus_Unlock"))
            button:setActive(true)
            button:setActiveColor(0.95, 0.5, 0.1)
        elseif mixed then
            button:setTitle(getText("IGUI_ModernStatus_Lock"))
            button:setActive(true)
            button:setActiveColor(0.45, 0.45, 0.45)
        else
            button:setTitle(getText("IGUI_ModernStatus_Lock"))
            button:setActive(false)
            button:setActiveColor(0.95, 0.5, 0.1)
        end
    end
    self:addChild(self.lockButton)
end

function MS_UnlinkedStylePanel:openColorPicker()
    local pickerX = self.colorPreview:getAbsoluteX() + self.colorPreview:getWidth() + 5
    local pickerY = self.colorPreview:getAbsoluteY()
    local color = summarizeColor(self.playerIndex)
    local initialColor = ColorInfo.new(color.r, color.g, color.b, 1)
    local colorPicker = ISColorPickerHSB:new(pickerX, pickerY, initialColor)
    colorPicker:setPickedFunc(function(_, colorRGB)
        self:onColorSelected(colorRGB.r, colorRGB.g, colorRGB.b)
    end)
    colorPicker:initialise()
    colorPicker:addToUIManager()
    colorPicker:setVisible(true)
end

function MS_UnlinkedStylePanel:onColorSelected(r, g, b)
    applyToEditable(self.playerIndex, function(indicator)
        indicator.indicatorColor = { r = r, g = g, b = b }
        MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "color", { r = r, g = g, b = b })
    end)
end

function MS_UnlinkedStylePanel:onResetColor()
    applyToEditable(self.playerIndex, function(indicator)
        local defaultConfig = MSConfig.getDefaultIndicatorConfig(indicator:getType())
        local defaultColor = defaultConfig.color
        indicator.indicatorColor = { r = defaultColor.r, g = defaultColor.g, b = defaultColor.b }
        MSConfig.updateIndicatorConfig(indicator.playerIndex, indicator:getType(), "color", { r = defaultColor.r, g = defaultColor.g, b = defaultColor.b })
    end)
end

function MS_UnlinkedStylePanel:onCycleMode()
    local nextMode = getNextMode(self.playerIndex)
    applyToEditable(self.playerIndex, function(indicator)
        applyIndicatorMode(indicator, nextMode)
    end)
    local desiredLayout = getLayoutMode(self.playerIndex)
    if desiredLayout ~= self.layoutMode then
        self:reopenPreservingPosition()
    end
end

function MS_UnlinkedStylePanel:onAddAllToMatrixClick()
    MS_MatrixManager.addAllUnlinkedIndicators(self.playerIndex)
    self:reopenPreservingPosition()
end

function MS_UnlinkedStylePanel:onHideUnlockedClick()
    setUnlockedUnlinkedHidden(self.playerIndex, not areAllUnlockedUnlinkedHidden(self.playerIndex))
    self:reopenPreservingPosition()
end

function MS_UnlinkedStylePanel:onToggleLockClick()
    setVisibleUnlinkedLocked(self.playerIndex, not areAllVisibleUnlinkedLocked(self.playerIndex))
    self:reopenPreservingPosition()
end

function MS_UnlinkedStylePanel:onMouseWheel(del)
    if self.scrollView and self.scrollView:isVisible() then
        return self.scrollView:onMouseWheel(del)
    end
    return false
end

function MS_UnlinkedStylePanel: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_UnlinkedStylePanel:onMouseUp(x, y)
    if self.moving then
        self.moving = false
        self:setCapture(false)
    end
    return ISPanel.onMouseUp(self, x, y)
end

function MS_UnlinkedStylePanel: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_UnlinkedStylePanel: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_UnlinkedStylePanel: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_UnlinkedStylePanel: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_UnlinkedStylePanel
