local PA, ACL, ACH = unpack(_G.ProjectAzilroka)
local stAM = PA:NewModule('stAddonManager', 'AceEvent-3.0')
PA.stAM, _G.stAddonManager = stAM, stAM

stAddonManagerProfilesDB, stAddonManagerServerDB = {}, {}

stAM.Title, stAM.Description, stAM.Authors, stAM.isEnabled = 'stAddonManager', ACL['A simple and minimalistic addon to disable/enabled addons without logging out.'], 'Azilroka    Safturento', false

local _G = _G
local min, max = min, max
local next, ipairs, sort, unpack, wipe, tinsert, concat = next, ipairs, sort, unpack, wipe, tinsert, table.concat
local format, gsub, strlen, strlower, strfind = format, gsub, strlen, strlower, strfind

local GetNumAddOns = C_AddOns.GetNumAddOns
local GetAddOnInfo = C_AddOns.GetAddOnInfo
local GetAddOnDependencies = C_AddOns.GetAddOnDependencies
local GetAddOnOptionalDependencies = C_AddOns.GetAddOnOptionalDependencies
local DisableAddOn = C_AddOns.DisableAddOn
local EnableAddOn = C_AddOns.EnableAddOn
local GetAddOnMetadata = C_AddOns.GetAddOnMetadata
local DisableAllAddOns = C_AddOns.DisableAllAddOns
local EnableAllAddOns = C_AddOns.EnableAllAddOns
local SaveAddOns = C_AddOns.SaveAddOns

local UIParent, CreateFrame, GameTooltip = UIParent, CreateFrame, GameTooltip

local IsShiftKeyDown = IsShiftKeyDown

local MAX_BUTTONS = 50

_G.StaticPopupDialogs.STADDONMANAGER_OVERWRITEPROFILE = {
	button1 = ACL['Overwrite'],
	button2 = ACL['Cancel'],
	timeout = 0,
	whileDead = 1,
	enterClicksFirstButton = 1,
	hideOnEscape = 1,
}

_G.StaticPopupDialogs.STADDONMANAGER_NEWPROFILE = {
	text = ACL['Enter a name for your new Addon Profile:'],
	button1 = ACL['Create'],
	button2 = ACL['Cancel'],
	timeout = 0,
	hasEditBox = 1,
	whileDead = 1,
	OnAccept = function(self) stAM:NewAddOnProfile(self.editBox:GetText()) end,
	EditBoxOnEnterPressed = function(self) stAM:NewAddOnProfile(self:GetText()) self:GetParent():Hide() end,
	EditBoxOnEscapePressed = function(self) self:GetParent():Hide() end,
}

_G.StaticPopupDialogs.STADDONMANAGER_RENAMEPROFILE = {
	text = ACL['Enter a name for your AddOn Profile:'],
	button1 = ACL['Update'],
	button2 = ACL['Cancel'],
	timeout = 0,
	hasEditBox = 1,
	whileDead = 1,
	EditBoxOnEscapePressed = function(self) self:GetParent():Hide() end,
}

_G.StaticPopupDialogs.STADDONMANAGER_DELETECONFIRMATION = {
	button1 = ACL['Delete'],
	button2 = ACL['Cancel'],
	timeout = 0,
	whileDead = 1,
	enterClicksFirstButton = 1,
	hideOnEscape = 1,
}

stAMCheckButtonMixin = {}

function stAMCheckButtonMixin:OnLoad()
	PA:SetTemplate(self)
end

function stAMCheckButtonMixin:OnClick()
	if self.name then
		if PA:IsAddOnEnabled(self.name, stAM.SelectedCharacter) then
			DisableAddOn(self.name, stAM.SelectedCharacter)
		else
			EnableAddOn(self.name, stAM.SelectedCharacter)
			if stAM.db.EnableRequiredAddons and self.required then
				for _, AddOn in next, self.required do
					EnableAddOn(AddOn)
				end
			end
		end
		stAM:UpdateAddonList()
		SaveAddOns()
	end
end

function stAMCheckButtonMixin:OnEnter()
	GameTooltip:SetOwner(self, 'ANCHOR_TOPRIGHT', 0, 4)
	GameTooltip:ClearLines()
	GameTooltip:AddDoubleLine('AddOn:', self.title, 1, 1, 1, 1, 1, 1)
	GameTooltip:AddDoubleLine(ACL['Authors:'], self.authors, 1, 1, 1, 1, 1, 1)
	GameTooltip:AddDoubleLine(ACL['Version:'], self.version, 1, 1, 1, 1, 1, 1)
	if self.notes ~= nil then
		GameTooltip:AddDoubleLine('Notes:', self.notes, 1, 1, 1, 1, 1, 1)
	end
	if self.required or self.optional then
		GameTooltip:AddLine(' ')
		if self.required then
			GameTooltip:AddDoubleLine('Required Dependencies:', concat(self.required, ', '), 1, 1, 1, 1, 1, 1)
		end
		if self.optional then
			GameTooltip:AddDoubleLine('Optional Dependencies:', concat(self.optional, ', '), 1, 1, 1, 1, 1, 1)
		end
	end
	GameTooltip:Show()

	self:SetBackdropBorderColor(unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor))
end

function stAMCheckButtonMixin:OnLeave()
	PA:SetTemplate(self)
	GameTooltip:Hide()
end

local function strtrim(str)
	return gsub(str, '^%s*(.-)%s*$', '%1')
end

function stAM:OpenPanel()
	_G.PlaySound(SOUNDKIT.IG_MAINMENU_OPTION)
	_G.HideUIPanel(_G.GameMenuFrame)
	_G.ShowUIPanel(stAM.Frame)
end

function stAM:BuildFrame()
	local Frame = CreateFrame('Frame', 'stAMFrame', UIParent)
	local Close = CreateFrame('Button', 'stAMCloseButton', Frame)
	local Search = CreateFrame('EditBox', 'stAMSearchBox', Frame, 'SearchBoxTemplate')

	local Profiles = CreateFrame('Button', 'stAMProfiles', Frame)
	local AddOns = CreateFrame('Frame', 'stAMAddOns', Frame)
	local Slider = CreateFrame('Slider', nil, AddOns)

	local Reload = CreateFrame('Button', 'stAMReload', Frame)
	local RequiredAddons = CreateFrame('CheckButton', nil, Frame)
	local OptionalAddons = CreateFrame('CheckButton', nil, Frame)
	local CharacterSelect = CreateFrame('Button', nil, Frame)

	local Title = Frame:CreateFontString(nil, 'OVERLAY')

	-- Defines
	local font, fontSize, fontFlag = PA:GetFont(stAM.db.Font, stAM.db.FontSize, stAM.db.FontFlag)
	local Texture = PA.Libs.LSM:Fetch('statusbar', stAM.db.CheckTexture)
	local FrameWidth = stAM.db.FrameWidth
	local NumAddOns = stAM.db.NumAddOns
	local Color = stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor
	local Width, Height = stAM.db.ButtonWidth, stAM.db.ButtonHeight

	Frame:SetSize(FrameWidth, 10 + NumAddOns * 25 + 40)
	Frame:SetPoint('CENTER', UIParent, 'CENTER', 0, 0)
	PA:SetTemplate(Frame)
	Frame:Hide()
	Frame:SetFrameStrata('HIGH')
	Frame:SetClampedToScreen(true)
	Frame:SetMovable(true)
	Frame:EnableMouse(true)
	Frame:SetScript('OnMouseDown', Frame.StartMoving)
	Frame:SetScript('OnMouseUp', Frame.StopMovingOrSizing)

	Title:SetPoint('TOPLEFT', 0, -5)
	Title:SetPoint('TOPRIGHT', 0, -5)
	Title:SetFont(font, 14, fontFlag)
	Title:SetText(stAM.Title)
	Title:SetJustifyH('CENTER')
	Title:SetJustifyV('MIDDLE')

	PA:SetTemplate(Close)
	Close:SetPoint('TOPRIGHT', -3, -3)
	Close:SetSize(16, 16)
	Close:SetScript('OnEnter', function() Close:SetBackdropBorderColor(unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor)) end)
	Close:SetScript('OnLeave', function() PA:SetTemplate(Close) end)
	Close:SetScript('OnClick', function() Frame:Hide() end)

	local Mask = Close:CreateMaskTexture()
	Mask:SetTexture('Interface/AddOns/ProjectAzilroka/Media/Textures/Close', 'CLAMPTOBLACKADDITIVE', 'CLAMPTOBLACKADDITIVE')
	Mask:SetSize(10, 10)
	Mask:SetPoint('CENTER')

	Close.Mask = Mask

	Close:SetNormalTexture(Texture)
	Close:SetPushedTexture(Texture)

	local Normal, Pushed = Close:GetNormalTexture(), Close:GetPushedTexture()

	PA:SetInside(Normal, Close)
	Normal:SetVertexColor(1, 1, 1)
	Normal:AddMaskTexture(Mask)

	PA:SetInside(Pushed, Close)
	Pushed:SetVertexColor(1, .2, .2)
	Pushed:AddMaskTexture(Mask)

	Search:SetPoint('TOPLEFT', Title, 'BOTTOMLEFT', 10, -10)
	Search:SetPoint('BOTTOMRIGHT', Profiles, 'BOTTOMLEFT', -5, 0)
	Search:SetSize(1, 20)
	Search.Left:SetTexture()
	Search.Middle:SetTexture()
	Search.Right:SetTexture()
	PA:SetTemplate(Search)
	Search.AddOns = {}
	Search:HookScript('OnEscapePressed', function() stAM:UpdateAddonList() end)
	Search:HookScript('OnTextChanged', function(_, userInput) stAM.scrollOffset = 0 stAM.searchQuery = userInput stAM:UpdateAddonList() end)
	Search:HookScript('OnEditFocusGained', function() Search:SetBackdropBorderColor(unpack(stAM.db.CheckColor)) Search:HighlightText() end)
	Search:HookScript('OnEditFocusLost', function() PA:SetTemplate(Search) Search:HighlightText(0, 0) PA:SetTemplate(Search) end)
	Search.clearButton:HookScript('OnClick', stAM.UpdateAddonList)

	PA:SetTemplate(Reload)
	Reload:SetSize(70, 20)
	Reload:SetScript('OnEnter', function() Reload:SetBackdropBorderColor(unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor)) end)
	Reload:SetScript('OnLeave', function() PA:SetTemplate(Reload) end)
	Reload:SetScript('OnClick', _G.ReloadUI)
	Reload.Text = Reload:CreateFontString(nil, 'OVERLAY')
	Reload.Text:SetFont(font, 12, fontFlag)
	Reload.Text:SetText(ACL['Reload'])
	Reload.Text:SetPoint('CENTER', 0, 0)
	Reload.Text:SetJustifyH('CENTER')
	Reload:SetPoint('TOPLEFT', AddOns, 'BOTTOMLEFT', 0, -10)

	RequiredAddons:SetPoint('LEFT', Reload, 'RIGHT', 50, 0)
	PA:SetTemplate(RequiredAddons)
	RequiredAddons:SetSize(20, 20)
	RequiredAddons:SetScript('OnClick', function()
		stAM.db.EnableRequiredAddons = not stAM.db.EnableRequiredAddons
	end)
	RequiredAddons:SetScript('OnEnter', function()
		GameTooltip:SetOwner(RequiredAddons, 'ANCHOR_TOPRIGHT', 0, 4)
		GameTooltip:ClearLines()
		GameTooltip:AddLine(ACL['Enable Required AddOns'], 1, 1, 1)
		GameTooltip:AddLine(ACL['This will attempt to enable all the "Required" AddOns for the selected AddOn.'], 1, 1, 1)
		GameTooltip:Show()
	end)
	RequiredAddons:SetScript('OnLeave', function() PA:SetTemplate(RequiredAddons) GameTooltip:Hide() end)

	RequiredAddons.CheckTexture = RequiredAddons:CreateTexture(nil, 'OVERLAY', nil, 1)
	RequiredAddons.CheckTexture:SetTexture(Texture)
	RequiredAddons.CheckTexture:SetVertexColor(unpack(Color))
	PA:SetInside(RequiredAddons.CheckTexture, RequiredAddons)

	RequiredAddons:SetCheckedTexture(RequiredAddons.CheckTexture)
	RequiredAddons:SetHighlightTexture('')

	RequiredAddons.Text = RequiredAddons:CreateFontString(nil, 'OVERLAY')
	RequiredAddons.Text:SetPoint('LEFT', RequiredAddons, 'RIGHT', 5, 0)
	RequiredAddons.Text:SetFont(font, 12, fontFlag)
	RequiredAddons.Text:SetText(ACL['Required'])

	RequiredAddons:SetChecked(stAM.db.EnableRequiredAddons)

	CharacterSelect:SetPoint('TOPRIGHT', AddOns, 'BOTTOMRIGHT', 0, -10)
	CharacterSelect.DropDown = CreateFrame('Frame', 'stAMCharacterSelectDropDown', CharacterSelect, 'UIDropDownMenuTemplate')
	CharacterSelect:SetSize(150, 20)
	PA:SetTemplate(CharacterSelect)

	CharacterSelect:SetScript('OnEnter', function() CharacterSelect:SetBackdropBorderColor(unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor)) end)
	CharacterSelect:SetScript('OnLeave', function() PA:SetTemplate(CharacterSelect) end)
	CharacterSelect:SetScript('OnClick', function() PA:EasyMenu(stAM.Menu, CharacterSelect.DropDown, CharacterSelect, 0, 38 + (stAM.MenuOffset * 16), 'MENU', 5) end)
	CharacterSelect.Text = CharacterSelect:CreateFontString(nil, 'OVERLAY')
	CharacterSelect.Text:SetFont(font, 12, fontFlag)
	CharacterSelect.Text:SetText(ACL['Character Select'])
	CharacterSelect.Text:SetPoint('CENTER', 0, 0)
	CharacterSelect.Text:SetJustifyH('CENTER')

	Profiles:SetPoint('TOPRIGHT', Title, 'BOTTOMRIGHT', -10, -10)
	PA:SetTemplate(Profiles)
	Profiles:SetSize(70, 20)
	Profiles:SetScript('OnEnter', function() Profiles:SetBackdropBorderColor(unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor)) end)
	Profiles:SetScript('OnLeave', function() PA:SetTemplate(Profiles) end)
	Profiles:SetScript('OnClick', function() stAM:ToggleProfiles() end)

	Profiles.Text = Profiles:CreateFontString(nil, 'OVERLAY')
	Profiles.Text:SetFont(font, 12, fontFlag)
	Profiles.Text:SetText(ACL['Profiles'])
	Profiles.Text:SetPoint('CENTER', 0, 0)
	Profiles.Text:SetJustifyH('CENTER')

	AddOns:SetPoint('TOPLEFT', Search, 'BOTTOMLEFT', 0, -5)
	AddOns:SetPoint('TOPRIGHT', Profiles, 'BOTTOMRIGHT', 0, -5)
	AddOns:SetHeight(NumAddOns * (Height + 5) + 15)
	PA:SetTemplate(AddOns)
	AddOns.Buttons = {}
	AddOns:EnableMouse(true)
	AddOns:EnableMouseWheel(true)

	Slider:SetPoint('RIGHT', -2, 0)
	Slider:SetWidth(12)
	Slider:SetHeight(NumAddOns * (Height + 5) + 11)
	Slider:SetThumbTexture(PA.Libs.LSM:Fetch('background', 'Solid'))
	Slider:SetOrientation('VERTICAL')
	Slider:SetValueStep(1)
	PA:SetTemplate(Slider)
	Slider:SetMinMaxValues(0, 1)
	Slider:SetValue(0)
	Slider:EnableMouse(true)
	Slider:EnableMouseWheel(true)

	local Thumb = Slider:GetThumbTexture()
	Thumb:SetSize(8, 16)
	Thumb:SetVertexColor(unpack(Color))

	AddOns.ScrollBar = Slider

	local OnScroll = function(_, delta)
		local numAddons = stAM.searchQuery and #Search.AddOns or #stAM.AddOnInfo
		if numAddons < stAM.db.NumAddOns then return end

		if IsShiftKeyDown() then
			if delta == 1 then
				stAM.scrollOffset = max(0, stAM.scrollOffset - stAM.db.NumAddOns)
			elseif delta == -1 then
				stAM.scrollOffset = min(numAddons - stAM.db.NumAddOns, stAM.scrollOffset + stAM.db.NumAddOns)
			end
		else
			if delta == 1 and stAM.scrollOffset > 0 then
				stAM.scrollOffset = stAM.scrollOffset - 1
			elseif delta == -1 then
				if stAM.scrollOffset < numAddons - stAM.db.NumAddOns then
					stAM.scrollOffset = stAM.scrollOffset + 1
				end
			end
		end

		Slider:SetMinMaxValues(0, (numAddons - stAM.db.NumAddOns))
		Slider:SetValue(stAM.scrollOffset)
		stAM:UpdateAddonList()
	end

	AddOns:SetScript('OnMouseWheel', OnScroll)
	Slider:SetScript('OnMouseWheel', OnScroll)
	Slider:SetScript('OnValueChanged', function(_, value)
		stAM.scrollOffset = value
		OnScroll()
	end)

	for i = 1, MAX_BUTTONS do
		local CheckButton = CreateFrame('CheckButton', 'stAMCheckButton_'..i, AddOns, 'stAMCheckButton')
		CheckButton:SetSize(Width, Height)
		CheckButton:SetPoint(unpack(i == 1 and {'TOPLEFT', AddOns, 'TOPLEFT', 10, -10} or {'TOP', AddOns.Buttons[i-1], 'BOTTOM', 0, -5}))
		CheckButton:SetCheckedTexture(Texture)
		local Checked = CheckButton:GetCheckedTexture()
		Checked:SetVertexColor(unpack(Color))
		PA:SetInside(Checked, CheckButton)

		CheckButton:SetHighlightTexture('')

		CheckButton.Text:SetFont(font, fontSize, fontFlag)
		CheckButton.StatusText:SetFont(font, fontSize, fontFlag)

		AddOns.Buttons[i] = CheckButton
	end

	Frame.Title = Title
	Frame.Close = Close
	Frame.Reload = Reload
	Frame.RequiredAddons = RequiredAddons
	Frame.OptionalAddons = OptionalAddons
	Frame.Search = Search
	Frame.CharacterSelect = CharacterSelect
	Frame.Profiles = Profiles
	Frame.AddOns = AddOns

	Frame.AddOns:SetHeight(stAM.db.NumAddOns * (stAM.db.ButtonHeight + 5) + 15)
	Frame:SetSize(stAM.db.FrameWidth, Frame.Title:GetHeight() + Frame.Search:GetHeight() + Frame.AddOns:GetHeight() + Frame.Profiles:GetHeight() + 40)

	stAM.Frame = Frame

	tinsert(_G.UISpecialFrames, stAM.Frame:GetName())

	if _G.GameMenuButtonAddons then
		_G.GameMenuButtonAddons:SetScript('OnClick', stAM.OpenPanel)
	else
		-- Game menu buttons are no longer persistent. Must be hooked every time the game menu is opened.
		hooksecurefunc(GameMenuFrame, 'Layout', function()
			for button in GameMenuFrame.buttonPool:EnumerateActive() do
				local text = button:GetText()

				if (text == _G["ADDONS"]) then
					button:SetScript("OnClick", stAM.OpenPanel)
				end
			end
		end)
	end
end

function stAM:NewAddOnProfile(name, overwrite)
	if _G.stAddonManagerProfilesDB[name] and (not overwrite) then
		_G.StaticPopupDialogs['STADDONMANAGER_OVERWRITEPROFILE'].text = format(ACL['There is already a profile named %s. Do you want to overwrite it?'], name)
		_G.StaticPopupDialogs['STADDONMANAGER_OVERWRITEPROFILE'].OnAccept = function() stAM:NewAddOnProfile(name, true) end
		_G.StaticPopup_Show('STADDONMANAGER_OVERWRITEPROFILE')
		return
	end

	_G.stAddonManagerProfilesDB[name] = {}

	for i in next, stAM.AddOnInfo do
		local AddOn, isEnabled = stAM.AddOnInfo[i].Name, PA:IsAddOnEnabled(i, stAM.SelectedCharacter)
		if isEnabled then
			tinsert(_G.stAddonManagerProfilesDB[name], AddOn)
		end
	end

	stAM:UpdateProfiles()
end

function stAM:LoadProfile(name)
	if not IsShiftKeyDown() then
		DisableAllAddOns(stAM.SelectedCharacter)
	end

	for _, AddOn in next, _G.stAddonManagerProfilesDB[name] do
		EnableAddOn(AddOn, stAM.SelectedCharacter)
	end

	stAM:UpdateAddonList()
end

function stAM:InitProfiles()
	local ProfileMenu = CreateFrame('Frame', 'stAMProfileMenu', stAM.Frame)
	ProfileMenu:SetPoint('TOPLEFT', stAM.Frame, 'TOPRIGHT', 3, 0)
	ProfileMenu:SetSize(250, 50)
	PA:SetTemplate(ProfileMenu)
	ProfileMenu:Hide()

	for _, name in next, {'EnableAll', 'DisableAll', 'NewButton'} do
		local Button = CreateFrame('Button', nil, ProfileMenu)
		PA:SetTemplate(Button)
		Button:SetSize(stAM.db.ButtonWidth, stAM.db.ButtonHeight)
		Button:SetScript('OnEnter', function() Button:SetBackdropBorderColor(unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor)) end)
		Button:SetScript('OnLeave', function() PA:SetTemplate(Button) end)

		Button.Text = Button:CreateFontString(nil, 'OVERLAY')
		Button.Text:SetFont(PA.Libs.LSM:Fetch('font', stAM.db.Font), 12, 'OUTLINE')
		Button.Text:SetPoint('CENTER', 0, 0)
		Button.Text:SetJustifyH('CENTER')

		ProfileMenu[name] = Button
	end

	ProfileMenu.EnableAll.Text:SetText(ACL['Enable All'])
	ProfileMenu.EnableAll:SetPoint('TOPLEFT', ProfileMenu, 'TOPLEFT', 10, -10)
	ProfileMenu.EnableAll:SetPoint('TOPRIGHT', ProfileMenu, 'TOP', -3, -10)
	ProfileMenu.EnableAll:SetScript('OnClick', function() EnableAllAddOns(stAM.SelectedCharacter) stAM:UpdateAddonList() end)

	ProfileMenu.DisableAll.Text:SetText(ACL['Disable All'])
	ProfileMenu.DisableAll:SetPoint('TOPRIGHT', ProfileMenu, 'TOPRIGHT', -10, -10)
	ProfileMenu.DisableAll:SetPoint('TOPLEFT', ProfileMenu, 'TOP', 2, -10)
	ProfileMenu.DisableAll:SetScript('OnClick', function() DisableAllAddOns(stAM.SelectedCharacter) stAM:UpdateAddonList() end)

	ProfileMenu.NewButton.Text:SetText(ACL['New Profile'])
	ProfileMenu.NewButton:SetPoint('TOPLEFT', ProfileMenu.EnableAll, 'BOTTOMLEFT', 0, -5)
	ProfileMenu.NewButton:SetPoint('TOPRIGHT', ProfileMenu.DisableAll, 'BOTTOMRIGHT', 0, -5)
	ProfileMenu.NewButton:SetScript('OnClick', function() _G.StaticPopup_Show('STADDONMANAGER_NEWPROFILE') end)

	ProfileMenu.Buttons = {}

	for i = 1, MAX_BUTTONS do
		local Pullout = CreateFrame('Frame', nil, ProfileMenu)
		Pullout:SetWidth(210)
		Pullout:SetHeight(stAM.db.ButtonHeight)
		Pullout:Hide()

		for _, Frame in next, {'Load', 'Delete', 'Update'} do
			local Button = CreateFrame('Button', nil, Pullout)
			PA:SetTemplate(Button)
			Button:SetSize(73, stAM.db.ButtonHeight)
			Button:RegisterForClicks('AnyDown')
			Button:SetScript('OnEnter', function() Button:SetBackdropBorderColor(unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor)) end)
			Button:SetScript('OnLeave', function() PA:SetTemplate(Button) end)

			Button.Text = Button:CreateFontString(nil, 'OVERLAY')
			Button.Text:SetFont(PA.Libs.LSM:Fetch('font', stAM.db.Font), 12, 'OUTLINE')
			Button.Text:SetPoint('CENTER', 0, 0)
			Button.Text:SetJustifyH('CENTER')

			Pullout[Frame] = Button
		end

		Pullout.Load:SetPoint('LEFT', Pullout, 0, 0)
		Pullout.Load.Text:SetText('Load')
		Pullout.Load:SetScript('OnClick', function(_, btn)
			if btn == 'RightButton' then
				local Dialog = _G.StaticPopupDialogs.STADDONMANAGER_RENAMEPROFILE
				Dialog.OnAccept = function(dialog)
					_G.stAddonManagerProfilesDB[Pullout.Name] = nil
					stAM:NewAddOnProfile(dialog.editBox:GetText())
					stAM:UpdateProfiles()
				end
				Dialog.EditBoxOnEnterPressed = function(editBox)
					_G.stAddonManagerProfilesDB[Pullout.Name] = nil
					stAM:NewAddOnProfile(editBox:GetText())
					stAM:UpdateProfiles()
					editBox:GetParent():Hide()
				end
				_G.StaticPopup_Show('STADDONMANAGER_RENAMEPROFILE')
			else
				stAM:LoadProfile(Pullout.Name)
			end
		end)

		Pullout.Update:SetPoint('LEFT', Pullout.Load, 'RIGHT', 5, 0)
		Pullout.Update.Text:SetText(ACL['Update'])
		Pullout.Update:SetScript('OnClick', function() stAM:NewAddOnProfile(Pullout.Name, true) end)

		Pullout.Delete:SetPoint('LEFT', Pullout.Update, 'RIGHT', 5, 0)
		Pullout.Delete.Text:SetText(ACL['Delete'])
		Pullout.Delete:SetScript('OnClick', function()
			local dialog = _G.StaticPopupDialogs['STADDONMANAGER_DELETECONFIRMATION']

			dialog.text = format(ACL['Are you sure you want to delete %s?'], Pullout.Name)
			dialog.OnAccept = function() _G.stAddonManagerProfilesDB[Pullout.Name] = nil stAM:UpdateProfiles() end

			_G.StaticPopup_Show('STADDONMANAGER_DELETECONFIRMATION')
		end)

		ProfileMenu.Buttons[i] = Pullout
	end

	stAM.ProfileMenu = ProfileMenu
end

function stAM:UpdateProfiles()
	local ProfileMenu = stAM.ProfileMenu

	wipe(stAM.Profiles)
	for name, _ in next, _G.stAddonManagerProfilesDB do tinsert(stAM.Profiles, name) end
	sort(stAM.Profiles)

	local PreviousButton
	for i, Button in ipairs(ProfileMenu.Buttons) do
		local isShown = i <= min(#stAM.Profiles, MAX_BUTTONS)
		if isShown then
			Button.Load.Text:SetText(stAM.Profiles[i])
		end

		Button.Name = isShown and stAM.Profiles[i] or nil
		Button:SetShown(isShown)

		if i == 1 then
			Button:SetPoint('TOPLEFT', ProfileMenu.NewButton, 'BOTTOMLEFT', 0, -5)
		else
			Button:SetPoint('TOP', PreviousButton, 'BOTTOM', 0, -5)
		end

		PreviousButton = Button
	end

	ProfileMenu:SetHeight((min(#stAM.Profiles, MAX_BUTTONS) + 2) * (stAM.db.ButtonHeight + 5) + 15)
end

function stAM:ToggleProfiles()
	_G.ToggleFrame(stAM.ProfileMenu)
	stAM:UpdateProfiles()
end

function stAM:UpdateAddonList()
	wipe(stAM.Frame.Search.AddOns)

	if stAM.searchQuery then
		local query = strlower(strtrim(stAM.Frame.Search:GetText()))

		if (strlen(query) == 0) then
			stAM.searchQuery = false
		end

		for i, AddOn in ipairs(stAM.AddOnInfo) do
			local name, title, authors = AddOn.Name, AddOn.Title, AddOn.Authors

			if strfind(strlower(name), query, nil, true) or strfind(strlower(title), query, nil, true) or (authors and strfind(strlower(authors), query, nil, true)) then
				tinsert(stAM.Frame.Search.AddOns, i)
			end
		end
	end

	for i, button in ipairs(stAM.Frame.AddOns.Buttons) do
		local addonIndex = (not stAM.searchQuery and (stAM.scrollOffset + i)) or stAM.Frame.Search.AddOns[stAM.scrollOffset + i]
		local info = stAM.AddOnInfo[addonIndex]

		if addonIndex and addonIndex <= #stAM.AddOnInfo then
			button.name, button.title, button.authors, button.version, button.notes, button.required, button.optional, button.icon, button.atlas = info.Name, info.Title, info.Authors, info.Version, info.Notes, info.Required, info.Optional, info.Icon, info.Atlas

			button.Text:SetText(info.Title)

			if info.Icon then
				button.Icon:SetTexture(info.Icon)
				button.Icon:SetTexCoord(PA:TexCoords())
			elseif info.Atlas then
				button.Icon:SetAtlas(info.Atlas)
				button.Icon:SetTexCoord(0, 1, 0, 1)
			end

			button.Icon:SetShown(info.Icon or info.Atlas)
			button.StatusIcon:SetShown(info.Missing or info.Disabled)
			button.StatusText:SetShown(info.Missing or info.Disabled)

			if info.Missing or info.Disabled then
				if info.Missing then
					button.StatusIcon:SetVertexColor(.77, .12, .24)
					button.StatusText:SetVertexColor(.77, .12, .24)
					button.StatusText:SetText(ACL['Missing: ']..concat(info.Missing, ', '))
				else
					button.StatusIcon:SetVertexColor(1, .8, .1)
					button.StatusText:SetVertexColor(1, .8, .1)
					button.StatusText:SetText(ACL['Disabled: ']..concat(info.Disabled, ', '))
				end

				button.Icon:SetPoint('LEFT', button.StatusIcon, 'RIGHT', 5, 0)
				button.Text:SetPoint('LEFT', button.Icon, 'RIGHT', 5, 0)
				button.Text:SetPoint('RIGHT', stAM.Frame.AddOns, 'CENTER', 0, 0)
			elseif (info.Icon or info.Atlas) then
				button.Icon:SetPoint('LEFT', button, 'RIGHT', 5, 0)
				button.Text:SetPoint('LEFT', button.Icon, 'RIGHT', 5, 0)
				button.Text:SetPoint('RIGHT', stAM.Frame.AddOns, 'RIGHT', -10, 0)
			else
				button.Text:SetPoint('LEFT', button, 'RIGHT', 5, 0)
				button.Text:SetPoint('RIGHT', stAM.Frame.AddOns, 'RIGHT', -10, 0)
			end

			button:SetChecked(PA:IsAddOnPartiallyEnabled(addonIndex, stAM.SelectedCharacter) or PA:IsAddOnEnabled(addonIndex, stAM.SelectedCharacter))
			button:GetCheckedTexture():SetVertexColor(unpack(PA:IsAddOnPartiallyEnabled(addonIndex, stAM.SelectedCharacter) and {.6, .6, .6} or stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor))
			button:SetShown(i <= min(#stAM.Frame.Search.AddOns > 0 and #stAM.Frame.Search.AddOns or stAM.db.NumAddOns, stAM.db.NumAddOns))
		else
			button:SetShown(false)
		end
	end
end

function stAM:Update()
	if not stAM.Frame then return end

	stAM.Frame:SetSize(stAM.db.FrameWidth, stAM.Frame.Title:GetHeight() + 5 + stAM.Frame.Search:GetHeight() + 5 + stAM.Frame.AddOns:GetHeight() + 10 + stAM.Frame.Profiles:GetHeight() + 20)
	stAM.Frame.AddOns:SetHeight(stAM.db.NumAddOns * (stAM.db.ButtonHeight + 5) + 15)
	stAM.Frame.AddOns.ScrollBar:SetHeight(stAM.db.NumAddOns * (stAM.db.ButtonHeight + 5) + 11)

	local font, fontSize, fontFlag = PA:GetFont(stAM.db.Font, stAM.db.FontSize, stAM.db.FontFlag)
	local checkTexture = PA.Libs.LSM:Fetch('statusbar', stAM.db.CheckTexture)
	local r, g, b, a = unpack(stAM.db.ClassColor and PA.ClassColor or stAM.db.CheckColor)
	local iconSize = stAM.db.ButtonHeight

	for _, CheckButton in ipairs(stAM.Frame.AddOns.Buttons) do
		CheckButton:SetSize(stAM.db.ButtonWidth, stAM.db.ButtonHeight)
		CheckButton.Icon:SetSize(iconSize, iconSize)
		CheckButton.Text:SetFont(font, fontSize, fontFlag)
		CheckButton.StatusText:SetFont(font, fontSize, fontFlag)
		CheckButton:SetCheckedTexture(checkTexture)
		CheckButton:GetCheckedTexture():SetVertexColor(r, g, b, a)
	end

	stAM.Frame.AddOns.ScrollBar:GetThumbTexture():SetVertexColor(r, g, b, a)
	stAM.Frame.Title:SetFont(font, 14, fontFlag)
	stAM.Frame.Search:SetFont(font, 12, fontFlag)
	stAM.Frame.Reload.Text:SetFont(font, 12, fontFlag)
	stAM.Frame.Profiles.Text:SetFont(font, 12, fontFlag)
	stAM.Frame.CharacterSelect.Text:SetFont(font, 12, fontFlag)
	stAM.Frame.RequiredAddons.Text:SetFont(font, 12, fontFlag)

	stAM.Frame.RequiredAddons:SetChecked(stAM.db.EnableRequiredAddons)
	stAM.Frame.OptionalAddons:SetChecked(stAM.db.EnableOptionalAddons)

	stAM.ProfileMenu.EnableAll.Text:SetFont(font, 12, fontFlag)
	stAM.ProfileMenu.DisableAll.Text:SetFont(font, 12, fontFlag)
	stAM.ProfileMenu.NewButton.Text:SetFont(font, 12, fontFlag)

	for _, Button in ipairs(stAM.ProfileMenu.Buttons) do
		Button.Load.Text:SetFont(font, 12, fontFlag)
		Button.Update.Text:SetFont(font, 12, fontFlag)
		Button.Delete.Text:SetFont(font, 12, fontFlag)
	end

	stAM:UpdateAddonList()
end

function stAM:GetOptions()
	local stAddonManager = ACH:Group(stAM.Title, stAM.Description, nil, nil, function(info) return stAM.db[info[#info]] end, function(info, value) stAM.db[info[#info]] = value stAM:Update() end)
	PA.Options.args.stAddonManager = stAddonManager

	stAddonManager.args.Description = ACH:Description(stAM.Description, 0)
	stAddonManager.args.Enable = ACH:Toggle(ACL['Enable'], nil, 1, nil, nil, nil, nil, function(info, value) stAM.db[info[#info]] = value if not stAM.isEnabled then stAM:Initialize() else _G.StaticPopup_Show('PROJECTAZILROKA_RL') end end)

	stAddonManager.args.General = ACH:Group(ACL['General'], nil, 2)
	stAddonManager.args.General.inline = true

	stAddonManager.args.General.args.NumAddOns = ACH:Range(ACL['# Shown AddOns'], nil, 1, { min = 3, max = MAX_BUTTONS, step = 1 })
	stAddonManager.args.General.args.FrameWidth = ACH:Range(ACL['Frame Width'], nil, 2, { min = 250, max = 2048, step = 2 })
	stAddonManager.args.General.args.ButtonHeight = ACH:Range(ACL['Button Height'], nil, 3, { min = 3, max = 30, step = 1 })
	stAddonManager.args.General.args.ButtonWidth = ACH:Range(ACL['Button Width'], nil, 4, { min = 3, max = 30, step = 1 })
	stAddonManager.args.General.args.EnableRequiredAddons = ACH:Toggle(ACL['Enable Required AddOns'], ACL['This will attempt to enable all the "Required" AddOns for the selected AddOn.'], 5)
	stAddonManager.args.General.args.CheckTexture = ACH:SharedMediaStatusbar(ACL['Texture'], nil, 6)
	stAddonManager.args.General.args.CheckColor = ACH:Color(COLOR_PICKER, nil, 2, true, nil, function(info) return unpack(stAM.db[info[#info]]) end, function(info, r, g, b, a) stAM.db[info[#info]] = { r, g, b, a} stAM:Update() end, function() return stAM.db.ClassColor end)
	stAddonManager.args.General.args.ClassColor = ACH:Toggle(ACL['Class Color Check Texture'], nil, 8)

	stAddonManager.args.General.args.FontSettings = ACH:Group(ACL['Font Settings'], nil, -1)
	stAddonManager.args.General.args.FontSettings.inline = true
	stAddonManager.args.General.args.FontSettings.args.Font = ACH:SharedMediaFont(ACL['Font'], nil, 1)
	stAddonManager.args.General.args.FontSettings.args.FontSize = ACH:Range(FONT_SIZE, nil, 2, { min = 6, max = 22, step = 1 })
	stAddonManager.args.General.args.FontSettings.args.FontFlag = ACH:FontFlags(ACL['Font Outline'], nil, 3)

	stAddonManager.args.AuthorHeader = ACH:Header(ACL['Authors:'], -2)
	stAddonManager.args.Authors = ACH:Description(stAM.Authors, -1, 'large')
end

function stAM:BuildProfile()
	PA.Defaults.profile.stAddonManager = {
		Enable = true,
		NumAddOns = 30,
		FrameWidth = 550,
		Font = 'PT Sans Narrow Bold',
		FontSize = 16,
		FontFlag = 'OUTLINE',
		ButtonHeight = 18,
		ButtonWidth = 22,
		CheckColor = { 0, .66, 1},
		ClassColor = false,
		CheckTexture = 'Solid',
		EnableRequiredAddons = true,
		EnableOptionalAddons = false,
	}
end

function stAM:UpdateSettings()
	stAM.db = PA.db.stAddonManager
end

function stAM:Initialize()
	if stAM.db.Enable ~= true then
		return
	end

	stAM.isEnabled = true

	stAM.Menu = {
		{ order = 0, text = 'All', checked = function() return stAM.SelectedCharacter == true end, func = function() stAM.SelectedCharacter = true stAM:UpdateAddonList() end}
	}

	stAddonManagerServerDB[PA.MyRealm] = stAddonManagerServerDB[PA.MyRealm] or {}
	stAddonManagerServerDB[PA.MyRealm][PA.MyName] = true

	local function menuSort(a, b)
		if a.order and b.order and not (a.order == b.order) then
			return a.order < b.order
		end
		return a.text < b.text
	end

	for Character in next, stAddonManagerServerDB[PA.MyRealm] do
		tinsert(stAM.Menu, { text = Character, checked = function() return stAM.SelectedCharacter == Character end, func = function() stAM.SelectedCharacter = Character stAM:UpdateAddonList() end })
	end
	sort(stAM.Menu, menuSort)

	stAM.AddOnInfo, stAM.Profiles, stAM.SelectedCharacter, stAM.MenuOffset, stAM.scrollOffset = {}, {}, PA.MyName, #stAM.Menu, 0

	local addonCache, _ = {}
	for addonIndex = 1, GetNumAddOns() do
		local Name, Title, Notes = GetAddOnInfo(addonIndex)
		local Required, Optional = { GetAddOnDependencies(addonIndex) }, { GetAddOnOptionalDependencies(addonIndex) }
		local iconTexture = GetAddOnMetadata(addonIndex, "IconTexture")
		local iconAtlas = GetAddOnMetadata(addonIndex, "IconAtlas")
		local MissingAddons, DisabledAddons

		if not next(Required) then
			Required = nil
		else
			for num, addon in next, Required do
				local Reason = addonCache[addon]
				if not Reason then
					_, _, _, _, Reason = GetAddOnInfo(addon)
					addonCache[addon] = Reason
				end
				if Reason == 'MISSING' then
					MissingAddons = MissingAddons or {}
					tinsert(MissingAddons, addon)
				elseif Reason == 'DISABLED' then
					DisabledAddons = DisabledAddons or {}
					tinsert(DisabledAddons, addon)
				end
			end
		end

--		Notes = Notes and Notes:gsub('[|n\n]', '')

		if not next(Optional) then
			Optional = nil
		end

		stAM.AddOnInfo[addonIndex] = { Name = Name, Title = Title, Authors = GetAddOnMetadata(addonIndex, 'Author'), Version = GetAddOnMetadata(addonIndex, 'Version'), Notes = Notes, Required = Required, Optional = Optional, Missing = MissingAddons, Disabled = DisabledAddons, Icon = iconTexture, Atlas = iconAtlas }
	end

	stAM:BuildFrame()
	stAM:InitProfiles()
	stAM:Update()
end
