local _, ns = ...
local SlickUI = ns.SlickUI
local Theme = SlickUI.Theme

local ActionBars = {
	Holders = {},
	PendingApply = false,
	SuppressedMainBarFrames = {},
	SuppressedBarContainers = {},
	ButtonStates = setmetatable({}, { __mode = "k" }),
	BarFrameStates = setmetatable({}, { __mode = "k" }),
	BlizzardEditModeActive = false,
	SpecialActionMode = false,
}
SlickUI.ActionBars = ActionBars

StaticPopupDialogs.SLICKUI_ACTIONBAR_REPAIR_RELOAD = {
	text = "Action Bar Repair Complete\n\nThe repaired Blizzard layout has been saved. Reload the interface now to finish applying it?",
	button1 = RELOADUI,
	button2 = CANCEL,
	OnAccept = function()
		if InCombatLockdown() then
			SlickUI:Print("Cannot reload during combat. Type /reload after combat ends.")
			return
		end
		ReloadUI()
	end,
	timeout = 0,
	whileDead = true,
	hideOnEscape = true,
	preferredIndex = 3,
}

-- Let WoW's secure state system control visibility while protected actions are
-- active. This keeps transitions combat-safe and ensures the normal SlickUI
-- bars do not cover the pet-battle or vehicle interface.
local ACTIVE_VISIBILITY_DRIVER = "[petbattle][vehicleui][overridebar][possessbar] hide; show"
-- The main ActionButtons are also Blizzard's vehicle/override buttons. Keep
-- their SlickUI holder visible so mounts and Delve vehicles retain controls;
-- secondary and pet bars still yield to the vehicle interface.
local MAIN_VISIBILITY_DRIVER = "[petbattle][vehicleui][overridebar][possessbar] hide; show"
local PET_VISIBILITY_DRIVER = "[petbattle][vehicleui][overridebar][possessbar] hide; [pet] show; hide"
local HIDDEN_VISIBILITY_DRIVER = "hide"

ActionBars.Definitions = {
	main = { label = "Main Bar", frame = "MainMenuBar", prefix = "ActionButton", position = "actionBarMain" },
	bottomLeft = { label = "Bottom Left", frame = "MultiBarBottomLeft", prefix = "MultiBarBottomLeftButton", position = "actionBarBottomLeft" },
	bottomRight = { label = "Bottom Right", frame = "MultiBarBottomRight", prefix = "MultiBarBottomRightButton", position = "actionBarBottomRight" },
	right = { label = "Right Bar", frame = "MultiBarRight", prefix = "MultiBarRightButton", position = "actionBarRight" },
	left = { label = "Right Bar 2", frame = "MultiBarLeft", prefix = "MultiBarLeftButton", position = "actionBarLeft" },
	bar5 = { label = "Bar 5", frame = "MultiBar5", prefix = "MultiBar5Button", position = "actionBar5" },
	bar6 = { label = "Bar 6", frame = "MultiBar6", prefix = "MultiBar6Button", position = "actionBar6" },
	bar7 = { label = "Bar 7", frame = "MultiBar7", prefix = "MultiBar7Button", position = "actionBar7" },
	pet = { label = "Pet Bar", frame = "PetActionBar", prefix = "PetActionButton", count = 10, position = "actionBarPet" },
}

ActionBars.Order = { "main", "bottomLeft", "bottomRight", "right", "left", "bar5", "bar6", "bar7", "pet" }

local function SetRegionVisible(region, visible)
	if region and region.SetAlpha then
		region:SetAlpha(visible and 1 or 0)
	end
end

local function AddUniqueFrame(results, seen, frame)
	if frame and not seen[frame] and frame.GetObjectType then
		seen[frame] = true
		results[#results + 1] = frame
	end
end

local function IsPageControlName(name)
	if type(name) ~= "string" then return false end
	local lower = string.lower(name)
	if lower == "actionbarupbutton" or lower == "actionbardownbutton" then return true end
	return string.find(lower, "page", 1, true) ~= nil
		and (string.find(lower, "actionbar", 1, true) ~= nil or string.find(lower, "mainmenubar", 1, true) ~= nil or string.find(lower, "mainactionbar", 1, true) ~= nil)
end

function ActionBars:GetMainBarPageControls()
	local results, seen = {}, {}
	local function Add(frame) AddUniqueFrame(results, seen, frame) end
	local function AddNamedChildren(parent, depth)
		if not parent or depth > 3 or not parent.GetChildren then return end
		for _, child in ipairs({ parent:GetChildren() }) do
			local name = child.GetName and child:GetName()
			if IsPageControlName(name) then Add(child) end
			AddNamedChildren(child, depth + 1)
		end
	end

	local explicit = {}
	local function AddExplicit(frame)
		if frame then explicit[#explicit + 1] = frame end
	end
	AddExplicit(_G.ActionBarUpButton)
	AddExplicit(_G.ActionBarDownButton)
	AddExplicit(_G.MainMenuBarPageNumber)
	AddExplicit(_G.MainActionBarPageNumber)
	AddExplicit(MainMenuBar and MainMenuBar.ActionBarPageNumber)
	AddExplicit(MainActionBar and MainActionBar.ActionBarPageNumber)
	for _, frame in ipairs(explicit) do
		Add(frame)
		if frame then
			Add(frame.UpButton); Add(frame.DownButton)
			Add(frame.PageUpButton); Add(frame.PageDownButton)
		end
	end
	AddNamedChildren(MainMenuBar, 1)
	AddNamedChildren(MainActionBar, 1)
	return results
end

function ActionBars:SetMainBarShellSuppressed(suppressed)
	local function Remember(frame, hideVisuals)
		if not frame then return end
		local saved = self.SuppressedMainBarFrames[frame]
		if suppressed then
			if not saved then
				saved = {
					alpha = frame.GetAlpha and frame:GetAlpha() or 1,
					mouse = frame.IsMouseEnabled and frame:IsMouseEnabled() or false,
					hideVisuals = hideVisuals,
				}
				self.SuppressedMainBarFrames[frame] = saved
			end
			if hideVisuals and frame.SetAlpha then frame:SetAlpha(0) end
			if frame.EnableMouse then frame:EnableMouse(false) end
		elseif saved then
			if saved.hideVisuals and frame.SetAlpha then frame:SetAlpha(saved.alpha or 1) end
			if frame.EnableMouse then frame:EnableMouse(saved.mouse == true) end
			self.SuppressedMainBarFrames[frame] = nil
		end
	end

	-- The secure ActionButtons have already been moved to the SlickUI holder, so
	-- the empty Blizzard shell must not retain an invisible mouse region.
	Remember(MainMenuBar, false)
	if MainActionBar and MainActionBar ~= MainMenuBar then Remember(MainActionBar, false) end
	for _, frame in ipairs(self:GetMainBarPageControls()) do Remember(frame, true) end

	if not suppressed then
		for frame, saved in pairs(self.SuppressedMainBarFrames) do
			if saved.hideVisuals and frame.SetAlpha then frame:SetAlpha(saved.alpha or 1) end
			if frame.EnableMouse then frame:EnableMouse(saved.mouse == true) end
			self.SuppressedMainBarFrames[frame] = nil
		end
	end
end

function ActionBars:SetBlizzardBarContainersSuppressed(suppressed)
	if InCombatLockdown() then
		self.PendingApply = true
		return false
	end
	for _, barKey in ipairs(self.Order) do
		local definition = self.Definitions[barKey]
		local frame = definition and _G[definition.frame]
		if frame then
			local saved = self.SuppressedBarContainers[frame]
			if suppressed then
				if not saved then
					saved = {
						alpha = frame.GetAlpha and frame:GetAlpha() or 1,
						mouse = frame.IsMouseEnabled and frame:IsMouseEnabled() or false,
					}
					self.SuppressedBarContainers[frame] = saved
				end
				-- The secure buttons have independent SlickUI holders. Suppress only
				-- their now-empty Blizzard containers, not the action buttons.
				if frame.SetAlpha then frame:SetAlpha(0) end
				if frame.EnableMouse then frame:EnableMouse(false) end
			elseif saved then
				if frame.SetAlpha then frame:SetAlpha(saved.alpha or 1) end
				if frame.EnableMouse then frame:EnableMouse(saved.mouse == true) end
				self.SuppressedBarContainers[frame] = nil
			end
		end
	end
	return true
end

function ActionBars:GetButtons(barKey)
	local definition = self.Definitions[barKey]
	local buttons = {}
	if not definition then return buttons end

	for index = 1, definition.count or 12 do
		local button = _G[definition.prefix .. index]
		if button then buttons[#buttons + 1] = button end
	end
	return buttons
end

function ActionBars:CreateHolder(barKey)
	local definition = self.Definitions[barKey]
	if not definition then return nil end

	local holder = CreateFrame("Frame", "SlickUI_ActionBarHolder_" .. barKey, UIParent, "BackdropTemplate")
	holder:SetSize(40, 40)
	holder:SetFrameStrata("LOW")
	holder.SlickMoverOnly = true
	Theme:CreatePanel(holder)
	for _, edge in ipairs(holder.SlickEdges or {}) do edge:SetAlpha(0) end
	if holder.SlickBackground then holder.SlickBackground:SetAlpha(0) end

	SlickUI.UnitFrames[definition.position] = holder
	SlickUI.Movers:Register(holder, definition.position, definition.label)
	self.Holders[barKey] = holder
	return holder
end

function ActionBars:ApplyVisibilityDriver(holder, shown, barKey)
	if not holder then return end
	local driver
	if not shown then
		driver = HIDDEN_VISIBILITY_DRIVER
	elseif barKey == "main" then
		driver = MAIN_VISIBILITY_DRIVER
	elseif barKey == "pet" then
		driver = PET_VISIBILITY_DRIVER
	else
		driver = ACTIVE_VISIBILITY_DRIVER
	end
	if holder.SlickUIVisibilityDriver == driver then return end
	UnregisterStateDriver(holder, "visibility")
	RegisterStateDriver(holder, "visibility", driver)
	holder.SlickUIVisibilityDriver = driver
end

function ActionBars:SetSlickEditMode(active)
	local holder = self.Holders.pet
	if not holder or InCombatLockdown() then return end
	if active then
		UnregisterStateDriver(holder, "visibility")
		holder.SlickUIVisibilityDriver = nil
		holder:Show()
	else
		local settings = SlickUI.DB.actionBars.bars.pet
		self:ApplyVisibilityDriver(holder, SlickUI.DB.actionBars.enabled and settings.shown ~= false, "pet")
	end
end

function ActionBars:IsSpecialActionModeActive()
	local function Active(check, ...)
		if type(check) ~= "function" then return false end
		local ok, active = pcall(check, ...)
		return ok and active == true
	end
	if Active(UnitHasVehicleUI, "player") then return true end
	if Active(HasVehicleActionBar) then return true end
	if Active(HasOverrideActionBar) then return true end
	if Active(HasTempShapeshiftActionBar) then return true end
	if Active(HasBonusActionBar) then return true end
	return false
end

function ActionBars:ApplySpecialActionMode()
	if InCombatLockdown() then
		self.PendingApply = true
		return false
	end
	self.SpecialActionMode = self:IsSpecialActionModeActive()
	if not self.SpecialActionMode then return false end
	for _, holder in pairs(self.Holders) do self:ApplyVisibilityDriver(holder, false) end
	self:SetMainBarShellSuppressed(false)
	self:SetBlizzardBarContainersSuppressed(false)
	self:RestoreBlizzardLayout()
	return true
end

function ActionBars:StyleButton(button)
	if self.ButtonStates[button] then return end
	local hitLeft, hitRight, hitTop, hitBottom = button:GetHitRectInsets()
	local state = {
		parent = button:GetParent(),
		frameLevel = button:GetFrameLevel(),
		hitRect = { hitLeft, hitRight, hitTop, hitBottom },
		width = button:GetWidth(),
		height = button:GetHeight(),
		scale = button:GetScale(),
		alpha = button:GetAlpha(),
		shown = button:IsShown(),
		hotkeyAlpha = button.HotKey and button.HotKey:GetAlpha() or 1,
		nameAlpha = button.Name and button.Name:GetAlpha() or 1,
		countAlpha = button.Count and button.Count:GetAlpha() or 1,
		points = {},
	}
	self.ButtonStates[button] = state
	for pointIndex = 1, button:GetNumPoints() do
		local point, relativeTo, relativePoint, x, y = button:GetPoint(pointIndex)
		state.points[#state.points + 1] = {
			point = point, relativeTo = relativeTo, relativePoint = relativePoint, x = x, y = y,
		}
	end

	if button.Icon then
		button.Icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
	end
	if button.HotKey then Theme:SetFont(button.HotKey, 9) end
	if button.Name then Theme:SetFont(button.Name, 8) end
	if button.Count then Theme:SetFont(button.Count, 10) end
	if button.NormalTexture and button.NormalTexture.SetAlpha then
		button.NormalTexture:SetAlpha(0.32)
	end

	local border = button:CreateTexture(nil, "OVERLAY", nil, 6)
	border:SetColorTexture(unpack(Theme.Colors.borderMuted))
	border:SetPoint("TOPLEFT", button, "TOPLEFT", -1, 1)
	border:SetPoint("TOPRIGHT", button, "TOPRIGHT", 1, 1)
	border:SetHeight(1)
	local bottom = button:CreateTexture(nil, "OVERLAY", nil, 6)
	bottom:SetColorTexture(unpack(Theme.Colors.borderMuted))
	bottom:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", -1, -1)
	bottom:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 1, -1)
	bottom:SetHeight(1)
	local left = button:CreateTexture(nil, "OVERLAY", nil, 6)
	left:SetColorTexture(unpack(Theme.Colors.borderMuted))
	left:SetPoint("TOPLEFT", button, "TOPLEFT", -1, 1)
	left:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", -1, -1)
	left:SetWidth(1)
	local right = button:CreateTexture(nil, "OVERLAY", nil, 6)
	right:SetColorTexture(unpack(Theme.Colors.borderMuted))
	right:SetPoint("TOPRIGHT", button, "TOPRIGHT", 1, 1)
	right:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 1, -1)
	right:SetWidth(1)
	state.border = { border, bottom, left, right }
end

function ActionBars:StyleBarFrame(barFrame)
	if not barFrame or self.BarFrameStates[barFrame] then return end

	local state = {
		parent = barFrame:GetParent(),
		frameLevel = barFrame:GetFrameLevel(),
		width = barFrame:GetWidth(),
		height = barFrame:GetHeight(),
		scale = barFrame:GetScale(),
		alpha = barFrame:GetAlpha(),
		points = {},
	}
	self.BarFrameStates[barFrame] = state
	for pointIndex = 1, barFrame:GetNumPoints() do
		local point, relativeTo, relativePoint, x, y = barFrame:GetPoint(pointIndex)
		state.points[#state.points + 1] = {
			point = point, relativeTo = relativeTo, relativePoint = relativePoint, x = x, y = y,
		}
	end
end

function ActionBars:RestoreBarFrame(barFrame)
	local original = barFrame and self.BarFrameStates[barFrame]
	if not original then return end
	if original.parent and barFrame:GetParent() ~= original.parent then
		barFrame:SetParent(original.parent)
	end
	barFrame:ClearAllPoints()
	for _, savedPoint in ipairs(original.points) do
		barFrame:SetPoint(savedPoint.point, savedPoint.relativeTo, savedPoint.relativePoint, savedPoint.x, savedPoint.y)
	end
	barFrame:SetSize(original.width, original.height)
	barFrame:SetScale(original.scale)
	barFrame:SetAlpha(original.alpha)
	barFrame:SetFrameLevel(original.frameLevel)
end

function ActionBars:RestoreButton(button)
	local original = button and self.ButtonStates[button]
	if not original then return end
	if original.parent and button:GetParent() ~= original.parent then
		button:SetParent(original.parent)
	end
	button:ClearAllPoints()
	for _, savedPoint in ipairs(original.points) do
		button:SetPoint(savedPoint.point, savedPoint.relativeTo, savedPoint.relativePoint, savedPoint.x, savedPoint.y)
	end
	button:SetSize(original.width, original.height)
	button:SetScale(original.scale)
	button:SetAlpha(original.alpha)
	button:SetShown(original.shown)
	button:SetFrameLevel(original.frameLevel)
	button:SetHitRectInsets(unpack(original.hitRect))
	if button.HotKey then button.HotKey:SetAlpha(original.hotkeyAlpha) end
	if button.Name then button.Name:SetAlpha(original.nameAlpha) end
	if button.Count then button.Count:SetAlpha(original.countAlpha) end
end

function ActionBars:ApplyBar(barKey)
	local settings = SlickUI.DB.actionBars.bars[barKey]
	local definition = self.Definitions[barKey]
	if not settings or not definition then return end
	local barFrame = _G[definition.frame]

	local holder = self.Holders[barKey] or self:CreateHolder(barKey)
	local buttons = self:GetButtons(barKey)
	if not holder or #buttons == 0 then return end

	local size = settings.buttonSize
	local spacing = settings.spacing
	local slotCount = math.max(1, math.min(#buttons, settings.numberOfSlots or #buttons))
	local columns = math.max(1, math.min(slotCount, settings.buttonsPerRow))
	local rows = math.ceil(slotCount / columns)
	local effectiveSize = size * settings.scale
	holder:SetSize(columns * effectiveSize + (columns - 1) * spacing, rows * effectiveSize + (rows - 1) * spacing)
	holder:SetAlpha(settings.opacity)
	self:ApplyVisibilityDriver(holder, SlickUI.DB.actionBars.enabled and settings.shown ~= false, barKey)

	-- Keep Blizzard's Edit Mode bar container in its original family, but attach
	-- the individual secure buttons to SlickUI's secure visibility holder. This
	-- preserves working SlickUI bars without creating Edit Mode anchor cycles.
	local buttonParent = holder
	local buttonAnchor = holder
	if barFrame and self.BarFrameStates[barFrame] then
		-- Migrate sessions that already ran the older container-parenting code.
		-- Restore before Blizzard gets another opportunity to reconcile Edit Mode.
		self:RestoreBarFrame(barFrame)
	elseif barFrame then
		self:StyleBarFrame(barFrame)
	end

	for index, button in ipairs(buttons) do
		self:StyleButton(button)
		if index > slotCount then
			button:SetAlpha(0)
			button:EnableMouse(false)
		else
		if button:GetParent() ~= buttonParent then
			button:SetParent(buttonParent)
		end
		button:SetFrameLevel(buttonParent:GetFrameLevel() + 2)
		button:SetHitRectInsets(0, 0, 0, 0)
		button:EnableMouse(true)
		button:ClearAllPoints()
		button:SetSize(size, size)
		local column = (index - 1) % columns
		local row = math.floor((index - 1) / columns)
		button:SetPoint("TOPLEFT", buttonAnchor, "TOPLEFT", column * (effectiveSize + spacing), -row * (effectiveSize + spacing))
		button:SetScale(settings.scale)
		button:SetAlpha(settings.opacity)
		SetRegionVisible(button.HotKey, not SlickUI.DB.actionBars.hideHotkeys)
		SetRegionVisible(button.Name, not SlickUI.DB.actionBars.hideMacroNames)
		SetRegionVisible(button.Count, not SlickUI.DB.actionBars.hideCounts)
		end
	end

	if holder.SlickMoverOverlay then
		local visible = not SlickUI.DB.locked
			and (not SlickUI.Movers or SlickUI.Movers:IsMoverOverlayVisible(definition.position))
		holder.SlickMoverOverlay:EnableMouse(visible)
		holder.SlickMoverOverlay:SetShown(visible)
	end
	holder:EnableMouse(not SlickUI.DB.locked)
	holder:SetFrameStrata(SlickUI.DB.locked and "MEDIUM" or "DIALOG")
end

function ActionBars:CaptureInitialVisibility()
	local settings = SlickUI.DB and SlickUI.DB.actionBars
	if not settings or settings.visibilityInitialized then return end

	for _, barKey in ipairs(self.Order) do
		local definition = self.Definitions[barKey]
		local barFrame = definition and _G[definition.frame]
		local barSettings = settings.bars and settings.bars[barKey]
			if barFrame and barSettings and barKey ~= "pet" then
			barSettings.shown = barFrame:IsShown() == true
		end
	end
	settings.visibilityInitialized = true
end

function ActionBars:SetBarShown(barKey, shown)
	if not self.Definitions[barKey] then return false end
	if not SlickUI:IsAvailableOutOfCombat("Action-bar visibility") then return false end
	SlickUI.DB.actionBars.bars[barKey].shown = shown == true
	self:ApplySettings()
	return true
end

function ActionBars:RestoreBlizzardLayout()
	if InCombatLockdown() then
		self.PendingApply = true
		return
	end

	for _, barKey in ipairs(self.Order) do
		for _, button in ipairs(self:GetButtons(barKey)) do
			self:RestoreButton(button)
		end
		local definition = self.Definitions[barKey]
		local barFrame = definition and _G[definition.frame]
		self:RestoreBarFrame(barFrame)
	end
	self:RefreshActionButtonVisuals()
end

-- Never call Blizzard's internal ActionButton_Update functions from addon
-- execution. They change protected attributes and visibility, and doing so can
-- taint every later combat update. Blizzard's own events repaint these buttons.
function ActionBars:RefreshActionButtonVisuals()
	return
end

function ActionBars:QueueSpecialActionVisualRefresh()
	return
end

function ActionBars:BeginBlizzardEditMode()
	if InCombatLockdown() then return false end
	self.BlizzardEditModeActive = true
	for _, holder in pairs(self.Holders) do self:ApplyVisibilityDriver(holder, false) end
	self:SetBlizzardBarContainersSuppressed(false)
	self:SetMainBarShellSuppressed(false)
	self:RestoreBlizzardLayout()
	return true
end

function ActionBars:EndBlizzardEditMode()
	if not self.BlizzardEditModeActive then return end
	C_Timer.After(0.2, function()
		ActionBars.BlizzardEditModeActive = false
		if InCombatLockdown() then ActionBars.PendingApply = true; return end
		local px, py = UIParent:GetCenter()
		if px and py then
			for _, barKey in ipairs(ActionBars.Order) do
				local definition = ActionBars.Definitions[barKey]
				local frame = definition and _G[definition.frame]
				local x, y = frame and frame:GetCenter()
				if x and y then
					SlickUI.DB.positions[definition.position] = { x = x - px, y = y - py }
					local holder = ActionBars.Holders[barKey]
					if holder then
						holder:ClearAllPoints()
						holder:SetPoint("CENTER", UIParent, "CENTER", x - px, y - py)
					end
				end
			end
		end
		ActionBars:ApplySettings()
	end)
end

function ActionBars:RepairActiveEditModeLayout()
	if InCombatLockdown() then
		SlickUI:Print("Action-bar layout repair cannot run during combat.")
		return false
	end
	if not C_EditMode or not C_EditMode.GetLayouts or not C_EditMode.SaveLayouts
		or not C_EditMode.SetActiveLayout or not EditModePresetLayoutManager then
		SlickUI:Print("Blizzard Edit Mode is not ready yet.")
		return false
	end
	local ok, saveInfo = pcall(C_EditMode.GetLayouts)
	if not ok or type(saveInfo) ~= "table" or type(saveInfo.layouts) ~= "table" then
		SlickUI:Print("Could not read the active Blizzard Edit Mode layout.")
		return false
	end
	local layouts = {}
	for _, layout in ipairs(EditModePresetLayoutManager:GetCopyOfPresetLayouts() or {}) do layouts[#layouts + 1] = layout end
	for _, layout in ipairs(saveInfo.layouts) do layouts[#layouts + 1] = layout end
	saveInfo.layouts = layouts
	local layout = saveInfo.layouts[saveInfo.activeLayout]
	local presetType = Enum and Enum.EditModeLayoutType and Enum.EditModeLayoutType.Preset or 0
	if not layout or layout.layoutType == presetType then
		SlickUI:Print("The active Blizzard layout is read-only. Select a custom layout first.")
		return false
	end
	local actionBarSystem = Enum and Enum.EditModeSystem and Enum.EditModeSystem.ActionBar or 0
	local repaired = 0
	for _, systemInfo in ipairs(layout.systems or {}) do
		if systemInfo.system == actionBarSystem and type(systemInfo.anchorInfo) == "table" then
			local frame
			for _, barKey in ipairs(self.Order) do
				local definition = self.Definitions[barKey]
				local candidate = definition and _G[definition.frame]
				if candidate and candidate.systemIndex == systemInfo.systemIndex then frame = candidate; break end
			end
			if frame then
				local x, y = frame:GetCenter()
				local px, py = UIParent:GetCenter()
				if x and y and px and py then
					systemInfo.isInDefaultPosition = false
					systemInfo.anchorInfo.point = "CENTER"
					systemInfo.anchorInfo.relativeTo = "UIParent"
					systemInfo.anchorInfo.relativePoint = "CENTER"
					systemInfo.anchorInfo.offsetX = x - px
					systemInfo.anchorInfo.offsetY = y - py
					repaired = repaired + 1
				end
			end
		end
	end
	if repaired == 0 then
		SlickUI:Print("No Blizzard action-bar layout records were available to repair.")
		return false
	end
	if not pcall(C_EditMode.SaveLayouts, saveInfo) then
		SlickUI:Print("Blizzard rejected the action-bar layout repair.")
		return false
	end
	pcall(C_EditMode.SetActiveLayout, saveInfo.activeLayout)
	SlickUI:Print("Repaired " .. repaired .. " Blizzard action-bar anchors.")
	StaticPopup_Show("SLICKUI_ACTIONBAR_REPAIR_RELOAD")
	return true
end

function ActionBars:ApplySettings()
	if not SlickUI.DB or not SlickUI.DB.actionBars then return end
	if self.BlizzardEditModeActive then return end
	if InCombatLockdown() then
		self.PendingApply = true
		return
	end

	self.PendingApply = false
	if self:ApplySpecialActionMode() then return end
	if not SlickUI.DB.actionBars.enabled then
		for _, holder in pairs(self.Holders) do
			self:ApplyVisibilityDriver(holder, false)
		end
		self:SetMainBarShellSuppressed(false)
		self:SetBlizzardBarContainersSuppressed(false)
		self:RestoreBlizzardLayout()
		return
	end

	for _, barKey in ipairs(self.Order) do
		self:ApplyBar(barKey)
	end
	self:SetBlizzardBarContainersSuppressed(true)
	self:SetMainBarShellSuppressed(true)
	if not SlickUI.DB.locked then self:SetSlickEditMode(true) end
end

function ActionBars:ResetBar(barKey)
	if not SlickUI:IsAvailableOutOfCombat("Action-bar reset") then return end
	local definition = self.Definitions[barKey]
	local defaults = definition and SlickUI.Defaults.positions[definition.position]
	local holder = definition and self.Holders[barKey]
	if not defaults or not holder then return end

	SlickUI.DB.positions[definition.position] = { x = defaults.x, y = defaults.y }
	holder:ClearAllPoints()
	holder:SetPoint("CENTER", UIParent, "CENTER", defaults.x, defaults.y)
end

function ActionBars:Initialize()
	self:CaptureInitialVisibility()
	self.EventFrame = CreateFrame("Frame")
	self.EventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
	self.EventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
	self.EventFrame:RegisterEvent("UPDATE_BINDINGS")
	self.EventFrame:RegisterEvent("ACTIONBAR_PAGE_CHANGED")
	self.EventFrame:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
	self.EventFrame:RegisterEvent("SPELLS_CHANGED")
	self.EventFrame:RegisterEvent("CVAR_UPDATE")
	self.EventFrame:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
	self.EventFrame:RegisterEvent("UNIT_ENTERED_VEHICLE")
	self.EventFrame:RegisterEvent("UNIT_EXITED_VEHICLE")
	self.EventFrame:RegisterEvent("UPDATE_VEHICLE_ACTIONBAR")
	self.EventFrame:RegisterEvent("UPDATE_OVERRIDE_ACTIONBAR")
	self.EventFrame:RegisterEvent("UPDATE_POSSESS_BAR")
	self.EventFrame:RegisterEvent("UPDATE_BONUS_ACTIONBAR")
	self.EventFrame:SetScript("OnEvent", function(_, event)
		if ActionBars.BlizzardEditModeActive then return end
		if event == "PLAYER_REGEN_ENABLED" and not ActionBars.PendingApply then return end
		if InCombatLockdown() then
			ActionBars.PendingApply = true
			return
		end
		C_Timer.After(0, function() ActionBars:ApplySettings() end)
		if event == "UNIT_ENTERED_VEHICLE" or event == "UNIT_EXITED_VEHICLE"
			or event == "UPDATE_VEHICLE_ACTIONBAR" or event == "UPDATE_OVERRIDE_ACTIONBAR"
			or event == "UPDATE_POSSESS_BAR" or event == "UPDATE_BONUS_ACTIONBAR" then
			C_Timer.After(0.25, function() ActionBars:ApplySettings() end)
			ActionBars:QueueSpecialActionVisualRefresh()
		end
	end)

	self:ApplySettings()
	C_Timer.After(1, function() ActionBars:ApplySettings() end)
	C_Timer.After(3, function() ActionBars:ApplySettings() end)
end

SlickUI:RegisterModule("ActionBars", ActionBars)
