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,
}
SlickUI.ActionBars = ActionBars

-- 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"
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
			if frame.SetMouseMotionEnabled then frame:SetMouseMotionEnabled(false) end
			if frame.SetMouseClickEnabled then frame:SetMouseClickEnabled(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
			if frame.SetMouseMotionEnabled then frame:SetMouseMotionEnabled(saved.mouse == true) end
			if frame.SetMouseClickEnabled then frame:SetMouseClickEnabled(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
			if frame.SetMouseMotionEnabled then frame:SetMouseMotionEnabled(saved.mouse == true) end
			if frame.SetMouseClickEnabled then frame:SetMouseClickEnabled(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
				if frame.SetMouseMotionEnabled then frame:SetMouseMotionEnabled(false) end
				if frame.SetMouseClickEnabled then frame:SetMouseClickEnabled(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
				if frame.SetMouseMotionEnabled then frame:SetMouseMotionEnabled(saved.mouse == true) end
				if frame.SetMouseClickEnabled then frame:SetMouseClickEnabled(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 = shown and (barKey == "pet" and PET_VISIBILITY_DRIVER or ACTIVE_VISIBILITY_DRIVER) or HIDDEN_VISIBILITY_DRIVER
	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: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)

	-- Never parent an Edit Mode managed Blizzard bar container to a SlickUI
	-- holder. Blizzard anchors its bars to one another while applying a layout;
	-- making any container depend on our holder can turn that chain into a cycle.
	-- Keep the Blizzard container in its original family and place only the
	-- individual secure buttons on the independent SlickUI holder.
	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)
		button:SetShown(index <= slotCount)
		if index > slotCount then
			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)
		if button.SetMouseMotionEnabled then button:SetMouseMotionEnabled(true) end
		if button.SetMouseClickEnabled then button:SetMouseClickEnabled(true) end
		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
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. Reloading UI...")
	C_Timer.After(0.5, ReloadUI)
	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 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: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)
	end)

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

SlickUI:RegisterModule("ActionBars", ActionBars)
