local addonName, ns = ...

local L = ns.L or {}
local optionsDialog

local UIParent = UIParent
local GameTooltip = GameTooltip
local math = math
local pairs = pairs
local ipairs = ipairs
local type = type
local pcall = pcall
local format = string.format
local tinsert = table.insert

local DBKEY_MASTER = "showOrbsInHUDEditMode"

local editHosts = {}

local function db()
    return rOrbsDB
end

local function getMasterEnabled()
    local d = db()
    if not d then return true end
    if d[DBKEY_MASTER] == false then return false end
    return true
end

local function setMasterEnabled(v)
    local d = db()
    if not d then return end
    d[DBKEY_MASTER] = v and true or false
end

local function isBlizzardEditModeActive()
    local f = EditModeManagerFrame
    return f and f.IsEditModeActive and f:IsEditModeActive()
end

local function getMouseFocusCompat()
    if GetMouseFoci then
        local list = GetMouseFoci()
        if list and list[1] then return list[1] end
    end
    if GetMouseFocus then return GetMouseFocus() end
end

local function isMouseFocusOnHostEditSubtree(host)
    if not host then return false end
    local f = getMouseFocusCompat()
    local dlg = optionsDialog
    while f do
        if f == host then return true end
        if host.Selection and f == host.Selection then return true end
        if dlg and dlg:IsShown() and dlg.anchorRef == host then
            if f == dlg then return true end
            if f.isROrbsEditSettingsUIPart then
                local p = f
                while p do
                    if p == dlg then return true end
                    p = p:GetParent()
                end
            end
        end
        f = f:GetParent()
    end
    return false
end

local function markROrbsUi(f)
    if f then f.isROrbsEditSettingsUIPart = true end
end

local liveApplyTimer
local function requestLiveApplyFromDialog()
    local key = optionsDialog and optionsDialog._rOrbsEditKey
    if not key then return end
    if liveApplyTimer then liveApplyTimer:Cancel() end
    liveApplyTimer = C_Timer.NewTimer(0, function()
        liveApplyTimer = nil
        pcall(function()
            if ns.ApplyBlizzardEditModeLive then
                ns.ApplyBlizzardEditModeLive(key)
            end
        end)
    end)
end

local function defaultPosForUnitKey(unitKey)
    local def = rOrbs and rOrbs.defaults and rOrbs.defaults.profile
    local u = def and def.units and def.units[unitKey]
    if u and u.pos then
        return { a1 = u.pos.a1, a2 = u.pos.a2, af = u.pos.af, x = u.pos.x, y = u.pos.y }
    end
    return { a1 = "CENTER", a2 = "CENTER", af = "UIParent", x = 0, y = 0 }
end

local function getHostEditPoint(host)
    if not host or not host.GetPoint then return end
    local a1, rel, a2, x, y = host:GetPoint(1)
    if not a1 or not a2 or x == nil or y == nil then return end
    local relFrame = rel or UIParent
    local relName = (rel and rel.GetName and rel:GetName()) or "UIParent"
    return a1, relFrame, relName, a2, x, y
end

local function snapEditCoord(v, grid)
    grid = grid or 10
    return math.floor((v or 0) / grid + 0.5) * grid
end

local function snapEditHost(host)
    if not host or not (db() and db().snapToGrid) then return end
    local a1, relFrame, relName, a2, x, y = getHostEditPoint(host)
    if not a1 then return end
    local nx, ny = snapEditCoord(x), snapEditCoord(y)
    if nx ~= x or ny ~= y then
        host:ClearAllPoints()
        host:SetPoint(a1, relFrame, a2, nx, ny)
    end
end

local function syncTargetHealthFrames(a1, relFrame, a2, x, y)
    local tf = _G["rOrbsTargetHealthFrame"]
    local orb = _G["rOrbsTargetHealthOrb"]
    if tf then
        tf:ClearAllPoints()
        tf:SetPoint(a1, relFrame, a2, x, y)
        tf:Show()
    end
    if orb then
        orb:ClearAllPoints()
        orb:SetPoint(a1, relFrame, a2, x, y)
        orb:Show()
    end
end

local function applyTargetHealthLayoutFromCfg(cfgU)
    if not cfgU or not cfgU.pos then return end
    local pos = cfgU.pos
    local af = _G[pos.af] or UIParent
    syncTargetHealthFrames(pos.a1, af, pos.a2, pos.x, pos.y)
end

local function persistTargetHealthOrbEditPosition(host, a1, relName, a2, x, y)
    local d = db()
    if not d or not host then return end
    if not d.framePositions then d.framePositions = {} end
    local frameName = host.GetName and host:GetName()
    if frameName then
        d.framePositions[frameName] = {
            point = a1,
            relativeTo = relName,
            relativePoint = a2,
            x = x,
            y = y,
        }
    end
    local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units.targetHealth
    if cfgU and cfgU.pos then
        cfgU.pos.a1, cfgU.pos.a2, cfgU.pos.af = a1, a2, relName
        cfgU.pos.x, cfgU.pos.y = x, y
        d["pos_targetHealth_x"] = x
        d["pos_targetHealth_y"] = y
        d["pos_targetHealth_a1"] = a1
        d["pos_targetHealth_a2"] = a2
        d["pos_targetHealth_af"] = relName
    end
    syncTargetHealthFrames(a1, _G[relName] or UIParent, a2, x, y)
end

local function captureAndPersistTargetHealthOrb(host)
    if not host then return false end
    local a1, relFrame, relName, a2, x, y = getHostEditPoint(host)
    if not a1 then return false end
    persistTargetHealthOrbEditPosition(
        host, a1, relName, a2,
        math.floor(x + 0.5),
        math.floor(y + 0.5)
    )
    return true
end

local function finalizeTargetHealthEditMode(host)
    captureAndPersistTargetHealthOrb(host)
    local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units.targetHealth
    if cfgU then
        applyTargetHealthLayoutFromCfg(cfgU)
    end
    local tf = _G["rOrbsTargetHealthFrame"]
    local orb = _G["rOrbsTargetHealthOrb"]
    if orb then orb:SetAlpha(1) end
    if tf and tf._rOrbsDeferTargetAlphaResync then
        tf._rOrbsDeferTargetAlphaResync()
    elseif tf then
        tf._rOrbsSkipAlphaFadePoller = true
        if host then host._rOrbsSkipAlphaFadePoller = true end
        if tf._rOrbsResyncTargetAlpha then
            tf._rOrbsResyncTargetAlpha()
        else
            tf:SetAlpha((UnitExists("target") and 1) or 0)
        end
        C_Timer.After(0.6, function()
            if tf then tf._rOrbsSkipAlphaFadePoller = nil end
            if host then host._rOrbsSkipAlphaFadePoller = nil end
        end)
    elseif host then
        host._rOrbsSkipAlphaFadePoller = nil
    end
end

local function unitLabel(key)
    if key == "player" then return L["EDITMODE_UNIT_PLAYER"] or "Player" end
    if key == "playerHealthOrb" then return L["EDITMODE_UNIT_PLAYER_HEALTH_ORB"] or "Player health orb" end
    if key == "playerPowerOrb" then return L["EDITMODE_UNIT_PLAYER_POWER_ORB"] or "Player power orb" end
    if key == "target" then return L["EDITMODE_UNIT_TARGET"] or "Target portrait" end
    if key == "targetHealth" then return L["EDITMODE_UNIT_TARGET_HEALTH"] or "Target orb" end
    if key == "targetHealthOrb" then return L["EDITMODE_UNIT_TARGET_HEALTH_ORB"] or "Target health orb" end
    if key == "targettarget" then return L["EDITMODE_UNIT_TOT"] or "Target of target" end
    if key == "pet" then return L["EDITMODE_UNIT_PET"] or "Pet" end
    if key == "party" then return L["EDITMODE_UNIT_PARTY"] or "Party" end
    if key == "combo" then return L["EDITMODE_UNIT_COMBO"] or "Class power (combo bar)" end
    if key == "runeHoriz" then return L["EDITMODE_UNIT_RUNE_HORIZ"] or "Class power (horizontal)" end
    return tostring(key)
end

local function tooltipActiveHosts()
    local lines
    for _, key in ipairs({
        "playerHealthOrb", "playerPowerOrb", "targetHealthOrb",
        "target", "targettarget", "pet", "party", "combo", "runeHoriz",
    }) do
        local host = editHosts[key]
        if host and host._rOrbsEditEligible then
            local name = "- " .. unitLabel(key)
            if lines then lines = lines .. "\n" .. name else lines = name end
        end
    end
    return lines
end

-- ---------------------------------------------------------------------------
-- Selection overlay (nine-slice BLP, same assets as nCastbar)
-- ---------------------------------------------------------------------------
local TEX_EDITMODE_HIGHLIGHT = "Interface/AddOns/rOrbs/media/EditModeHighlighted"
local TEX_EDITMODE_SELECTED = "Interface/AddOns/rOrbs/media/EditModeSelected"

local EditModeSelectionMixin = {}

function EditModeSelectionMixin:OnDragStart()
    local p = self.parent
    if p and p.StartMoving then p:StartMoving() end
end

function EditModeSelectionMixin:OnDragStop()
    local p = self.parent
    if p and p.StopMovingOrSizing then p:StopMovingOrSizing() end
    if p then snapEditHost(p) end
    if p and p._rOrbsOnEditDragStop then p:_rOrbsOnEditDragStop() end
end

function EditModeSelectionMixin:ShowHighlighted()
    if not self.parent or not self.parent:IsShown() then return end
    self.isSelected = false
    if self.Background then
        self.Background:SetTexture(TEX_EDITMODE_HIGHLIGHT)
        self.Background:SetVertexColor(1, 1, 1, 1)
    end
    self:Show()
    if self.Label then self.Label:Hide() end
end

function EditModeSelectionMixin:ShowSelected()
    if not self.parent or not self.parent:IsShown() then return end
    self.isSelected = true
    if self.Background then
        self.Background:SetTexture(TEX_EDITMODE_SELECTED)
        self.Background:SetVertexColor(1, 1, 1, 1)
    end
    self:Show()
    GameTooltip:Hide()
    if self.Label then self.Label:Show() end
end

function EditModeSelectionMixin:OnShow()
    local offset = 8
    if self.Background then
        self.Background:SetPoint("TOPLEFT", self, "TOPLEFT", -offset, offset)
        self.Background:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", offset, -offset)
    end
    self:RegisterEvent("GLOBAL_MOUSE_DOWN")
end

function EditModeSelectionMixin:OnHide()
    self:UnregisterEvent("GLOBAL_MOUSE_DOWN")
end

function EditModeSelectionMixin:OnEvent(event, ...)
    if event ~= "GLOBAL_MOUSE_DOWN" then return end
    if not isBlizzardEditModeActive() or not getMasterEnabled() then return end
    if not self:IsShown() or not self.parent then return end
    local ch = _G.rOrbsEditModeCheckboxHost
    if ch and ch:IsShown() and ch:IsMouseOver() then
        return
    end
    local nc = _G.nCastbarEditModeCheckboxHost
    if nc and nc:IsShown() and nc:IsMouseOver() then
        return
    end
    local rc = _G.rCastbarEditModeCheckboxHost
    if rc and rc:IsShown() and rc:IsMouseOver() then
        return
    end
    local rt = _G.rThreatEditModeCheckboxHost
    if rt and rt:IsShown() and rt:IsMouseOver() then
        return
    end
    local om = _G.ObeliskMinimapEditModeCheckboxHost
    if om and om:IsShown() and om:IsMouseOver() then
        return
    end
    local nmm = _G.nMinimapEditModeCheckboxHost
    if nmm and nmm:IsShown() and nmm:IsMouseOver() then
        return
    end
    local gqt = _G.GQuesttrackerEditModeCheckboxHost
    if gqt and gqt:IsShown() and gqt:IsMouseOver() then
        return
    end
    local cs = _G.CombatStatusEditModeCheckboxHost
    if cs and cs:IsShown() and cs:IsMouseOver() then
        return
    end
    local nbh = _G.nBuffEditModeCheckboxHost
    if nbh and nbh:IsShown() and nbh:IsMouseOver() then
        return
    end
    if self.parent:IsFocused() then
        return
    end
    self:ShowHighlighted()
    if self.parent.ShowAnchorOptions then
        self.parent:ShowAnchorOptions(false)
    end
    if ns._rOrbsEditSelectedHost == self.parent then
        ns._rOrbsEditSelectedHost = nil
    end
end

function EditModeSelectionMixin:OnEnter()
    GameTooltip:SetOwner(self, "ANCHOR_CURSOR")
    if not self.isSelected then
        local hint = L["EDITMODE_CLICK_TO_EDIT"] or "Click to edit"
        GameTooltip:SetText(hint, 1, 1, 1, 1, true)
        if self.uiName then
            GameTooltip:AddLine(self.uiName, 1, 0.82, 0, true)
        end
    elseif self.uiName then
        GameTooltip:SetText(self.uiName, 1, 0.82, 0, 1, true)
    end
    GameTooltip:Show()
end

function EditModeSelectionMixin:OnLeave()
    GameTooltip:Hide()
end

function EditModeSelectionMixin:OnMouseDown(_, button)
    self:ShowSelected()
    if self.parent.ShowAnchorOptions then
        self.parent:ShowAnchorOptions(true)
    end
    if EditModeManagerFrame and EditModeManagerFrame.ClearSelectedSystem then
        EditModeManagerFrame:ClearSelectedSystem()
    end
    ns._rOrbsEditSelectedHost = self.parent
    if button == "RightButton" and self.parent.OnAnchorRightClick then
        self.parent:OnAnchorRightClick()
    end
end

local function createSelection(parent, uiName)
    local f = CreateFrame("Frame", nil, parent)
    f:Hide()
    f:SetAllPoints(true)
    f:SetFrameStrata(parent:GetFrameStrata())
    f:SetToplevel(true)
    f:SetFrameLevel(999)
    f:EnableMouse(true)
    f:EnableMouseMotion(true)
    f:RegisterForDrag("LeftButton")
    f:SetIgnoreParentAlpha(true)

    f.Background = f:CreateTexture(nil, "BACKGROUND")
    f.Background:SetTexture(TEX_EDITMODE_HIGHLIGHT)
    pcall(function()
        f.Background:SetTextureSliceMargins(16, 16, 16, 16)
        f.Background:SetTextureSliceMode(0)
    end)
    f.Background:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 0)
    f.Background:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 0, 0)
    f.Background:SetVertexColor(1, 1, 1, 1)

    f.Label = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightMedium")
    f.Label:SetText(uiName)
    f.Label:SetJustifyH("CENTER")
    f.Label:SetPoint("CENTER", f, "CENTER", 0, 0)
    f.Label:Hide()

    Mixin(f, EditModeSelectionMixin)
    f:SetScript("OnShow", f.OnShow)
    f:SetScript("OnHide", f.OnHide)
    f:SetScript("OnEnter", f.OnEnter)
    f:SetScript("OnLeave", f.OnLeave)
    f:SetScript("OnEvent", f.OnEvent)
    f:SetScript("OnMouseDown", f.OnMouseDown)
    f:SetScript("OnDragStart", f.OnDragStart)
    f:SetScript("OnDragStop", f.OnDragStop)

    f.parent = parent
    f.uiName = uiName
    f._rOrbsBlizzEditHit = true
    return f
end

-- ---------------------------------------------------------------------------
-- Options side panel
-- ---------------------------------------------------------------------------
local optionsWidgets = {}
local clearAllEditFocus

local function applyEditOptionsZOrder(dlg)
    if not dlg or not dlg:IsShown() or not dlg._roDialogBorder then return end
    local base = dlg:GetFrameLevel() or 0
    dlg._roDialogBorder:SetFrameLevel(math.max(0, base - 100))
    if dlg.scrollFrame then dlg.scrollFrame:SetFrameLevel(base + 20) end
    if dlg.CloseButton then dlg.CloseButton:SetFrameLevel(base + 40) end
end

local function snapEditSliderValue(v, minV, maxV, step)
    v = math.max(minV, math.min(maxV, v))
    if step and step >= 1 then
        v = math.floor(v + 0.5)
    elseif step and step < 1 then
        local m = math.floor(1 / step + 0.5)
        v = math.floor(v * m + 0.5) / m
    end
    return v
end

local function clearOptionsWidgets()
    for i = #optionsWidgets, 1, -1 do
        local w = optionsWidgets[i]
        if w then
            w:Hide()
            w:SetParent(nil)
        end
        optionsWidgets[i] = nil
    end
end

local function layoutOptionsContent()
    if not optionsDialog or not optionsDialog.content then return end
    local c = optionsDialog.content
    local y = -4
    local gap = 10
    for i = 1, #optionsWidgets do
        local w = optionsWidgets[i]
        if w then
            w:ClearAllPoints()
            w:SetPoint("TOPLEFT", c, "TOPLEFT", 8, y)
            local h = w.estimatedHeight or w:GetHeight() or 24
            y = y - h - gap
        end
    end
    local contentDepth = math.abs(y) + 44
    c:SetHeight(math.max(1, contentDepth))
    local wScroll = optionsDialog.scrollFrame and optionsDialog.scrollFrame:GetWidth() or 0
    if wScroll < 24 then
        wScroll = (optionsDialog:GetWidth() or 432) - 48
    end
    c:SetWidth(math.max(200, wScroll - 8))
    local screenH = (GetScreenHeight and GetScreenHeight()) or (UIParent and UIParent:GetHeight()) or 800
    local chromeScroll = 64
    local maxDialog = math.floor(screenH * 0.92)
    local maxViewport = math.max(160, maxDialog - chromeScroll)
    local viewport = math.min(math.max(1, contentDepth), maxViewport)
    optionsDialog:SetHeight(math.max(220, math.min(maxDialog, viewport + chromeScroll)))
    if optionsDialog.scrollFrame then
        optionsDialog.scrollFrame:SetVerticalScroll(0)
        optionsDialog.scrollFrame:UpdateScrollChildRect()
    end
    applyEditOptionsZOrder(optionsDialog)
end

local function addFontHeader(text)
    if not optionsDialog or not optionsDialog.content then return end
    local fs = optionsDialog.content:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    fs:SetWidth(math.max(200, (optionsDialog:GetWidth() or 420) - 40))
    fs:SetJustifyH("LEFT")
    fs:SetTextColor(1, 1, 1)
    fs:SetText(text)
    markROrbsUi(fs)
    fs.estimatedHeight = 22
    tinsert(optionsWidgets, fs)
end

local optionsSliderSeq = 0
local function addSlider(label, minV, maxV, step, current, onCommit, valueFmt)
    if not optionsDialog or not optionsDialog.content then return end
    optionsSliderSeq = optionsSliderSeq + 1
    local holder = CreateFrame("Frame", nil, optionsDialog.content)
    holder:SetSize(360, 48)
    local lbl = holder:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lbl:SetPoint("TOPLEFT", 0, -2)
    lbl:SetText(label)
    lbl:SetTextColor(1, 1, 1)
    markROrbsUi(lbl)
    local valStr = holder:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    valStr:SetTextColor(1, 0.82, 0)
    valStr:SetPoint("TOPRIGHT", holder, "TOPRIGHT", -6, -2)
    markROrbsUi(valStr)
    local sliderName = "rOrbsEditOptMS" .. optionsSliderSeq
    local s = CreateFrame("Slider", sliderName, holder, "MinimalSliderTemplate")
    s:SetPoint("TOPLEFT", holder, "TOPLEFT", 0, -24)
    s:SetPoint("TOPRIGHT", holder, "TOPRIGHT", -8, -24)
    s:SetHeight(20)
    s:SetMinMaxValues(minV, maxV)
    s:SetValueStep(step or 1)
    s:SetObeyStepOnDrag(true)
    local back = CreateFrame("Button", nil, holder)
    back:SetSize(11, 19)
    back:SetPoint("RIGHT", s, "LEFT", -4, 0)
    back:SetNormalAtlas("Minimal_SliderBar_Button_Left")
    back:SetHighlightAtlas("Minimal_SliderBar_Button_Left", "ADD")
    local fwd = CreateFrame("Button", nil, holder)
    fwd:SetSize(11, 19)
    fwd:SetPoint("LEFT", s, "RIGHT", 4, 0)
    fwd:SetNormalAtlas("Minimal_SliderBar_Button_Right")
    fwd:SetHighlightAtlas("Minimal_SliderBar_Button_Right", "ADD")
    local function syncValStr(v)
        if valueFmt then valStr:SetText(valueFmt(v)) else valStr:SetText(tostring(v)) end
    end
    local lock = false
    local function publishFromSlider(v)
        if lock then return end
        lock = true
        v = snapEditSliderValue(v, minV, maxV, step)
        s:SetValue(v)
        syncValStr(v)
        onCommit(v)
        requestLiveApplyFromDialog()
        lock = false
    end
    local function nudgeBy(delta)
        lock = true
        local v = snapEditSliderValue(s:GetValue() + delta, minV, maxV, step)
        s:SetValue(v)
        syncValStr(v)
        onCommit(v)
        requestLiveApplyFromDialog()
        lock = false
        if PlaySound then pcall(PlaySound, SOUNDKIT and SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON or 856) end
    end
    s:SetValue(snapEditSliderValue(current, minV, maxV, step))
    syncValStr(s:GetValue())
    s:SetScript("OnValueChanged", function(_, v)
        publishFromSlider(v)
    end)
    back:SetScript("OnClick", function()
        nudgeBy(-(s:GetValueStep() or 1))
    end)
    fwd:SetScript("OnClick", function()
        nudgeBy(s:GetValueStep() or 1)
    end)
    markROrbsUi(holder)
    markROrbsUi(s)
    markROrbsUi(back)
    markROrbsUi(fwd)
    holder.estimatedHeight = 52
    tinsert(optionsWidgets, holder)
end

local function addResetButton(editKey)
    if not optionsDialog or not optionsDialog.content then return end
    local b
    do
        local ok, btn = pcall(CreateFrame, "Button", nil, optionsDialog.content, "MagicButtonTemplate")
        if ok and btn then
            b = btn
        else
            b = CreateFrame("Button", nil, optionsDialog.content, "UIPanelButtonTemplate")
        end
    end
    b:SetSize(320, 26)
    b:SetText(L["EDITMODE_RESET_POS"] or RESET_TO_DEFAULT or "Reset position")
    b:SetScript("OnClick", function()
        local pos = defaultPosForUnitKey(editKey)
        local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units[editKey]
        if cfgU and cfgU.pos and pos then
            cfgU.pos.a1, cfgU.pos.a2, cfgU.pos.af = pos.a1, pos.a2, pos.af
            cfgU.pos.x, cfgU.pos.y = pos.x, pos.y
        end
        local d = db()
        if d and pos then
            d["pos_" .. editKey .. "_x"] = pos.x
            d["pos_" .. editKey .. "_y"] = pos.y
            d["pos_" .. editKey .. "_a1"] = pos.a1
            d["pos_" .. editKey .. "_a2"] = pos.a2
            d["pos_" .. editKey .. "_af"] = pos.af
        end
        if editKey == "player" and d and d.framePositions then
            d.framePositions["rOrbsHealthOrb"] = nil
            d.framePositions["rOrbsPowerOrb"] = nil
        end
        if editKey == "targetHealth" and d and d.framePositions then
            d.framePositions["rOrbsTargetHealthOrb"] = nil
        end
        local anchorHost = editHosts[editKey]
        if (not anchorHost or not anchorHost.GetPoint) and editKey == "player" then
            anchorHost = _G["rOrbsPlayerFrame"]
        end
        if (not anchorHost or not anchorHost.GetPoint) and editKey == "targetHealth" then
            anchorHost = _G["rOrbsTargetHealthFrame"]
        end
        if anchorHost and cfgU and pos then
            anchorHost:ClearAllPoints()
            local af = _G[pos.af] or UIParent
            anchorHost:SetPoint(pos.a1, af, pos.a2, pos.x, pos.y)
        end
        if editKey == "player" then
            local def = rOrbs and rOrbs.defaults and rOrbs.defaults.profile and rOrbs.defaults.profile.units
                and rOrbs.defaults.profile.units.player
            if cfgU and def then
                cfgU.size = def.size
                cfgU.scale = def.scale
                if d then
                    d.size_player = def.size
                    d.scale_player = def.scale
                end
            end
            local pf = _G["rOrbsPlayerFrame"]
            local hOrb, pOrb = _G["rOrbsHealthOrb"], _G["rOrbsPowerOrb"]
            if pf and cfgU then
                pf:SetSize(cfgU.size, cfgU.size)
                pf:SetScale(cfgU.scale or 1)
            end
            if hOrb and pf and cfgU then
                hOrb:ClearAllPoints()
                hOrb:SetPoint(cfgU.pos.a1, pf, cfgU.pos.a2, cfgU.pos.x, cfgU.pos.y)
                hOrb:SetSize(cfgU.size, cfgU.size)
            end
            if pOrb and pf and cfgU then
                pOrb:ClearAllPoints()
                pOrb:SetPoint(cfgU.pos.a1, pf, cfgU.pos.a2, (cfgU.pos.x or 0) * (-1), cfgU.pos.y)
                pOrb:SetSize(cfgU.size, cfgU.size)
            end
        end
        if editKey == "targetHealth" then
            local def = rOrbs and rOrbs.defaults and rOrbs.defaults.profile and rOrbs.defaults.profile.units
                and rOrbs.defaults.profile.units.targetHealth
            if cfgU and def then
                cfgU.size = def.size
                cfgU.scale = def.scale
                if d then
                    d["size_targetHealth"] = def.size
                    d["scale_targetHealth"] = def.scale
                end
            end
            local tf = _G["rOrbsTargetHealthFrame"]
            local orb = _G["rOrbsTargetHealthOrb"]
            if tf and cfgU then
                tf:SetSize(cfgU.size, cfgU.size)
                tf:SetScale(cfgU.scale or 1)
            end
            if orb and cfgU then
                orb:SetSize(cfgU.size, cfgU.size)
            end
            applyTargetHealthLayoutFromCfg(cfgU)
        end
        requestLiveApplyFromDialog()
    end)
    markROrbsUi(b)
    b.estimatedHeight = 34
    tinsert(optionsWidgets, b)
end

local function ensureOptionsDialog()
    if optionsDialog then return optionsDialog end
    local f = CreateFrame("Frame", "rOrbsEditModeOptions", UIParent)
    f:SetSize(432, 320)
    f:SetPoint("CENTER", UIParent, "CENTER", 260, 0)
    f:SetFrameStrata("DIALOG")
    f:SetFrameLevel(8000)
    f:Hide()
    f:EnableMouse(true)
    f:SetMovable(true)
    f:SetClampedToScreen(true)
    f:RegisterForDrag("LeftButton")
    f:SetDontSavePosition(true)
    markROrbsUi(f)

    local border = CreateFrame("Frame", nil, f, "DialogBorderTranslucentTemplate")
    border:SetAllPoints(true)
    markROrbsUi(border)
    border:SetFrameLevel(5)
    f._roDialogBorder = border

    f.Title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    f.Title:SetPoint("TOP", f, "TOP", 0, -20)
    markROrbsUi(f.Title)

    f.scrollFrame = CreateFrame("ScrollFrame", nil, f)
    f.scrollFrame:SetPoint("TOPLEFT", f, "TOPLEFT", 14, -48)
    f.scrollFrame:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -14, 14)
    f.scrollFrame:EnableMouseWheel(true)
    f.scrollFrame:SetFrameLevel(100)
    f.scrollFrame:SetScript("OnMouseWheel", function(self, delta)
        local step = 40
        local v = self:GetVerticalScroll() - (delta > 0 and step or -step)
        local max = math.max(0, self:GetVerticalScrollRange())
        if v < 0 then v = 0 elseif v > max then v = max end
        self:SetVerticalScroll(v)
    end)
    markROrbsUi(f.scrollFrame)

    f.content = CreateFrame("Frame", nil, f.scrollFrame)
    f.scrollFrame:SetScrollChild(f.content)
    f.content:SetPoint("TOPLEFT", f.scrollFrame, "TOPLEFT", 0, 0)
    markROrbsUi(f.content)

    f.CloseButton = CreateFrame("Button", nil, f, "UIPanelCloseButtonNoScripts")
    f.CloseButton:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, 0)
    markROrbsUi(f.CloseButton)
    f.CloseButton:SetFrameLevel(200)
    f.CloseButton:SetScript("OnClick", function()
        clearAllEditFocus()
    end)

    f:SetScript("OnDragStart", function(self) self:StartMoving() end)
    f:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        local left = self:GetLeft()
        local top = self:GetTop()
        self:ClearAllPoints()
        self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
    end)

    optionsDialog = f
    return f
end

local function buildOptionsForHost(host)
    ensureOptionsDialog()
    clearOptionsWidgets()
    if not host or not host._rOrbsEditKey then return end
    local key = host._rOrbsEditKey
    local kind = host._rOrbsEditKind or "orb"

    optionsDialog._rOrbsEditKey = key
    optionsDialog.Title:SetText(format(L["EDITMODE_PANEL_TITLE"] or "rOrbs: %s", unitLabel(key)))

    addFontHeader(L["EDITMODE_LAYOUT"] or "Layout")

    if kind == "party" then
        local portrait = (db() and db().partyPortraitSize) or (ns.cfg.units.party and ns.cfg.units.party.width) or 95
        local sc = (ns.cfg.units.party and ns.cfg.units.party.scale) or 1
        addSlider(L["EDITMODE_SIZE"] or "Size", 50, 160, 1, portrait, function(v)
            if db() then db().partyPortraitSize = v end
        end, function(v) return format("%d px", v) end)
        addSlider(L["EDITMODE_SCALE"] or "Scale", 0.5, 2.5, 0.01, sc, function(v)
            if ns.cfg.units.party then ns.cfg.units.party.scale = v end
            if db() then db().partyScale = v end
        end, function(v) return format("%.2f", v) end)
    elseif kind == "combo" then
        local slot = (db() and db().comboBarSlotScale) or 1
        local sc = (db() and db().comboBarScale) or 0.7
        addSlider(L["EDITMODE_SIZE"] or "Size", 0.5, 1.5, 0.01, slot, function(v)
            if db() then db().comboBarSlotScale = v end
        end, function(v) return format("%.2f", v) end)
        addSlider(L["EDITMODE_SCALE"] or "Scale", 0.3, 2.5, 0.01, sc, function(v)
            if db() then db().comboBarScale = v end
        end, function(v) return format("%.2f", v) end)
    elseif kind == "runeHoriz" then
        local sz = (db() and db().runeHorizSize) or 28
        local sc = (db() and db().runeHorizUIScale) or 1
        addSlider(L["EDITMODE_SIZE"] or "Size", 12, 64, 1, sz, function(v)
            if db() then db().runeHorizSize = v end
        end, function(v) return format("%d px", v) end)
        addSlider(L["EDITMODE_SCALE"] or "Scale", 0.5, 2.5, 0.01, sc, function(v)
            if db() then db().runeHorizUIScale = v end
        end, function(v) return format("%.2f", v) end)
    elseif kind == "playerSubOrb" or kind == "targetSubOrb" then
        local unitKey = (kind == "playerSubOrb") and "player" or "targetHealth"
        local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units[unitKey]
        if not cfgU then return end
        local sz = cfgU.size or 128
        local sc = cfgU.scale or 1
        addSlider(L["EDITMODE_SIZE"] or "Size", 48, 320, 1, sz, function(v)
            cfgU.size = v
            if db() then db()["size_" .. unitKey] = v end
        end, function(v) return format("%d px", v) end)
        addSlider(L["EDITMODE_SCALE"] or "Scale", 0.5, 2.5, 0.01, sc, function(v)
            cfgU.scale = v
            if db() then db()["scale_" .. unitKey] = v end
        end, function(v) return format("%.2f", v) end)
        addResetButton(unitKey)
    else
        local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units[key]
        if not cfgU then return end
        local sz = cfgU.size or 128
        local sc = cfgU.scale or 1
        addSlider(L["EDITMODE_SIZE"] or "Size", 48, 320, 1, sz, function(v)
            cfgU.size = v
            if db() then db()["size_" .. key] = v end
        end, function(v) return format("%d px", v) end)
        addSlider(L["EDITMODE_SCALE"] or "Scale", 0.5, 2.5, 0.01, sc, function(v)
            cfgU.scale = v
            if db() then db()["scale_" .. key] = v end
        end, function(v) return format("%.2f", v) end)
        addResetButton(key)
    end

    layoutOptionsContent()
end

local function showOptionsForHost(host, showState)
    local dlg = ensureOptionsDialog()
    if showState then
        buildOptionsForHost(host)
        dlg.anchorRef = host
        dlg:ClearAllPoints()
        local left = host:GetLeft() or 0
        local top = host:GetTop() or 0
        dlg:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left + 288, top + 64)
        dlg:Show()
        dlg:SetFrameLevel(8000)
        dlg:Raise()
        if dlg.CloseButton then
            dlg.CloseButton:SetFrameStrata("DIALOG")
        end
        applyEditOptionsZOrder(dlg)
        C_Timer.After(0, function()
            if dlg and dlg:IsShown() then applyEditOptionsZOrder(dlg) end
        end)
    else
        if dlg.anchorRef == host then
            dlg:Hide()
            dlg.anchorRef = nil
        end
    end
end

local function wireHost(host)
    if not host or host._rOrbsBlizzEditWired then return end
    host._rOrbsBlizzEditWired = true

    host.ShowAnchorOptions = function(self, state)
        showOptionsForHost(self, state)
    end

    function host:IsFocused()
        return isMouseFocusOnHostEditSubtree(self)
    end

    if not host.Selection then
        local title = format(L["EDITMODE_PANEL_TITLE"] or "rOrbs: %s", unitLabel(host._rOrbsEditKey))
        host.Selection = createSelection(host, title)
    end

    host._rOrbsOnEditDragStop = function(self)
        local a1, relFrame, relName, a2, x, y = getHostEditPoint(self)
        if not a1 then return end
        local key = self._rOrbsEditKey
        local kind = self._rOrbsEditKind or "orb"
        local d = db()
        if not d then return end
        if kind == "party" and d then
            d.partyPosX = math.floor((x or 0) + 0.5)
            d.partyPosY = math.floor((y or 0) + 0.5)
            if ns.cfg.units.party then
                ns.cfg.units.party.pos.x = d.partyPosX
                ns.cfg.units.party.pos.y = d.partyPosY
            end
        elseif kind == "combo" and d then
            local uiScale = UIParent:GetEffectiveScale()
            local sc = d.comboBarScale or 0.7
            if relFrame == UIParent and a2 == "CENTER" and a1 == "CENTER" then
                d.comboBarOffX = math.floor(((x or 0) * sc / uiScale) + 0.5)
                d.comboBarOffY = math.floor(((y or 0) * sc / uiScale) + 0.5)
            end
            if ns.ComboBar and ns.ComboBar.ApplyLayout then
                ns.ComboBar.ApplyLayout()
            end
        elseif kind == "runeHoriz" and d then
            d.runeHorizOffX = math.floor((x or 0) + 0.5)
            d.runeHorizOffY = math.floor((y or 0) + 0.5)
            if ns.RunePoints and ns.RunePoints.ApplyRuneHorizLayout then
                ns.RunePoints.ApplyRuneHorizLayout()
            end
        elseif kind == "targetSubOrb" then
            captureAndPersistTargetHealthOrb(self)
        elseif kind == "playerSubOrb" then
            if not d.framePositions then d.framePositions = {} end
            local frameName = self.GetName and self:GetName()
            if frameName then
                d.framePositions[frameName] = {
                    point = a1,
                    relativeTo = relName,
                    relativePoint = a2,
                    x = math.floor((x or 0) + 0.5),
                    y = math.floor((y or 0) + 0.5),
                }
            end
        elseif d and key then
            d["pos_" .. key .. "_x"] = math.floor((x or 0) + 0.5)
            d["pos_" .. key .. "_y"] = math.floor((y or 0) + 0.5)
            local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units[key]
            if cfgU and cfgU.pos then
                cfgU.pos.a1, cfgU.pos.a2, cfgU.pos.af = a1, a2, relName
                cfgU.pos.x, cfgU.pos.y = d["pos_" .. key .. "_x"], d["pos_" .. key .. "_y"]
            end
        end
    end
end

local function setHostBlizzardPresentation(host, active)
    if not host then return end
    if active then
        pcall(function()
            host:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", tile = false, insets = { left = 0, right = 0, top = 0, bottom = 0 } })
            host:SetBackdropColor(0, 0, 0, 0)
        end)
    else
        pcall(function()
            host:SetBackdrop(nil)
        end)
    end
end

local function showHostPresentation(host)
    wireHost(host)
    host._rOrbsEditPresentation = true
    local okM, mouseEn = pcall(function() return host:IsMouseEnabled() end)
    host._roEditSnapMouse = okM and mouseEn or false
    local okV, mov = pcall(function() return host:IsMovable() end)
    host._roEditSnapMovable = okV and mov or false
    if host._rOrbsEditKey == "pet" or host._rOrbsEditKey == "targettarget" then
        host._rOrbsSkipAlphaFadePoller = true
        host:SetAlpha(1)
    end
    if host._rOrbsEditKey == "targetHealthOrb" then
        if not host._rOrbsEditLayoutSynced then
            captureAndPersistTargetHealthOrb(host)
            host._rOrbsEditLayoutSynced = true
        end
        host._rOrbsSkipAlphaFadePoller = true
        host:SetAlpha(1)
        local tf = _G["rOrbsTargetHealthFrame"]
        if tf then
            tf._rOrbsSkipAlphaFadePoller = true
            tf:SetAlpha(1)
        end
    end
    setHostBlizzardPresentation(host, true)
    host:SetMovable(true)
    host:EnableMouse(true)
    host:Show()
    if host._rOrbsEditKey == "combo" and db() and db().comboPointStyle == "combobar" and ns.ComboBar and ns.ComboBar.ShowPreview then
        if not host._rOrbsComboEditPreview then
            host._rOrbsComboEditPreview = true
            ns.ComboBar.ShowPreview()
        end
    end
    if host._rOrbsEditKey == "runeHoriz" and ns.RunePoints and ns.RunePoints.ShowAll then
        if not host._rOrbsRuneEditPreview then
            host._rOrbsRuneEditPreview = true
            ns.RunePoints.ShowAll()
        end
    end
    if host.Selection then
        if ns._rOrbsEditSelectedHost == host then
            host.Selection:ShowSelected()
        else
            host.Selection:ShowHighlighted()
        end
    end
end

local function hideHostPresentation(host, opts)
    opts = opts or {}
    local forceHide = opts.forceHide
    if host.ShowAnchorOptions then host:ShowAnchorOptions(false) end
    host._rOrbsEditPresentation = nil
    host._rOrbsEditLayoutSynced = nil
    if host._rOrbsEditKey ~= "targetHealthOrb" then
        host._rOrbsSkipAlphaFadePoller = nil
    end
    host._rOrbsComboEditPreview = nil
    host._rOrbsRuneEditPreview = nil
    if host._rOrbsEditKey == "combo" and db() and db().comboPointStyle == "combobar" and ns.ComboBar and ns.ComboBar.HidePreview then
        ns.ComboBar.HidePreview()
    end
    if host._rOrbsEditKey == "runeHoriz" and ns.RunePoints and ns.RunePoints.StopPreview then
        ns.RunePoints.StopPreview()
    end
    if host.Selection then
        host.Selection:Hide()
    end
    setHostBlizzardPresentation(host, false)
    if host._roEditSnapMovable ~= nil then
        host:SetMovable(host._roEditSnapMovable)
        host._roEditSnapMovable = nil
    else
        host:SetMovable(false)
    end
    if host._roEditSnapMouse ~= nil then
        host:EnableMouse(host._roEditSnapMouse)
        host._roEditSnapMouse = nil
    else
        host:EnableMouse(false)
    end

    if host._rOrbsEditKey == "targetHealthOrb" then
        finalizeTargetHealthEditMode(host)
    elseif host._rOrbsEditKey == "pet" then
        host:SetAlpha((UnitExists("pet") and not UnitIsDead("pet") and 1) or 0)
    elseif host._rOrbsEditKey == "targettarget" then
        host:SetAlpha((UnitExists("targettarget") and 1) or 0)
    end

    if forceHide then
        host:Hide()
    end
end

local function hostEligible(key)
    if not ns.cfg or not ns.cfg.units then return false end
    if key == "playerHealthOrb" then
        return ns.cfg.units.player and ns.cfg.units.player.show and _G["rOrbsHealthOrb"] ~= nil
    end
    if key == "playerPowerOrb" then
        return ns.cfg.units.player and ns.cfg.units.player.show and _G["rOrbsPowerOrb"] ~= nil
    end
    if key == "targetHealthOrb" then
        if not (ns.cfg.units.targetHealth and ns.cfg.units.targetHealth.show) then return false end
        if db() and db().targetOrbEnabled == false then return false end
        return _G["rOrbsTargetHealthOrb"] ~= nil
    end
    if key == "target" then return ns.cfg.units.target and ns.cfg.units.target.show end
    if key == "targettarget" then
        if not (ns.cfg.units.targettarget and ns.cfg.units.targettarget.show) then return false end
        if db() and db().totOrbEnabled == false then return false end
        return true
    end
    if key == "pet" then
        if not (ns.cfg.units.pet and ns.cfg.units.pet.show) then return false end
        if db() and db().petOrbEnabled == false then return false end
        return true
    end
    if key == "party" then return ns.cfg.units.party and ns.cfg.units.party.show end
    if key == "combo" then return db() and db().comboPointStyle == "combobar" end
    if key == "runeHoriz" then return db() and (db().runePointsPosition == "horizontal") end
    return false
end

local function updateHostsVisibility()
    local any = false
    if isBlizzardEditModeActive() and getMasterEnabled() then
        for key, host in pairs(editHosts) do
            if hostEligible(key) then
                host._rOrbsEditEligible = true
                any = true
                showHostPresentation(host)
            else
                host._rOrbsEditEligible = false
                hideHostPresentation(host, {})
            end
        end
    else
        local forceHide = isBlizzardEditModeActive() and not getMasterEnabled()
        for key, host in pairs(editHosts) do
            hideHostPresentation(host, { forceHide = forceHide })
        end
    end
    return any
end

local arrowKeyBtn
local function clearArrowKeys()
    if arrowKeyBtn then
        ClearOverrideBindings(arrowKeyBtn)
    end
end

local function nudgeHost(dx, dy)
    local h = ns._rOrbsEditSelectedHost
    if not h or not h._rOrbsEditKey then return end
    local step = IsShiftKeyDown() and 5 or 1
    local a1, rel, a2, x, y = h:GetPoint(1)
    if not a1 or not rel or not a2 then return end
    h:ClearAllPoints()
    h:SetPoint(a1, rel, a2, (x or 0) + dx * step, (y or 0) + dy * step)
    snapEditHost(h)
    if h._rOrbsOnEditDragStop then h:_rOrbsOnEditDragStop() end
end

local function setupArrowKeys()
    if not isBlizzardEditModeActive() or not getMasterEnabled() then
        clearArrowKeys()
        return
    end
    if InCombatLockdown() then return end
    if not arrowKeyBtn then
        arrowKeyBtn = CreateFrame("Button", "rOrbsBlizzEditKeyHandler", UIParent)
        arrowKeyBtn:Hide()
        arrowKeyBtn:SetScript("OnClick", function(_, key)
            local stepx, stepy = 0, 0
            if key == "UP" then stepy = 1 elseif key == "DOWN" then stepy = -1
            elseif key == "LEFT" then stepx = -1 elseif key == "RIGHT" then stepx = 1
            else return end
            nudgeHost(stepx, stepy)
        end)
    end
    ClearOverrideBindings(arrowKeyBtn)
    SetOverrideBindingClick(arrowKeyBtn, false, "UP", "rOrbsBlizzEditKeyHandler", "UP")
    SetOverrideBindingClick(arrowKeyBtn, false, "DOWN", "rOrbsBlizzEditKeyHandler", "DOWN")
    SetOverrideBindingClick(arrowKeyBtn, false, "LEFT", "rOrbsBlizzEditKeyHandler", "LEFT")
    SetOverrideBindingClick(arrowKeyBtn, false, "RIGHT", "rOrbsBlizzEditKeyHandler", "RIGHT")
end

function clearAllEditFocus()
    ns._rOrbsEditSelectedHost = nil
    if optionsDialog then
        optionsDialog:Hide()
        optionsDialog.anchorRef = nil
    end
    for _, host in pairs(editHosts) do
        if host.Selection and host.Selection:IsShown() then
            host.Selection:ShowHighlighted()
        end
    end
end

local checkboxFrame

local function initCheckboxFrame()
    if checkboxFrame or not EditModeManagerFrame then return end
    local MainFrame = CreateFrame("Frame", "rOrbsEditModeCheckboxHost", UIParent)
    MainFrame:Hide()
    MainFrame.owner = EditModeManagerFrame
    MainFrame:SetFrameStrata("DIALOG")
    MainFrame:SetFrameLevel(EditModeManagerFrame:GetFrameLevel() + 2)

    MainFrame.Border = CreateFrame("Frame", nil, MainFrame, "DialogBorderTranslucentTemplate")
    MainFrame.Border:SetAllPoints(true)

    local cb = CreateFrame("CheckButton", nil, MainFrame, "UICheckButtonTemplate")
    MainFrame.Checkbox = cb
    cb.Text:SetText(L["EDITMODE_CHECKBOX_LABEL"] or "rOrbs")
    cb.Text:SetFontObject("GameFontHighlightMedium")
    cb:SetHitRectInsets(0, -120, 0, 0)
    cb:SetChecked(getMasterEnabled())
    cb:SetScript("OnClick", function(self)
        setMasterEnabled(self:GetChecked())
        updateHostsVisibility()
        setupArrowKeys()
    end)
    cb:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(L["EDITMODE_CHECKBOX_HEADER"] or "rOrbs", 1, 0.82, 0)
        local list = tooltipActiveHosts()
        local body = list and format(L["EDITMODE_CHECKBOX_DESC"] or "%s", list) or (L["EDITMODE_CHECKBOX_DESC_EMPTY"] or "")
        if body ~= "" then
            GameTooltip:AddLine(body, 1, 1, 1, true)
        end
        GameTooltip:Show()
    end)
    cb:SetScript("OnLeave", function() GameTooltip:Hide() end)

    local cbSnap = CreateFrame("CheckButton", nil, MainFrame, "UICheckButtonTemplate")
    MainFrame.SnapCheckbox = cbSnap
    cbSnap.Text:SetText(L["FRAME_POS_SNAP_GRID"] or "Snap to grid (10 px)")
    cbSnap.Text:SetFontObject("GameFontHighlightSmall")
    cbSnap:SetHitRectInsets(0, -200, 0, 0)
    cbSnap:SetChecked(db() and db().snapToGrid == true)
    cbSnap:SetScript("OnClick", function(self)
        local d = db()
        if d then d.snapToGrid = self:GetChecked() and true or false end
    end)
    cbSnap:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(L["FRAME_POS_SNAP_GRID"] or "Snap to grid (10 px)", 1, 0.82, 0)
        GameTooltip:AddLine(L["FRAME_POS_SNAP_GRID_TIP"] or "", 1, 1, 1, true)
        GameTooltip:Show()
    end)
    cbSnap:SetScript("OnLeave", function() GameTooltip:Hide() end)

    markROrbsUi(MainFrame)
    markROrbsUi(cb)
    markROrbsUi(cbSnap)

    local tw = math.max(cb.Text:GetStringWidth() or 70, cbSnap.Text:GetStringWidth() or 70, 48)
    local chk = 28
    local gap = 8
    local total = chk + gap + tw
    local padH = 18
    local rowH = 30
    MainFrame:SetSize(total + padH * 2, 46 + rowH)
    cb:ClearAllPoints()
    cb:SetPoint("TOPLEFT", MainFrame, "TOPLEFT", padH, -10)
    cbSnap:ClearAllPoints()
    cbSnap:SetPoint("TOPLEFT", MainFrame, "TOPLEFT", padH, -10 - rowH)

    local function checkboxHostOnUpdate(self, elapsed)
        self.t = (self.t or 0) + elapsed
        if self.t < 0.25 then return end
        self.t = 0
        local owner = self.owner
        if not owner then return end
        if owner:IsShown() then
            self:ClearAllPoints()
            local anchorRef = (self.secondaryOwner and self.secondaryOwner:IsShown()) and self.secondaryOwner or owner
            local wf = EditModeExpandedWarningFrame
            local gapY = 6
            local nc = _G["nCastbarEditModeCheckboxHost"]
            local rc = _G["rCastbarEditModeCheckboxHost"]
            if rc and rc:IsShown() then
                self:SetPoint("TOPRIGHT", rc, "BOTTOMRIGHT", 0, -gapY)
            elseif nc and nc:IsShown() then
                self:SetPoint("TOPRIGHT", nc, "BOTTOMRIGHT", 0, -gapY)
            elseif wf and wf:IsShown() then
                self:SetPoint("TOPRIGHT", wf, "BOTTOMLEFT", -4, -4)
            else
                self:SetPoint("TOPRIGHT", anchorRef, "TOPLEFT", -8, -4)
            end
        else
            self:ClearAllPoints()
            self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", 0, -8)
        end
        if owner:IsShown() then
            updateHostsVisibility()
            if isBlizzardEditModeActive() then
                self:Show()
            else
                self:Hide()
            end
        else
            self:Hide()
        end
    end

    MainFrame.t = 0
    MainFrame:SetScript("OnUpdate", checkboxHostOnUpdate)

    function MainFrame:EnterHost()
        if EditModeManagerExpandedFrame then
            self.secondaryOwner = EditModeManagerExpandedFrame
        end
        self.Checkbox:SetChecked(getMasterEnabled())
        if self.SnapCheckbox then
            self.SnapCheckbox:SetChecked(db() and db().snapToGrid == true)
        end
        self.t = 1
        self:SetScript("OnUpdate", checkboxHostOnUpdate)
        updateHostsVisibility()
        setupArrowKeys()
        self:Show()
    end

    function MainFrame:ExitHost()
        self:Hide()
        self:SetScript("OnUpdate", nil)
        for _, host in pairs(editHosts) do
            hideHostPresentation(host)
        end
        clearArrowKeys()
        if optionsDialog then
            optionsDialog:Hide()
            optionsDialog.anchorRef = nil
        end
        clearAllEditFocus()
    end

    checkboxFrame = MainFrame
end

local function onEditModeEnter()
    if not checkboxFrame then initCheckboxFrame() end
    if checkboxFrame then checkboxFrame:EnterHost() end
end

local function onEditModeExit()
    if ns.ComboBar and ns.ComboBar.HidePreview and db() and db().comboPointStyle == "combobar" then
        pcall(ns.ComboBar.HidePreview)
    end
    if checkboxFrame then checkboxFrame:ExitHost() end
end

function ns.OpenBlizzardHudEditModeForOrbs()
    if InCombatLockdown() then
        return false
    end
    local em = EditModeManagerFrame
    if not em then return false end
    setMasterEnabled(true)
    if em.IsEditModeActive and em:IsEditModeActive() then
        onEditModeEnter()
        return true
    end
    if ShowUIPanel then
        pcall(ShowUIPanel, em)
    end
    if em.IsShown and not em:IsShown() and em.Show then
        pcall(em.Show, em)
    end
    return true
end

function ns.RegisterBlizzardEditModeHost(host, key, meta)
    if not host or not key then return end
    meta = meta or {}
    host._rOrbsEditKey = key
    host._rOrbsEditKind = meta.kind or "orb"
    editHosts[key] = host
end

function ns.ApplyBlizzardEditModeLive(editKey)
    if editKey == "playerHealthOrb" or editKey == "playerPowerOrb" then
        local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units.player
        if not cfgU then return end
        local pf = _G["rOrbsPlayerFrame"]
        local hOrb, pOrb = _G["rOrbsHealthOrb"], _G["rOrbsPowerOrb"]
        if pf then
            pf:SetSize(cfgU.size, cfgU.size)
            pf:SetScale(cfgU.scale or 1)
        end
        if hOrb then hOrb:SetSize(cfgU.size, cfgU.size) end
        if pOrb then pOrb:SetSize(cfgU.size, cfgU.size) end
        if pf and pf.UpdateAllElements then pcall(pf.UpdateAllElements, pf, "ForceUpdate") end
        return
    end
    if editKey == "targetHealthOrb" then
        local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units.targetHealth
        if not cfgU then return end
        local tf = _G["rOrbsTargetHealthFrame"]
        local orb = _G["rOrbsTargetHealthOrb"]
        if tf then
            tf:SetSize(cfgU.size, cfgU.size)
            tf:SetScale(cfgU.scale or 1)
        end
        if orb then orb:SetSize(cfgU.size, cfgU.size) end
        if tf and tf.UpdateAllElements then pcall(tf.UpdateAllElements, tf, "ForceUpdate") end
        return
    end
    local host = editHosts[editKey]
    if not host then return end
    local kind = host._rOrbsEditKind or "orb"
    local d = db()
    if kind == "party" then
        if ns.RefreshPartyFramesFromDB then ns.RefreshPartyFramesFromDB() end
        return
    end
    if kind == "combo" then
        if ns.ComboBar and ns.ComboBar.ApplyLayout then ns.ComboBar.ApplyLayout() end
        if ns.ComboBar and ns.ComboBar.ForceRefresh then ns.ComboBar.ForceRefresh() end
        return
    end
    if kind == "runeHoriz" then
        if ns.RunePoints and ns.RunePoints.ApplyRuneHorizLayout then
            ns.RunePoints.ApplyRuneHorizLayout()
        end
        if ns.RunePoints and ns.RunePoints.ForceRefresh then
            ns.RunePoints.ForceRefresh()
        end
        return
    end
    local cfgU = ns.cfg and ns.cfg.units and ns.cfg.units[editKey]
    if not cfgU or not host then return end
    host:SetSize(cfgU.size, cfgU.size)
    host:SetScale(cfgU.scale or 1)
    if host.UpdateAllElements then
        pcall(host.UpdateAllElements, host, "ForceUpdate")
    end
end

if EventRegistry and EventRegistry.RegisterCallback then
    EventRegistry:RegisterCallback("EditMode.Enter", onEditModeEnter, ns)
    EventRegistry:RegisterCallback("EditMode.Exit", onEditModeExit, ns)
end
