local t,M="\065\099\101\067\111\110\115\111\108\101-\051\046\048",7 local u,r=LibStub:NewLibrary(t,M)if not u then return end u.embeds=u.embeds or{}u.commands=u.commands or{}u.weakcommands=u.weakcommands or{}local W,d,g=table.concat,tostring,select local V,a,L=type,pairs,error local e,X,S=string.format,string.find,string.sub local Q=math.max local A=_G local C={}local function b(t,M,...)local r=0 if t~=u then r=r+1 C[r]="\124\099\102\102\051\051\102\102\057\057"..(d(t).."\124\114\058")end for t=1,g("\035",...),1 do r=r+1 C[r]=d(g(t,...))end M:AddMessage(W(C," ",1,r))end function u.Print(t,...)local M=...if V(M)=="\116\097\098\108\101"and M.AddMessage then return b(t,M,g(2,...))else return b(t,DEFAULT_CHAT_FRAME,...)end end function u.Printf(t,...)local M=...if V(M)=="\116\097\098\108\101"and M.AddMessage then return b(t,M,e(g(2,...)))else return b(t,DEFAULT_CHAT_FRAME,e(...))end end function u.RegisterChatCommand(W,t,M,r)if V(t)~="\115\116\114\105\110\103"then L("\085\115\097\103\101\058 \065\099\101\067\111\110\115\111\108\101\058\082\101\103\105\115\116\101\114\067\104\097\116\067\111\109\109\097\110\100\040 \"\099\111\109\109\097\110\100\"\044 \102\117\110\099\091\044 \112\101\114\115\105\115\116 \093\041\058 \'\099\111\109\109\097\110\100\' - \101\120\112\101\099\116\101\100 \097 \115\116\114\105\110\103",2)end if r==nil then r=true end local d="\065\067\069\067\079\078\083\079\076\069\095"..t:upper()if V(M)=="\115\116\114\105\110\103"then SlashCmdList[d]=function(t,u)W[M](W,t,u)end else SlashCmdList[d]=M end A["\083\076\065\083\072\095"..(d.."\049")]="\047"..t:lower()u.commands[t]=d if not r then if not u.weakcommands[W]then u.weakcommands[W]={}end u.weakcommands[W][t]=M end return true end function u.UnregisterChatCommand(M,t)local r=u.commands[t]if r then SlashCmdList[r]=nil A["\083\076\065\083\072\095"..(r.."\049")]=nil hash_SlashCmdList["\047"..t:upper()]=nil u.commands[t]=nil end end function u.IterateChatCommands(t)return a(u.commands)end local function p(t,...)if t>1 then return nil,p(t-1,...)elseif t==1 then return nil,...else return...end end function u.GetArgs(W,t,M,r)M=M or 1 r=Q(r or 1,1)local d=r d=X(t,"\091\094 \093",d)if not d then return p(M,1000000000.0)end if M<1 then return d end local g local V=S(t,d,d)if V=="\""then d=d+1 g="\040\091\124\"\093\041"elseif V=="\'"then d=d+1 g="\040\091\124\'\093\041"else g="\040\091\124 \093\041"end r=d while true do local W d,W,V=X(t,g,d)if not d then break end if V=="\124"then if S(t,d,d+1)=="\124\072"then d=X(t,"\124\104",d+2)if not d then break end d=X(t,"\124\104",d+2)if not d then break end elseif S(t,d,d+1)=="\124\084"then d=X(t,"\124\116",d+2)if not d then break end end d=d+2 else return S(t,r,d-1),u:GetArgs(t,M-1,d+1)end end return S(t,r),p(M-1,1000000000.0)end local R={"\080\114\105\110\116","\080\114\105\110\116\102";"\082\101\103\105\115\116\101\114\067\104\097\116\067\111\109\109\097\110\100","\085\110\114\101\103\105\115\116\101\114\067\104\097\116\067\111\109\109\097\110\100","\071\101\116\065\114\103\115"}function u.Embed(M,t)for u,r in a(R)do t[r]=M[r]end M.embeds[t]=true return t end function u.OnEmbedEnable(M,t)if u.weakcommands[t]then for M,u in a(u.weakcommands[t])do t:RegisterChatCommand(M,u,false,true)end end end function u.OnEmbedDisable(M,t)if u.weakcommands[t]then for M,u in a(u.weakcommands[t])do t:UnregisterChatCommand(M)end end end for t in a(u.embeds)do u:Embed(t)end
--- **AceConsole-3.0** provides registration facilities for slash commands.
-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
-- to your addons individual needs.
--
-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
-- and can be accessed directly, without having to explicitly call AceConsole itself.\\
-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
-- make into AceConsole.
-- @class file
-- @name AceConsole-3.0
-- @release $Id: AceConsole-3.0.lua 1284 2022-09-25 09:15:30Z nevcairiel $
local MAJOR,MINOR = "AceConsole-3.0", 7

local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)

if not AceConsole then return end -- No upgrade needed

AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable

-- Lua APIs
local tconcat, tostring, select = table.concat, tostring, select
local type, pairs, error = type, pairs, error
local format, strfind, strsub = string.format, string.find, string.sub
local max = math.max

-- WoW APIs
local _G = _G

local tmp={}
local function Print(self,frame,...)
	local n=0
	if self ~= AceConsole then
		n=n+1
		tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
	end
	for i=1, select("#", ...) do
		n=n+1
		tmp[n] = tostring(select(i, ...))
	end
	frame:AddMessage( tconcat(tmp," ",1,n) )
end

--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
-- @paramsig [chatframe ,] ...
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
-- @param ... List of any values to be printed
function AceConsole:Print(...)
	local frame = ...
	if type(frame) == "table" and frame.AddMessage then	-- Is first argument something with an .AddMessage member?
		return Print(self, frame, select(2,...))
	else
		return Print(self, DEFAULT_CHAT_FRAME, ...)
	end
end


--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
-- @paramsig [chatframe ,] "format"[, ...]
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
-- @param format Format string - same syntax as standard Lua format()
-- @param ... Arguments to the format string
function AceConsole:Printf(...)
	local frame = ...
	if type(frame) == "table" and frame.AddMessage then	-- Is first argument something with an .AddMessage member?
		return Print(self, frame, format(select(2,...)))
	else
		return Print(self, DEFAULT_CHAT_FRAME, format(...))
	end
end




--- Register a simple chat command
-- @param command Chat command to be registered WITHOUT leading "/"
-- @param func Function to call when the slash command is being used (funcref or methodname)
-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
function AceConsole:RegisterChatCommand( command, func, persist )
	if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end

	if persist==nil then persist=true end	-- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk

	local name = "ACECONSOLE_"..command:upper()

	if type( func ) == "string" then
		SlashCmdList[name] = function(input, editBox)
			self[func](self, input, editBox)
		end
	else
		SlashCmdList[name] = func
	end
	_G["SLASH_"..name.."1"] = "/"..command:lower()
	AceConsole.commands[command] = name
	-- non-persisting commands are registered for enabling disabling
	if not persist then
		if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
		AceConsole.weakcommands[self][command] = func
	end
	return true
end

--- Unregister a chatcommand
-- @param command Chat command to be unregistered WITHOUT leading "/"
function AceConsole:UnregisterChatCommand( command )
	local name = AceConsole.commands[command]
	if name then
		SlashCmdList[name] = nil
		_G["SLASH_" .. name .. "1"] = nil
		hash_SlashCmdList["/" .. command:upper()] = nil
		AceConsole.commands[command] = nil
	end
end

--- Get an iterator over all Chat Commands registered with AceConsole
-- @return Iterator (pairs) over all commands
function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end


local function nils(n, ...)
	if n>1 then
		return nil, nils(n-1, ...)
	elseif n==1 then
		return nil, ...
	else
		return ...
	end
end


--- Retreive one or more space-separated arguments from a string.
-- Treats quoted strings and itemlinks as non-spaced.
-- @param str The raw argument string
-- @param numargs How many arguments to get (default 1)
-- @param startpos Where in the string to start scanning (default  1)
-- @return Returns arg1, arg2, ..., nextposition\\
-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
function AceConsole:GetArgs(str, numargs, startpos)
	numargs = numargs or 1
	startpos = max(startpos or 1, 1)

	local pos=startpos

	-- find start of new arg
	pos = strfind(str, "[^ ]", pos)
	if not pos then	-- whoops, end of string
		return nils(numargs, 1e9)
	end

	if numargs<1 then
		return pos
	end

	-- quoted or space separated? find out which pattern to use
	local delim_or_pipe
	local ch = strsub(str, pos, pos)
	if ch=='"' then
		pos = pos + 1
		delim_or_pipe='([|"])'
	elseif ch=="'" then
		pos = pos + 1
		delim_or_pipe="([|'])"
	else
		delim_or_pipe="([| ])"
	end

	startpos = pos

	while true do
		-- find delimiter or hyperlink
		local _
		pos,_,ch = strfind(str, delim_or_pipe, pos)

		if not pos then break end

		if ch=="|" then
			-- some kind of escape

			if strsub(str,pos,pos+1)=="|H" then
				-- It's a |H....|hhyper link!|h
				pos=strfind(str, "|h", pos+2)	-- first |h
				if not pos then break end

				pos=strfind(str, "|h", pos+2)	-- second |h
				if not pos then break end
			elseif strsub(str,pos, pos+1) == "|T" then
				-- It's a |T....|t  texture
				pos=strfind(str, "|t", pos+2)
				if not pos then break end
			end

			pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)

		else
			-- found delimiter, done with this arg
			return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
		end

	end

	-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
	return strsub(str, startpos), nils(numargs-1, 1e9)
end


--- embedding and embed handling

local mixins = {
	"Print",
	"Printf",
	"RegisterChatCommand",
	"UnregisterChatCommand",
	"GetArgs",
}

-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
-- @param target target object to embed AceBucket in
function AceConsole:Embed( target )
	for k, v in pairs( mixins ) do
		target[v] = self[v]
	end
	self.embeds[target] = true
	return target
end

function AceConsole:OnEmbedEnable( target )
	if AceConsole.weakcommands[target] then
		for command, func in pairs( AceConsole.weakcommands[target] ) do
			target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
		end
	end
end

function AceConsole:OnEmbedDisable( target )
	if AceConsole.weakcommands[target] then
		for command, func in pairs( AceConsole.weakcommands[target] ) do
			target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
		end
	end
end

for addon in pairs(AceConsole.embeds) do
	AceConsole:Embed(addon)
end
