vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
Name: AceConsole-2.0
Revision: $Rev: 4621 $
Author(s): ckknight (ckknight@gmail.com)
           cladhaire (cladhaire@gmail.com)
           hyperactiveChipmunk (hyperactiveChipmunk@gmail.com)
Inspired By: AceChatCmd 1.x by Turan (<email here>)
Website: http://www.wowace.com/
Documentation: http://wiki.wowace.com/index.php/AceConsole-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceConsole-2.0
Description: Mixin to allow for input/output capabilities. This uses the
             AceOptions data table format to determine input.
             http://wiki.wowace.com/index.php/AceOptions_data_table
Dependencies: AceLibrary, AceOO-2.0
]]

local MAJOR_VERSION = "AceConsole-2.0"
local MINOR_VERSION = "$Revision: 4621 $"

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end

-- localize --
local MAP_ONOFF = { [false] = "|cffff0000Off|r", [true] = "|cff00ff00On|r" }
local USAGE = "Usage"
local IS_CURRENTLY_SET_TO = "|cffffff7f%s|r is currently set to |cffffff7f[|r%s|cffffff7f]|r"
local IS_NOW_SET_TO = "|cffffff7f%s|r is now set to |cffffff7f[|r%s|cffffff7f]|r"
local IS_NOT_A_VALID_OPTION_FOR = "|cffffff7f%s|r is not a valid option for |cffffff7f%s|r"
local IS_NOT_A_VALID_VALUE_FOR = "|cffffff7f%s|r is not a valid value for |cffffff7f%s|r"
local NO_OPTIONS_AVAILABLE = "No options available"
local OPTION_HANDLER_NOT_FOUND = "Option handler |cffffff7f%q|r not found."
local OPTION_HANDLER_NOT_VALID = "Option handler not valid."
local OPTION_IS_DISABLED = "Option %q is disabled."
-- localize --

local NONE = NONE or "None"

local AceOO = AceLibrary("AceOO-2.0")
local AceEvent, AceHook

local AceConsole = AceOO.Mixin { "Print", "CustomPrint", "RegisterChatCommand" }

local _G = getfenv(0)

local function print(text, name, r, g, b, frame, delay)
        if not text or string.len(text) == 0 then
                text = " "
        end
        if not name or name == AceConsole then
                (frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5)
        else
                (frame or DEFAULT_CHAT_FRAME):AddMessage("|cffffff78" .. tostring(name) .. ":|r " .. text, r, g, b, 1, delay or 5)
        end
end

local tmp
function AceConsole:CustomPrint(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        a1 = tostring(a1)
        if string.find(a1, "%%") then
                print(string.format(a1, tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20)), self, r, g, b, frame or self.printFrame, delay)
        else
                if not tmp then
                        tmp = {}
                end
                table.insert(tmp, a1)
                table.insert(tmp, a2)
                table.insert(tmp, a3)
                table.insert(tmp, a4)
                table.insert(tmp, a5)
                table.insert(tmp, a6)
                table.insert(tmp, a7)
                table.insert(tmp, a8)
                table.insert(tmp, a9)
                table.insert(tmp, a10)
                table.insert(tmp, a11)
                table.insert(tmp, a12)
                table.insert(tmp, a13)
                table.insert(tmp, a14)
                table.insert(tmp, a15)
                table.insert(tmp, a16)
                table.insert(tmp, a17)
                table.insert(tmp, a18)
                table.insert(tmp, a19)
                table.insert(tmp, a20)
                while tmp[table.getn(tmp)] == nil do
                        table.remove(tmp)
                end
                for k = 1, table.getn(tmp) do
                        tmp[k] = tostring(tmp[k])
                end
                print(table.concat(tmp, " "), self, r, g, b, frame or self.printFrame, delay)
                for k,v in tmp do
                        tmp[k] = nil
                end
                table.setn(tmp, 0)
        end
end

function AceConsole:Print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end

local work
local argwork

local function findTableLevel(self, options, chat, text, index, passTable)
        if not index then
                index = 1
                if work then
                        for k,v in pairs(work) do
                                work[k] = nil
                        end
                        table.setn(work, 0)
                        for k,v in pairs(argwork) do
                                argwork[k] = nil
                        end
                        table.setn(argwork, 0)
                else
                        work = {}
                        argwork = {}
                end
                local len = string.len(text)
                local count
                repeat
                        text, count = string.gsub(text, "(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[[^%]]-) (.-%]|h|r)", "%1\001%2")
                until count == 0
                text = string.gsub(text, "(%]|h|r)(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[)", "%1 %2")
                for token in string.gfind(text, "([^%s]+)") do
                        local token = token
                        local num = tonumber(token)
                        if num then
                                token = num
                        else
                                token = string.gsub(token, "\001", " ")
                        end
                        table.insert(work, token)
                end
        end
        
        local path = chat
        for i = 1, index - 1 do
                path = path .. " " .. tostring(work[i])
        end
        
        if type(options.args) == "table" then
                local disabled, hidden = options.disabled, options.hidden
                if hidden then
                        if type(hidden) == "function" then
                                hidden = hidden()
                        elseif type(hidden) == "string" then
                                local handler = options.handler or self
                                if type(handler[hidden]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, hidden)
                                end
                                hidden = handler[hidden](handler)
                        end
                end
                if hidden then
                        disabled = true
                elseif disabled then
                        if type(disabled) == "function" then
                                disabled = disabled()
                        elseif type(disabled) == "string" then
                                local handler = options.handler or self
                                if type(handler[disabled]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, disabled)
                                end
                                disabled = handler[disabled](handler)
                        end
                end
                if not disabled then
                        local next = work[index] and string.lower(work[index])
                        if next then
                                for k,v in options.args do
                                        local good = false
                                        if string.lower(k) == next then
                                                good = true
                                        elseif type(v.aliases) == "table" then
                                                for _,alias in ipairs(v.aliases) do
                                                        if string.lower(alias) == next then
                                                                good = true
                                                                break
                                                        end
                                                end
                                        elseif type(v.aliases) == "string" and string.lower(v.aliases) == next then
                                                good = true
                                        end
                                        if good then
                                                return findTableLevel(options.handler or self, v, chat, text, index + 1, options.pass and options or nil)
                                        end
                                end
                        end
                end
        end
        for i = index, table.getn(work) do
                table.insert(argwork, work[i])
        end
        return options, path, argwork, options.handler or self, passTable, passTable and work[index - 1]
end

local function validateOptionsMethods(self, options, position)
        if type(options) ~= "table" then
                return "Options must be a table.", position
        end
        self = options.handler or self
        if options.type == "execute" then
                if options.func and type(options.func) ~= "string" and type(options.func) ~= "function" then
                        return "func must be a string or function", position
                end
                if options.func and type(options.func) == "string" and type(self[options.func]) ~= "function" then
                        return string.format("%q is not a proper function", options.func), position
                end
        else
                if options.get then
                        if type(options.get) ~= "string" and type(options.get) ~= "function" then
                                return "get must be a string or function", position
                        end
                        if type(options.get) == "string" and type(self[options.get]) ~= "function" then
                                return string.format("%q is not a proper function", options.get), position
                        end
                end
                if options.set then
                        if type(options.set) ~= "string" and type(options.set) ~= "function" then
                                return "set must be a string or function", position
                        end
                        if type(options.set) == "string" and type(self[options.set]) ~= "function" then
                                return string.format("%q is not a proper function", options.set), position
                        end
                end
                if options.validate and type(options.validate) ~= "table" then
                        if type(options.validate) ~= "string" and type(options.validate) ~= "function" then
                                return "validate must be a string or function", position
                        end
                        if type(options.validate) == "string" and type(self[options.validate]) ~= "function" then
                                return string.format("%q is not a proper function", options.validate), position
                        end
                end
        end
        if options.disabled and type(options.disabled) == "string" and type(self[options.disabled]) ~= "function" then
                return string.format("%q is not a proper function", options.disabled), position
        end
        if options.hidden and type(options.hidden) == "string" and type(self[options.hidden]) ~= "function" then
                return string.format("%q is not a proper function", options.hidden), position
        end
        if options.type == "group" and type(options.args) == "table" then
                for k,v in pairs(options.args) do
                        if type(v) == "table" then
                                local newposition
                                if position then
                                        newposition = position .. ".args." .. k
                                else
                                        newposition = "args." .. k
                                end
                                local err, pos = validateOptionsMethods(self, v, newposition)
                                if err then
                                        return err, pos
                                end
                        end
                end
        end
end

local function validateOptions(self, options, position, baseOptions, fromPass)
        if not baseOptions then
                baseOptions = options
        end
        if type(options) ~= "table" then
                return "Options must be a table.", position
        end
        local kind = options.type
        if type(kind) ~= "string" then
                return '"type" must be a string.', position
        elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" then
                return '"type" must either be "range", "text", "group", "toggle", "execute", or "color".', position
        end
        if options.aliases then
                if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
                        return '"alias" must be a table or string', position
                end
        end
        if not fromPass then
                if kind == "execute" then
                        if type(options.func) ~= "string" and type(options.func) ~= "function" then
                                return '"func" must be a string or function', position
                        end
                elseif kind == "range" or kind == "text" or kind == "toggle" then
                        if type(options.set) ~= "string" and type(options.set) ~= "function" then
                                return '"set" must be a string or function', position
                        end
                        if kind == "text" and options.get == false then
                        elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
                                return '"get" must be a string or function', position
                        end
                elseif kind == "group" and options.pass then
                        if options.pass ~= true then
                                return '"pass" must be either nil, true, or false', position
                        end
                        if not options.func then
                                if type(options.set) ~= "string" and type(options.set) ~= "function" then
                                        return '"set" must be a string or function', position
                                end
                                if type(options.get) ~= "string" and type(options.get) ~= "function" then
                                        return '"get" must be a string or function', position
                                end
                        elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
                                return '"func" must be a string or function', position
                        end
                end
        else
                if kind == "group" then
                        return 'cannot have "type" = "group" as a subgroup of a passing group', position
                end
        end
        if options ~= baseOptions then
                if type(options.desc) ~= "string" then
                        return '"desc" must be a string', position
                elseif string.len(options.desc) == 0 then
                        return '"desc" cannot be a 0-length string', position
                end
        end
        if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
                if options.cmdName then
                        if type(options.cmdName) ~= "string" then
                                return '"cmdName" must be a string or nil', position
                        elseif string.len(options.cmdName) == 0 then
                                return '"cmdName" cannot be a 0-length string', position
                        end
                        if type(options.guiName) ~= "string" then
                                return '"guiName" must be a string or nil', position
                        elseif string.len(options.guiName) == 0 then
                                return '"guiName" cannot be a 0-length string', position
                        end
                else
                        if type(options.name) ~= "string" then
                                return '"name" must be a string', position
                        elseif string.len(options.name) == 0 then
                                return '"name" cannot be a 0-length string', position
                        end
                end
        end
        if options.message and type(options.message) ~= "string" then
                return '"message" must be a string or nil', position
        end
        if options.error and type(options.error) ~= "string" then
                return '"error" must be a string or nil', position
        end
        if options.current and type(options.current) ~= "string" then
                return '"current" must be a string or nil', position
        end
        if options.disabled then
                if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
                        return '"disabled" must be a function, string, or boolean', position
                end
        end
        if options.hidden then
                if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
                        return '"hidden" must be a function, string, or boolean', position
                end
        end
        if kind == "text" then
                if type(options.validate) == "table" then
                else
                        if type(options.usage) ~= "string" then
                                return '"usage" must be a string', position
                        elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
                                return '"validate" must be a string or function', position
                        end
                end
        elseif kind == "range" then
                if options.min or options.max then
                        if type(options.min) ~= "number" then
                                return '"min" must be a number', position
                        elseif type(options.max) ~= "number" then
                                return '"max" must be a number', position
                        elseif options.min >= options.max then
                                return '"min" must be less than "max"', position
                        end
                end
                if options.step then
                        if type(options.step) ~= "number" then
                                return '"step" must be a number', position
                        elseif options.step < 0 then
                                return '"step" must be nonnegative', position
                        end
                end
                if options.isPercent and options.isPercent ~= true then
                        return '"isPercent" must either be nil, true, or false', position
                end
        elseif kind == "toggle" then
                if options.map then
                        if type(options.map) ~= "table" then
                                return '"map" must be a table', position
                        elseif type(options.map[true]) ~= "string" then
                                return '"map[true]" must be a string', position
                        elseif type(options.map[false]) ~= "string" then
                                return '"map[false]" must be a string', position
                        end
                end
        elseif kind == "color" then
                if options.hasAlpha and options.hasAlpha ~= true then
                        return '"hasAlpha" must be nil, true, or false', position
                end
        elseif kind == "group" then
                if options.pass and options.pass ~= true then
                        return '"pass" must be nil, true, or false', position
                end
                if type(options.args) ~= "table" then
                        return '"args" must be a table', position
                end
                for k,v in pairs(options.args) do
                        if type(k) ~= "string" then
                                return '"args" keys must be strings', position
                        end
                        if not string.find(k, "^%w+$") then
                                return string.format('"args" keys must be standard strings. %q is not appropriate.', k), position
                        end
                        if type(v) ~= "table" then
                                return '"args" values must be tables', position and position .. "." .. k or k
                        end
                        local newposition
                        if position then
                                newposition = position .. ".args." .. k
                        else
                                newposition = "args." .. k
                        end
                        local err, pos = validateOptions(self, v, newposition, baseOptions, options.pass)
                        if err then
                                return err, pos
                        end
                end
        end
end

local colorTable
local colorFunc
local colorCancelFunc

local order

local function printUsage(self, handler, realOptions, options, path, args, quiet, filter)
        if filter then
                filter = "^" .. string.gsub(filter, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
        end
        local hidden, disabled = options.hidden, options.disabled
        if hidden then
                if type(hidden) == "function" then
                        hidden = hidden()
                elseif type(hidden) == "string" then
                        if type(handler[handler]) ~= "function" then
                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, handler)
                        end
                        hidden = handler[hidden](handler)
                end
        end
        if hidden then
                disabled = true
        elseif disabled then
                if type(disabled) == "function" then
                        disabled = disabled()
                elseif type(disabled) == "string" then
                        if type(handler[disabled]) ~= "function" then
                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, disabled)
                        end
                        disabled = handler[disabled](handler)
                end
        end
        local kind = string.lower(options.type or "group")
        if disabled then
                print(string.format(OPTION_IS_DISABLED, path), realOptions.cmdName or realOptions.name or self)
        elseif kind == "text" then
                local var
                if passTable then
                        if not passTable.get then
                        elseif type(passTable.get) == "function" then
                                var = passTable.get(passValue)
                        else
                                if type(handler[passTable.get]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.get)
                                end
                                var = handler[passTable.get](handler, passValue)
                        end
                else
                        if not options.get then
                        elseif type(options.get) == "function" then
                                var = options.get()
                        else
                                if type(handler[options.get]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.get)
                                end
                                var = handler[options.get](handler)
                        end
                end
                
                local usage
                if type(options.validate) == "table" then
                        if filter then
                                if not order then
                                        order = {}
                                end
                                for _,v in ipairs(options.validate) do
                                        if string.find(k, filter) then
                                                table.insert(order, k)
                                        end
                                end
                                usage = "{" .. table.concat(order, " || ") .. "}"
                                for k in pairs(order) do
                                        order[k] = nil
                                end
                                table.setn(order, 0)
                        else
                                usage = "{" .. table.concat(options.validate, " || ") .. "}"
                        end
                else
                        usage = options.usage or "<value>"
                end
                if not quiet then
                        print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
                end
                if (passTable and passTable.get) or options.get then
                        print(string.format(options.current or IS_CURRENTLY_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE)))
                end
        elseif kind == "range" then
                local var
                if passTable then
                        if type(passTable.get) == "function" then
                                var = passTable.get(passValue)
                        else
                                if type(handler[passTable.get]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.get)
                                end
                                var = handler[passTable.get](handler, passValue)
                        end
                else
                        if type(options.get) == "function" then
                                var = options.get()
                        else
                                local handler = options.handler or self
                                if type(handler[options.get]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.get)
                                end
                                var = handler[options.get](handler)
                        end
                end
                
                local usage
                local min = options.min or 0
                local max = options.max or 1
                if options.isPercent then
                        min, max = min * 100, max * 100
                        var = tostring(var * 100) .. "%"
                end
                local bit = "-"
                if min < 0 or max < 0 then
                        bit = " - "
                end
                usage = string.format("(%s%s%s)", min, bit, max)
                if not quiet then
                        print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
                end
                print(string.format(options.current or IS_CURRENTLY_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE)))
        elseif kind == "group" then
                local usage
                if next(options.args) then
                        if not order then
                                order = {}
                        end
                        for k,v in pairs(options.args) do
                                if filter then
                                        if string.find(k, filter) then
                                                table.insert(order, k)
                                        elseif type(v.aliases) == "table" then
                                                for _,bit in ipairs(v.aliases) do
                                                        if string.find(v.aliases, filter) then
                                                                table.insert(order, k)
                                                                break
                                                        end
                                                end
                                        elseif type(v.aliases) == "string" then
                                                if string.find(v.aliases, filter) then
                                                        table.insert(order, k)
                                                end
                                        end
                                else
                                        table.insert(order, k)
                                end
                        end
                        table.sort(order)
                        if not quiet then
                                if options == realOptions then
                                        if options.desc then
                                                print(tostring(options.desc), realOptions.cmdName or realOptions.name or self)
                                                print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"))
                                        elseif self.description or self.notes then
                                                print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self)
                                                print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"))
                                        else
                                                print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
                                        end
                                else
                                        if options.desc then
                                                print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
                                                print(tostring(options.desc))
                                        else
                                                print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
                                        end
                                end
                        end
                        for _,k in ipairs(order) do
                                local v = options.args[k]
                                local hidden, disabled = v.hidden, v.disabled
                                if hidden then
                                        if type(hidden) == "function" then
                                                hidden = hidden()
                                        elseif type(hidden) == "string" then
                                                local handler = v.handler or self
                                                if type(handler[hidden]) ~= "function" then
                                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, hidden)
                                                end
                                                hidden = handler[hidden](handler)
                                        end
                                end
                                if hidden then
                                        disabled = true
                                elseif disabled then
                                        if type(disabled) == "function" then
                                                disabled = disabled()
                                        elseif type(disabled) == "string" then
                                                local handler = v.handler or self
                                                if type(handler[disabled]) ~= "function" then
                                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, disabled)
                                                end
                                                disabled = handler[disabled](handler)
                                        end
                                end
                                if type(v.aliases) == "table" then
                                        k = k .. " || " .. table.concat(v.aliases, " || ")
                                elseif type(v.aliases) == "string" then
                                        k = k .. " || " .. v.aliases
                                end
                                if v.get and v.type ~= "group" and v.type ~= "pass" and v.type ~= "execute" then
                                        local a1,a2,a3,a4
                                        if type(v.get) == "function" then
                                                if options.pass then
                                                        a1,a2,a3,a4 = v.get(k)
                                                else
                                                        a1,a2,a3,a4 = v.get()
                                                end
                                        else
                                                local handler = v.handler or self
                                                if type(handler[v.get]) ~= "function" then
                                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, v.get)
                                                end
                                                if options.pass then
                                                        a1,a2,a3,a4 = handler[v.get](handler, k)
                                                else
                                                        a1,a2,a3,a4 = handler[v.get](handler)
                                                end
                                        end
                                        if v.type == "color" then
                                                if v.hasAlpha then
                                                        if not a1 or not a2 or not a3 or not a4 then
                                                                s = NONE
                                                        else
                                                                s = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a4*255, a1*255, a2*255, a3*255, a4*255, a1*255, a2*255, a3*255)
                                                        end
                                                else
                                                        if not a1 or not a2 or not a3 then
                                                                s = NONE
                                                        else
                                                                s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", a1*255, a2*255, a3*255, a1*255, a2*255, a3*255)
                                                        end
                                                end
                                        elseif v.type == "toggle" then
                                                if v.map then
                                                        s = tostring(v.map[a1 or false] or NONE)
                                                else
                                                        s = tostring(MAP_ONOFF[a1 or false] or NONE)
                                                end
                                        elseif v.type == "range" then
                                                if v.isPercent then
                                                        s = tostring(a1 * 100) .. "%"
                                                else
                                                        s = tostring(a1)
                                                end
                                        else
                                                s = tostring(a1 or NONE)
                                        end
                                        if not hidden then
                                                if disabled then
                                                        local s = string.gsub(s, "|cff%x%x%x%x%x%x(.-)|r", "%1")
                                                        local desc = string.gsub(v.desc or NONE, "|cff%x%x%x%x%x%x(.-)|r", "%1")
                                                        print(string.format("|cffcfcfcf - %s: [%s]|r %s", k, s, desc))
                                                else
                                                        print(string.format(" - |cffffff7f%s: [|r%s|cffffff7f]|r %s", k, s, v.desc or NONE))
                                                end
                                        end
                                else
                                        if not hidden then
                                                if disabled then
                                                        local desc = string.gsub(v.desc or NONE, "|cff%x%x%x%x%x%x(.-)|r", "%1")
                                                        print(string.format("|cffcfcfcf - %s: %s", k, desc))
                                                else
                                                        print(string.format(" - |cffffff7f%s:|r %s", k, v.desc or NONE))
                                                end
                                        end
                                end
                        end
                        for k in pairs(order) do
                                order[k] = nil
                        end
                        table.setn(order, 0)
                else
                        if options.desc then
                                desc = options.desc
                                print(string.format("|cffffff7f%s:|r %s", USAGE, path), realOptions.cmdName or realOptions.name or self)
                                print(tostring(options.desc))
                        elseif options == realOptions and (self.description or self.notes) then
                                print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self)
                                print(string.format("|cffffff7f%s:|r %s", USAGE, path))
                        else
                                print(string.format("|cffffff7f%s:|r %s", USAGE, path), realOptions.cmdName or realOptions.name or self)
                        end
                        print(self, NO_OPTIONS_AVAILABLE)
                end
        end
end

local function handlerFunc(self, chat, msg, options)
        if not msg then
                msg = ""
        else
                msg = string.gsub(msg, "^%s*(.-)%s*$", "%1")
                msg = string.gsub(msg, "%s+", " ")
        end
        
        local realOptions = options
        local options, path, args, handler, passTable, passValue = findTableLevel(self, options, chat, msg)
        
        local hidden, disabled = options.hidden, options.disabled
        if hidden then
                if type(hidden) == "function" then
                        hidden = hidden()
                elseif type(hidden) == "string" then
                        if type(handler[hidden]) ~= "function" then
                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, hidden)
                        end
                        hidden = handler[hidden](handler)
                end
        end
        if hidden then
                disabled = true
        elseif disabled then
                if type(disabled) == "function" then
                        disabled = disabled()
                elseif type(disabled) == "string" then
                        if type(handler[disabled]) ~= "function" then
                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, disabled)
                        end
                        disabled = handler[disabled](handler)
                end
        end
        local kind = string.lower(options.type or "group")
        if disabled then
                print(string.format(OPTION_IS_DISABLED, path), realOptions.cmdName or realOptions.name or self)
        elseif kind == "text" then
                if table.getn(args) > 0 then
                        if (type(options.validate) == "table" and table.getn(args) > 1) or (type(options.validate) ~= "table" and not options.input) then
                                local arg = table.concat(args, " ")
                                for k,v in pairs(args) do
                                        args[k] = nil
                                end
                                table.setn(args, 0)
                                table.insert(args, arg)
                        end
                        if options.validate then
                                local good
                                if type(options.validate) == "function" then
                                        good = options.validate(unpack(args))
                                elseif type(options.validate) == "table" then
                                        local arg = args[1]
                                        arg = type(arg) == "string" and string.lower(arg) or arg
                                        for _,v in ipairs(options.validate) do
                                                if type(arg) == "string" then
                                                        if string.lower(v) == arg then
                                                                args[1] = v
                                                                good = true
                                                                break
                                                        end
                                                elseif v == arg then
                                                        good = true
                                                        break
                                                end
                                        end
                                else
                                        if type(handler[options.validate]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.validate)
                                        end
                                        good = handler[options.validate](handler, unpack(args))
                                end
                                if not good then
                                        local usage
                                        if type(options.validate) == "table" then
                                                usage = "{" .. table.concat(options.validate, " || ") .. "}"
                                        else
                                                usage = options.usage or "<value>"
                                        end
                                        print(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, tostring(table.concat(args, " ")), path), realOptions.cmdName or realOptions.name or self)
                                        print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage))
                                        return
                                end
                        end
                        
                        if passTable then
                                if type(passTable.set) == "function" then
                                        passTable.set(passValue, unpack(args))
                                else
                                        if type(handler[passTable.set]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.set)
                                        end
                                        handler[passTable.set](handler, passTable.set, unpack(args))
                                end
                        else
                                if type(options.set) == "function" then
                                        options.set(unpack(args))
                                else
                                        if type(handler[options.set]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.set)
                                        end
                                        handler[options.set](handler, unpack(args))
                                end
                        end
                end
                
                if table.getn(args) > 0 then
                        local var
                        if passTable then
                                if not passTable.get then
                                elseif type(passTable.get) == "function" then
                                        var = passTable.get(passValue)
                                else
                                        if type(handler[passTable.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.get)
                                        end
                                        var = handler[passTable.get](handler, passValue)
                                end
                        else
                                if not options.get then
                                elseif type(options.get) == "function" then
                                        var = options.get()
                                else
                                        if type(handler[options.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.get)
                                        end
                                        var = handler[options.get](handler)
                                end
                        end
                        if (passTable and passTable.get) or options.get then
                                print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self)
                        end
                else
                        printUsage(self, handler, realOptions, options, path, args)
                end
        elseif kind == "execute" then
                if passTable then
                        if type(passFunc) == "function" then
                                set(passValue)
                        else
                                if type(handler[passFunc]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, passFunc)
                                end
                                handler[passFunc](handler, passValue)
                        end
                else
                        if type(options.func) == "function" then
                                options.func()
                        else
                                local handler = options.handler or self
                                if type(handler[options.func]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.func)
                                end
                                handler[options.func](handler)
                        end
                end
        elseif kind == "toggle" then
                local var
                if passTable then
                        if type(passTable.get) == "function" then
                                var = passTable.get(passValue)
                        else
                                if type(handler[passTable.get]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.get)
                                end
                                var = handler[passTable.get](handler, passValue)
                        end
                        if type(passTable.set) == "function" then
                                passTable.set(passValue, not var)
                        else
                                if type(handler[passTable.set]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.set)
                                end
                                handler[passTable.set](handler, passValue, not var)
                        end
                        if type(passTable.get) == "function" then
                                var = passTable.get(passValue)
                        else
                                var = handler[passTable.get](handler, passValue)
                        end
                else
                        local handler = options.handler or self
                        if type(options.get) == "function" then
                                var = options.get()
                        else
                                if type(handler[options.get]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.get)
                                end
                                var = handler[options.get](handler)
                        end
                        if type(options.set) == "function" then
                                options.set(not var)
                        else
                                if type(handler[options.set]) ~= "function" then
                                        AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.set)
                                end
                                handler[options.set](handler, not var)
                        end
                        if type(options.get) == "function" then
                                var = options.get()
                        else
                                var = handler[options.get](handler)
                        end
                end
                
                print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), (options.map or MAP_ONOFF)[var or false] or NONE), realOptions.cmdName or realOptions.name or self)
        elseif kind == "range" then
                local arg
                if table.getn(args) <= 1 then
                        arg = args[1]
                else
                        arg = table.concat(args, " ")
                end

                if arg then
                        local min = options.min or 0
                        local max = options.max or 1
                        local good = false
                        if type(arg) == "number" then
                                if options.isPercent then
                                        arg = arg / 100
                                end

                                if arg >= min and arg <= max then
                                        good = true
                                end
                                
                                if type(options.step) == "number" and step > 0 then
                                        local step = options.step
                                        arg = math.floor((x - min) / step + 0.5) * step + min
                                        if arg > max then
                                                arg = max
                                        elseif arg < min then
                                                arg = min
                                        end
                                end
                        end
                        if not good then
                                local usage
                                local min = options.min or 0
                                local max = options.max or 1
                                if options.isPercent then
                                        min, max = min * 100, max * 100
                                end
                                local bit = "-"
                                if min < 0 or max < 0 then
                                        bit = " - "
                                end
                                usage = string.format("(%s%s%s)", min, bit, max)
                                print(string.format(options.error or IS_NOT_A_VALID_VALUE_FOR, tostring(arg), path), realOptions.cmdName or realOptions.name or self)
                                print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage))
                                return
                        end
                        
                        if passTable then
                                if type(passTable.set) == "function" then
                                        passTable.set(passValue, arg)
                                else
                                        if type(handler[passTable.set]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.set)
                                        end
                                        handler[passTable.set](handler, passValue, arg)
                                end
                        else
                                if type(options.set) == "function" then
                                        options.set(arg)
                                else
                                        local handler = options.handler or self
                                        if type(handler[options.set]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.set)
                                        end
                                        handler[options.set](handler, arg)
                                end
                        end
                end
                
                if arg then
                        local var
                        if passTable then
                                if type(passTable.get) == "function" then
                                        var = passTable.get(passValue)
                                else
                                        if type(handler[passTable.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.get)
                                        end
                                        var = handler[passTable.get](handler, passValue)
                                end
                        else
                                if type(options.get) == "function" then
                                        var = options.get()
                                else
                                        local handler = options.handler or self
                                        if type(handler[options.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.get)
                                        end
                                        var = handler[options.get](handler)
                                end
                        end
                        
                        if var and options.isPercent then
                                var = tostring(var * 100) .. "%"
                        end
                        print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self)
                else
                        printUsage(self, handler, realOptions, options, path, args)
                end
        elseif kind == "color" then
                if table.getn(args) > 0 then
                        local r,g,b,a
                        if table.getn(args) == 1 then
                                local arg = tostring(args[1])
                                if options.hasAlpha then
                                        if string.len(arg) == 8 and string.find(arg, "^%x*$")  then
                                                r,g,b,a = tonumber(string.sub(arg, 1, 2), 16) / 255, tonumber(string.sub(arg, 3, 4), 16) / 255, tonumber(string.sub(arg, 5, 6), 16) / 255, tonumber(string.sub(arg, 7, 8), 16) / 255
                                        end
                                else
                                        if string.len(arg) == 6 and string.find(arg, "^%x*$") then
                                                r,g,b = tonumber(string.sub(arg, 1, 2), 16) / 255, tonumber(string.sub(arg, 3, 4), 16) / 255, tonumber(string.sub(arg, 5, 6), 16) / 255
                                        end
                                end
                        elseif table.getn(args) == 4 and options.hasAlpha then
                                local a1,a2,a3,a4 = args[1], args[2], args[3], args[4]
                                if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and type(a4) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 and a4 <= 1 then
                                        r,g,b,a = a1,a2,a3,a4
                                elseif (type(a1) == "number" or string.len(a1) == 2) and string.find(a1, "^%x*$") and (type(a2) == "number" or string.len(a2) == 2) and string.find(a2, "^%x*$") and (type(a3) == "number" or string.len(a3) == 2) and string.find(a3, "^%x*$") and (type(a4) == "number" or string.len(a4) == 2) and string.find(a4, "^%x*$") then
                                        r,g,b,a = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255, tonumber(a4, 16) / 255
                                end
                        elseif table.getn(args) == 3 and not options.hasAlpha then
                                local a1,a2,a3 = args[1], args[2], args[3]
                                if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 then
                                        r,g,b = a1,a2,a3
                                elseif (type(a1) == "number" or string.len(a1) == 2) and string.find(a1, "^%x*$") and (type(a2) == "number" or string.len(a2) == 2) and string.find(a2, "^%x*$") and (type(a3) == "number" or string.len(a3) == 2) and string.find(a3, "^%x*$") then
                                        r,g,b = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255
                                end
                        end
                        if not r then
                                print(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, table.concat(args, ' '), path), realOptions.cmdName or realOptions.name or self)
                                print(string.format("|cffffff7f%s:|r %s {0-1} {0-1} {0-1}%s", USAGE, path, options.hasAlpha and " {0-1}" or ""))
                                return
                        end
                        if passTable then
                                if type(passTable.set) == "function" then
                                        passTable.set(passValue, r,g,b,a)
                                else
                                        if type(handler[passTable.set]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.set)
                                        end
                                        handler[passTable.set](handler, passValue, r,g,b,a)
                                end
                        else
                                if type(options.set) == "function" then
                                        options.set(r,g,b,a)
                                else
                                        if type(handler[options.set]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.set)
                                        end
                                        handler[options.set](handler, r,g,b,a)
                                end
                        end
                        
                        local r,g,b,a
                        if passTable then
                                if type(passTable.get) == "function" then
                                        r,g,b,a = passTable.get(passValue)
                                else
                                        if type(handler[passTable.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.get)
                                        end
                                        r,g,b,a = handler[passTable.get](handler, passValue)
                                end
                        else
                                if type(options.get) == "function" then
                                        r,g,b,a = options.get()
                                else
                                        if type(handler[options.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.get)
                                        end
                                        r,g,b,a = handler[options.get](handler)
                                end
                        end
                        
                        local s
                        if type(r) == "number" and type(g) == "number" and type(b) == "number" then
                                if options.hasAlpha and type(a) == "number" then
                                        s = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
                                else
                                        s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", r*255, g*255, b*255, r*255, g*255, b*255)
                                end
                        else
                                s = NONE
                        end
                        print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), s), realOptions.cmdName or realOptions.name or self)
                else
                        local r,g,b,a
                        if passTable then
                                if type(passTable.get) == "function" then
                                        r,g,b,a = passTable.get(passValue)
                                else
                                        if type(handler[passTable.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, passTable.get)
                                        end
                                        r,g,b,a = handler[passTable.get](handler, passValue)
                                end
                        else
                                if type(options.get) == "function" then
                                        r,g,b,a = options.get()
                                else
                                        if type(handler[options.get]) ~= "function" then
                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, options.get)
                                        end
                                        r,g,b,a = handler[options.get](handler)
                                end
                        end
                        
                        if not colorTable then
                                colorTable = {}
                                local t = colorTable
                                
                                if ColorPickerOkayButton then
                                        local ColorPickerOkayButton_OnClick = ColorPickerOkayButton:GetScript("OnClick")
                                        ColorPickerOkayButton:SetScript("OnClick", function()
                                                if ColorPickerOkayButton_OnClick then
                                                        ColorPickerOkayButton_OnClick()
                                                end
                                                if t.active then
                                                        ColorPickerFrame.cancelFunc = nil
                                                        ColorPickerFrame.func = nil
                                                        ColorPickerFrame.opacityFunc = nil
                                                        local r,g,b,a
                                                        if t.passValue then
                                                                if type(t.get) == "function" then
                                                                        r,g,b,a = t.get(t.passValue)
                                                                else
                                                                        if type(t.handler[t.get]) ~= "function" then
                                                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, t.get)
                                                                        end
                                                                        r,g,b,a = t.handler[t.get](t.handler, t.passValue)
                                                                end
                                                        else
                                                                if type(t.get) == "function" then
                                                                        r,g,b,a = t.get()
                                                                else
                                                                        if type(t.handler[t.get]) ~= "function" then
                                                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, t.get)
                                                                        end
                                                                        r,g,b,a = t.handler[t.get](t.handler)
                                                                end
                                                        end
                                                        if r ~= t.r or g ~= t.g or b ~= t.b or (t.hasAlpha and a ~= t.a) then
                                                                local s
                                                                if type(r) == "number" and type(g) == "number" and type(b) == "number" then
                                                                        if t.hasAlpha and type(a) == "number" then
                                                                                s = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
                                                                        else
                                                                                s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", r*255, g*255, b*255, r*255, g*255, b*255)
                                                                        end
                                                                else
                                                                        s = NONE
                                                                end
                                                                print(string.format(t.message, tostring(t.name), s), t.realOptions.cmdName or t.realOptions.name or self)
                                                        end
                                                        for k,v in pairs(t) do
                                                                t[k] = nil
                                                        end
                                                end
                                        end)
                                end
                        else
                                for k,v in pairs(colorTable) do
                                        colorTable[k] = nil
                                end
                        end
                        
                        if type(r) ~= "number" or type(g) ~= "number" or type(b) ~= "number" then
                                r,g,b = 1, 1, 1
                        end
                        if type(a) ~= "number" then
                                a = 1
                        end
                        local t = colorTable
                        t.r = r
                        t.g = g
                        t.b = b
                        if hasAlpha then
                                t.a = a
                        end
                        t.realOptions = realOptions
                        t.hasAlpha = options.hasAlpha
                        t.handler = handler
                        t.set = passTable and passTable.set or options.set
                        t.get = passTable and passTable.get or options.get
                        t.name = options.cmdName or options.name
                        t.message = options.message or IS_NOW_SET_TO
                        t.passValue = passValue
                        t.active = true
                        
                        if not colorFunc then
                                colorFunc = function()
                                        local r,g,b = ColorPickerFrame:GetColorRGB()
                                        if t.hasAlpha then
                                                local a = 1 - OpacitySliderFrame:GetValue()
                                                if type(t.set) == "function" then
                                                        if t.passValue then
                                                                t.set(t.passValue, r,g,b,a)
                                                        else
                                                                t.set(r,g,b,a)
                                                        end
                                                else
                                                        if type(t.handler[t.set]) ~= "function" then
                                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, t.set)
                                                        end
                                                        if t.passValue then
                                                                t.handler[t.set](t.handler, t.passValue, r,g,b,a)
                                                        else
                                                                t.handler[t.set](t.handler, r,g,b,a)
                                                        end
                                                end
                                        else
                                                if type(t.set) == "function" then
                                                        if t.passValue then
                                                                t.set(t.passValue, r,g,b)
                                                        else
                                                                t.set(r,g,b)
                                                        end
                                                else
                                                        if type(t.handler[t.set]) ~= "function" then
                                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, t.set)
                                                        end
                                                        if t.passValue then
                                                                t.handler[t.set](t.handler, t.passValue, r,g,b)
                                                        else
                                                                t.handler[t.set](t.handler, r,g,b)
                                                        end
                                                end
                                        end
                                end
                        end
                        
                        ColorPickerFrame.func = colorFunc
                        ColorPickerFrame.hasOpacity = options.hasAlpha
                        if options.hasAlpha then
                                ColorPickerFrame.opacityFunc = ColorPickerFrame.func
                                ColorPickerFrame.opacity = 1 - a
                        end
                        ColorPickerFrame:SetColorRGB(r,g,b)
                        
                        if not colorCancelFunc then
                                colorCancelFunc = function()
                                        if t.hasAlpha then
                                                if type(t.set) == "function" then
                                                        if t.passValue then
                                                                t.set(t.passValue, t.r,t.g,t.b,t.a)
                                                        else
                                                                t.set(t.r,t.g,t.b,t.a)
                                                        end
                                                else
                                                        if type(t.handler[t.get]) ~= "function" then
                                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, t.get)
                                                        end
                                                        if t.passValue then
                                                                t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b,t.a)
                                                        else
                                                                t.handler[t.set](t.handler, t.r,t.g,t.b,t.a)
                                                        end
                                                end
                                        else
                                                if type(t.set) == "function" then
                                                        if t.passValue then
                                                                t.set(t.passValue, t.r,t.g,t.b)
                                                        else
                                                                t.set(t.r,t.g,t.b)
                                                        end
                                                else
                                                        if type(t.handler[t.set]) ~= "function" then
                                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, t.set)
                                                        end
                                                        if t.passValue then
                                                                t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b)
                                                        else
                                                                t.handler[t.set](t.handler, t.r,t.g,t.b)
                                                        end
                                                end
                                        end
                                        for k,v in pairs(t) do
                                                t[k] = nil
                                        end
                                        ColorPickerFrame.cancelFunc = nil
                                        ColorPickerFrame.func = nil
                                        ColorPickerFrame.opacityFunc = nil
                                end
                        end
                        
                        ColorPickerFrame.cancelFunc = colorCancelFunc
                        
                        ShowUIPanel(ColorPickerFrame)
                end
        elseif kind == "group" then
                if table.getn(args) == 0 then
                        printUsage(self, handler, realOptions, options, path, args)
                else
                        -- invalid argument
                        print(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, args[1], path), realOptions.cmdName or realOptions.name or self)
                end
        end
end

local external
function AceConsole:RegisterChatCommand(slashCommands, options, name)
        if type(slashCommands) ~= "table" and slashCommands ~= false then
                error(string.format("Bad argument #2 to `RegisterInputCommand' (expected table, got %s)", tostring(type(slashCommands))), 2)
        end
        if not slashCommands and type(name) ~= "string" then
                error(string.format("Bad argument #4 to `RegisterInputCommand' (expected string, got %s)", tostring(type(name))), 2)
        end
        if type(options) ~= "table" and type(options) ~= "function" and options ~= nil then
                error(string.format("Bad argument #3 to `RegisterInputCommand' (expected table, function, or nil, got %s)", tostring(type(options))), 2)
        end
        if name then
                if type(name) ~= "string" then
                        error(string.format("Bad argument #4 to `RegisterInputCommand' (expected string or nil, got %s)", tostring(type(name))), 2)
                elseif not string.find(name, "^%w+$") or string.upper(name) ~= name or string.len(name) == 0 then
                        error("Argument #4 must be an uppercase, letters-only string with at least 1 character", 2)
                end
        end
        if slashCommands then
                if table.getn(slashCommands) == 0 then
                        error("Argument #2 must include at least one string")
                end
                
                for k,v in pairs(slashCommands) do
                        if type(k) ~= "number" then
                                error("All keys in argument #2 must be numbers", 2)
                        end
                        if type(v) ~= "string" then
                                error("All values in argument #2 must be strings", 2)
                        elseif not string.find(v, "^/[A-Za-z][A-Za-z0-9_]*$") then
                                error("All values in argument #2 must be in the form of \"/word\"", 2)
                        end
                end
        end
        
        if not options then
                options = {
                        type = 'group',
                        args = {},
                        handler = self
                }
        end
        
        if type(options) == "table" then
                local err, position = validateOptions(self, options)
                if err then
                        if position then
                                error(position .. ": " .. err, 2)
                        else
                                error(err, 2)
                        end
                end
                
                if not options.handler then
                        options.handler = self
                end
                
                if options.handler == self and string.lower(options.type) == "group" then
                        local class = self.class
                        while class and class ~= AceOO.Class do
                                if type(class.GetAceOptionsDataTable) == "function" then
                                        local t = class:GetAceOptionsDataTable()
                                        for k,v in pairs(t) do
                                                if type(options.args) ~= "table" then
                                                        options.args = {}
                                                end
                                                if options.args[k] == nil then
                                                        options.args[k] = v
                                                end
                                        end
                                end
                                local mixins = class.mixins
                                if mixins then
                                        for mixin in pairs(mixins) do
                                                if type(mixin.GetAceOptionsDataTable) == "function" then
                                                        local t = mixin:GetAceOptionsDataTable()
                                                        for k,v in pairs(t) do
                                                                if type(options.args) ~= "table" then
                                                                        options.args = {}
                                                                end
                                                                if options.args[k] == nil then
                                                                        options.args[k] = v
                                                                end
                                                        end
                                                end
                                        end
                                end
                                class = class.super
                        end
                end
        end
        
        local chat
        if slashCommands then
                chat = slashCommands[1]
        else
                chat = _G["SLASH_"..name..1]
        end
        
        local handler
        if type(options) == "function" then
                handler = options
        else
                function handler(msg)
                        handlerFunc(self, chat, msg, options)
                end
        end
        
        if not _G.SlashCmdList then
                _G.SlashCmdList = {}
        end
        
        if not name then
                repeat
                        name = string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1)
                until not _G.SlashCmdList[name]
        end
        
        if slashCommands then
                if _G.SlashCmdList[name] then
                        local i = 0
                        while true do
                                i = i + 1
                                if _G["SLASH_"..name..i] then
                                        _G["SLASH_"..name..i] = nil
                                else
                                        break
                                end
                        end
                end
                
                local i = 0
                for _,command in ipairs(slashCommands) do
                        i = i + 1
                        _G["SLASH_"..name..i] = command
                        if string.lower(command) ~= command then
                                i = i + 1
                                _G["SLASH_"..name..i] = string.lower(command)
                        end
                end
        end
        _G.SlashCmdList[name] = handler
        if self ~= AceConsole and self.slashCommand == nil then
                self.slashCommand = chat
        end
        
        if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then
                external(AceConsole, "AceEvent-2.0", AceLibrary("AceEvent-2.0"))
        end
        if AceEvent then
                if not AceConsole.nextAddon then
                        AceConsole.nextAddon = {}
                end
                AceConsole.nextAddon[self] = options
                if not self.playerLogin then
                        AceConsole:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true)
                end
        end
        
        if not AceHook and AceLibrary:HasInstance("AceHook-2.0") then
                external(AceConsole, "AceHook-2.0", AceLibrary("AceHook-2.0"))
        end

        if AceHook then
                if not self.hooks then
                        AceConsole:HookScript(ChatFrameEditBox, "OnTabPressed")
                        AceConsole:Hook("ChatEdit_OnLoad")
                end
        end
                
        AceConsole.registry[name] = options
end

function AceConsole:PLAYER_LOGIN()
        self.playerLogin = true
        for addon, options in pairs(self.nextAddon) do
                local err, position = validateOptionsMethods(addon, options)
                if err then
                        if position then
                                error(tostring(addon) .. ": AceConsole: " .. position .. ": " .. err)
                        else
                                error(tostring(addon) .. ": AceConsole: " .. err)
                        end
                end
                self.nextAddon[addon] = nil
        end
        
        self:RegisterChatCommand({ "/reload", "/rl", "/reloadui" }, ReloadUI, "RELOAD")
        local tmp
        self:RegisterChatCommand({ "/print" }, function(text)
                RunScript("local function func(...) for k,v in ipairs(arg) do arg[k] = tostring(v) end DEFAULT_CHAT_FRAME:AddMessage(table.concat(arg, ' ')) end func(" .. text .. ")")
        end, "PRINT")
end

function AceConsole:ChatEdit_OnLoad()
        AceConsole:HookScript(this, "OnTabPressed")
end

local function LCS(strings) --returns Least Common Substring
        local len = 0
        local numStrings = table.getn(strings)
        
        for _, s in strings do
                len = string.len(s) > len and string.len(s) or len
        end
        
        for i = 1, len do
                local c = string.sub(strings[1], i, i)
                for j = 2, numStrings do
                        if string.sub(strings[j], i, i) ~= c then
                                return string.sub(strings[1], 0, i-1)
                        end
                end
        end
        return strings[1]
end

function AceConsole:OnTabPressed()
        --Get the position of the cursor
        local ost = this:GetScript("OnTextSet")
        if ost then this:SetScript("OnTextSet", nil) end
        this:Insert("\1")
        local pos = string.find(this:GetText(), "\1", 1) - 1
        this:HighlightText(pos, pos+1)
        this:Insert("\0")
        if ost then this:SetScript("OnTextSet", ost) end
        
        local text = this:GetText()
        
        local _, _, cmd  = string.find(text, "^([%S]+)")
        if not cmd then return self.hooks[this].OnTabPressed.orig() end
        
        local left = string.find(string.sub(text, 0, pos), "[%S]+$") or string.len(cmd)
        
        local _, _, word = string.find(string.sub(text, left, pos), "^([%S]+)")
        
        local realOptions, validArgs, path, argwork
        if string.sub(cmd, 1, 1) == "/" then
                if left == 1 and word == cmd then
                        return self.hooks[this].OnTabPressed.orig()
                else
                        for name in pairs(SlashCmdList) do --global
                                if AceConsole.registry[name] then
                                        local i = 0
                                        local scmd
                                        while true do
                                                i = i + 1
                                                scmd = _G["SLASH_"..name..i]
                                                if not scmd then break end
                                                if cmd == scmd then
                                                        realOptions = AceConsole.registry[name]
                                                        validArgs, path, argwork = findTableLevel(self, AceConsole.registry[name], cmd, string.sub(this:GetText(), string.len(cmd)+2, pos))
                                                end
                                        end
                                end
                        end
                end
        end
        
        if not validArgs then return self.hooks[this].OnTabPressed.orig() end
        
        if not validArgs.args then
                printUsage(self, validArgs.handler, realOptions, validArgs, path, argwork)
        else
                local matches = {}
                for arg in validArgs.args do
                        if string.find(string.lower(arg), string.lower(word), nil, 1) == 1 then
                                table.insert(matches, arg)
                        end     
                end
                local numMatches = table.getn(matches)
                if validArgs.type == "group" then
                        if numMatches == 1 then
                                this:HighlightText(left - 1, left + string.len(word))
                                this:Insert(matches[1])
                                this:Insert(" ")
                        elseif numMatches > 1 then
                                printUsage(self, validArgs.handler, realOptions, validArgs, path, argwork, true, LCS(matches))
                                this:HighlightText(left - 1, left + string.len(word))
                                this:Insert(LCS(matches))
                        else
                                printUsage(self, validArgs.handler, realOptions, validArgs, path, argwork)
                        end
                end
        end
end

function external(self, major, instance)
        if major == "AceEvent-2.0" then
                if not AceEvent then
                        AceEvent = instance
                        
                        AceEvent:embed(self)
                end
        elseif major == "AceHook-2.0" then
                if not AceHook then
                        AceHook = instance
                        
                        AceHook:embed(self)
                end
        end
end

local function activate(self, oldLib, oldDeactivate)
        AceConsole = self
        
        self.super.activate(self, oldLib, oldDeactivate)
        
        if oldLib then
                self.registry = oldLib.registry
                self.nextAddon = oldLib.nextAddon
        end
        if not self.registry then
                self.registry = {}
        else
                for name,options in pairs(self.registry) do
                        self:RegisterChatCommand(false, options, name)
                end
        end
        
        if oldDeactivate then
                oldDeactivate(oldLib)
        end
end

AceLibrary:Register(AceConsole, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
AceConsole = AceLibrary(MAJOR_VERSION)