vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
Name: AceConsole-2.0
Revision: $Rev: 8315 $
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: 8315 $"

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 |cffffff7f%s|r is disabled."
-- localize --

local NONE = NONE or "None"

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

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

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, connector, 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
                tmp[1] = a1
                tmp[2] = a2
                tmp[3] = a3
                tmp[4] = a4
                tmp[5] = a5
                tmp[6] = a6
                tmp[7] = a7
                tmp[8] = a8
                tmp[9] = a9
                tmp[10] = a10
                tmp[11] = a11
                tmp[12] = a12
                tmp[13] = a13
                tmp[14] = a14
                tmp[15] = a15
                tmp[16] = a16
                tmp[17] = a17
                tmp[18] = a18
                tmp[19] = a19
                tmp[20] = a20
                local n = 20
                while tmp[n] == nil do
                        n = n - 1
                end
                table.setn(tmp, n)
                for k = 1, n do
                        tmp[k] = tostring(tmp[k])
                end
                print(table.concat(tmp, connector or " "), 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)
        return 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

function AceConsole:PrintComma(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        return 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.cmdHidden or 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.cmdHidden and type(options.cmdHidden) == "string" and type(self[options.cmdHidden]) ~= "function" then
                return string.format("%q is not a proper function", options.cmdHidden), position
        end
        if options.guiHidden and type(options.guiHidden) == "string" and type(self[options.guiHidden]) ~= "function" then
                return string.format("%q is not a proper function", options.guiHidden), 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(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" and kind ~= "header" then
                return '"type" must either be "range", "text", "group", "toggle", "execute", "color", or "header".', 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 kind == "header" then
                elseif 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.type == "header" and not options.cmdName and not options.name then
                elseif 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
                                if not options.guiNameIsMap then
                                        return '"guiName" must be a string or nil', position
                                end
                        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.guiNameIsMap then
                if type(options.guiNameIsMap) ~= "boolean" then
                        return '"guiNameIsMap" must be a boolean or nil', position
                elseif options.type ~= "toggle" then
                        return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
                elseif type(options.map) ~= "table" then
                        return '"map" must be a table', position
                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.order then
                if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
                        return '"order" must be a non-zero number or nil', position
                end
        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.cmdHidden then
                if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
                        return '"cmdHidden" must be a function, string, or boolean', position
                end
        end
        if options.guiHidden then
                if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
                        return '"guiHidden" 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
                        local t = options.validate
                        local iTable = nil
                        for k,v in pairs(t) do
                                if type(k) == "number" then
                                        if iTable == nil then
                                                iTable = true
                                        elseif not iTable then
                                                return '"validate" must either have all keys be indexed numbers or strings', position
                                        elseif k < 1 or k > table.getn(t) then
                                                return '"validate" numeric keys must be indexed properly. >= 1 and <= table.getn', position
                                        end
                                else
                                        if iTable == nil then
                                                iTable = false
                                        elseif iTable then
                                                return '"validate" must either have all keys be indexed numbers or strings', position
                                        end
                                end
                                if type(v) ~= "string" then
                                        return '"validate" values must all be strings', position
                                end
                        end
                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, function, or table', 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
                        elseif string.find(k, "%s") then
                                return string.format('"args" keys must not include spaces. %q is not appropriate.', k), position
                        elseif string.len(k) == 0 then
                                return '"args" keys must not be 0-length strings.', 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(v, newposition, baseOptions, options.pass)
                        if err then
                                return err, pos
                        end
                end
        end
end

local colorTable
local colorFunc
local colorCancelFunc

local order

local mysort_args
local mysort

local function printUsage(self, handler, realOptions, options, path, args, quiet, filter)
        if filter then
                filter = "^" .. string.gsub(filter, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
        end
        local hidden, disabled = options.cmdHidden or 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
                                local handler = passTable.handler or handler
                                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
                                local handler = options.handler or handler
                                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 k,v in pairs(options.validate) do
                                        if string.find(v, filter) then
                                                table.insert(order, v)
                                        end
                                end
                                usage = "{" .. table.concat(order, " || ") .. "}"
                                for k in pairs(order) do
                                        order[k] = nil
                                end
                                table.setn(order, 0)
                        else
                                if not order then
                                        order = {}
                                end
                                for k,v in pairs(options.validate) do
                                        table.insert(order, v)
                                end
                                usage = "{" .. table.concat(order, " || ") .. "}"
                                for k in pairs(order) do
                                        order[k] = nil
                                end
                                table.setn(order, 0)
                        end
                        var = options.validate[var] or var
                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
                                local handler = passTable.handler or handler
                                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 handler
                                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 v.type ~= "header" then
                                        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
                        end
                        if not mysort then
                                mysort = function(a, b)
                                        local alpha, bravo = mysort_args[a], mysort_args[b]
                                        local alpha_order = alpha and alpha.order or 100
                                        local bravo_order = bravo and bravo.order or 100
                                        if alpha_order == bravo_order then
                                                return tostring(a) < tostring(b)
                                        else
                                                if alpha_order < 0 then
                                                        if bravo_order > 0 then
                                                                return false
                                                        end
                                                else
                                                        if bravo_order < 0 then
                                                                return true
                                                        end
                                                end
                                                if alpha_order > 0 and bravo_order > 0 then
                                                        return tostring(a) < tostring(b)
                                                end
                                                return alpha_order < bravo_order
                                        end
                                end
                        end
                        mysort_args = options.args
                        table.sort(order, mysort)
                        mysort_args = nil
                        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]
                                if v then
                                        local hidden, disabled = v.cmdHidden or v.hidden, v.disabled
                                        if hidden then
                                                if type(hidden) == "function" then
                                                        hidden = hidden()
                                                elseif type(hidden) == "string" then
                                                        local handler = v.handler or handler
                                                        if type(handler[hidden]) ~= "function" then
                                                                AceConsole:error(OPTION_HANDLER_NOT_FOUND, hidden)
                                                        end
                                                        hidden = handler[hidden](handler)
                                                end
                                        end
                                        if not hidden then
                                                if disabled then
                                                        if type(disabled) == "function" then
                                                                disabled = disabled()
                                                        elseif type(disabled) == "string" then
                                                                local handler = v.handler or handler
                                                                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 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 handler
                                                                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
                                                        elseif v.type == "text" and type(v.validate) == "table" then
                                                                s = tostring(v.validate[a1] or a1)
                                                        else
                                                                s = tostring(a1 or NONE)
                                                        end
                                                        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] %s|r", k, s, desc))
                                                        else
                                                                print(string.format(" - |cffffff7f%s: [|r%s|cffffff7f]|r %s", k, s, v.desc or NONE))
                                                        end
                                                else
                                                        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
                        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.cmdHidden or 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 _G_this = this
        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 = string.lower(tostring(arg))
                                        for k,v in pairs(options.validate) do
                                                local bit
                                                if string.lower(v) == arg then
                                                        args[1] = v
                                                        good = true
                                                        break
                                                elseif type(k) == "string" and string.lower(k) == arg then
                                                        args[1] = k
                                                        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
                                                if not order then
                                                        order = {}
                                                end
                                                for k,v in pairs(options.validate) do
                                                        table.insert(order, v)
                                                end
                                                usage = "{" .. table.concat(order, " || ") .. "}"
                                                for k in pairs(order) do
                                                        order[k] = nil
                                                end
                                                table.setn(order, 0)
                                        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
                        
                        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 var ~= args[1] then
                                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
                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 type(options.validate) == "table" then
                                var = options.validate[var] or var
                        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
                        if var == args[1] then
                                return
                        end
                else
                        printUsage(self, handler, realOptions, options, path, args)
                        return
                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
                        local ret, msg
                        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 good and type(options.step) == "number" and options.step > 0 then
                                        local step = options.step
                                        arg = math.floor((arg - 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
                        
                        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 ~= arg then
                                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
                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)
                        if var == arg then
                                return
                        end
                else
                        printUsage(self, handler, realOptions, options, path, args)
                        return
                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
                return
        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
                return
        end
        this = _G_this
        if Dewdrop then
                Dewdrop:Refresh(1)
                Dewdrop:Refresh(2)
                Dewdrop:Refresh(3)
                Dewdrop:Refresh(4)
                Dewdrop:Refresh(5)
        end
end

local external
function AceConsole:RegisterChatCommand(slashCommands, options, name)
        if type(slashCommands) ~= "table" and slashCommands ~= false then
                AceConsole:error("Bad argument #2 to `RegisterChatCommand' (expected table, got %s)", type(slashCommands))
        end
        if not slashCommands and type(name) ~= "string" then
                AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string, got %s)", type(name))
        end
        if type(options) ~= "table" and type(options) ~= "function" and options ~= nil then
                AceConsole:error("Bad argument #3 to `RegisterChatCommand' (expected table, function, or nil, got %s)", type(options))
        end
        if name then
                if type(name) ~= "string" then
                        AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string or nil, got %s)", type(name))
                elseif not string.find(name, "^%w+$") or string.upper(name) ~= name or string.len(name) == 0 then
                        AceConsole:error("Argument #4 must be an uppercase, letters-only string with at least 1 character")
                end
        end
        if slashCommands then
                if table.getn(slashCommands) == 0 then
                        AceConsole:error("Argument #2 to `RegisterChatCommand' must include at least one string")
                end
                
                for k,v in pairs(slashCommands) do
                        if type(k) ~= "number" then
                                AceConsole:error("All keys in argument #2 to `RegisterChatCommand' must be numbers")
                        end
                        if type(v) ~= "string" then
                                AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be strings")
                        elseif not string.find(v, "^/[A-Za-z][A-Za-z0-9_]*$") then
                                AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be in the form of \"/word\"")
                        end
                end
        end
        
        if not options then
                options = {
                        type = 'group',
                        args = {},
                        handler = self
                }
        end
        
        if type(options) == "table" then
                local err, position = validateOptions(options)
                if err then
                        if position then
                                AceConsole:error(position .. ": " .. err)
                        else
                                AceConsole:error(err)
                        end
                end
                
                if not options.handler then
                        options.handler = self
                end
                
                if options.handler == self and string.lower(options.type) == "group" and self.class then
                        AceConsole:InjectAceOptionsTable(self, options)
                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
                if type(options) == "table" then
                        AceConsole.nextAddon[self] = options
                        if not self.playerLogin then
                                AceConsole:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true)
                        end
                end
        end
        
        AceConsole.registry[name] = options
end

function AceConsole:InjectAceOptionsTable(handler, options)
        self:argCheck(handler, 2, "table")
        self:argCheck(options, 3, "table")
        if string.lower(options.type) ~= "group" then
                self:error('Cannot inject into options table argument #3 if its type is not "group"')
        end
        if options.handler ~= nil and options.handler ~= handler then
                self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
        end
        options.handler = handler
        local class = handler.class
        if not class then
                self:error("Cannot retrieve AceOptions tables from a non-object argument #2")
        end
        while class and class ~= AceOO.Class do
                if type(class.GetAceOptionsDataTable) == "function" then
                        local t = class:GetAceOptionsDataTable(handler)
                        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(handler)
                                        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
        return 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 = 1,table.getn(arg) do arg[k] = tostring(arg[k]) end DEFAULT_CHAT_FRAME:AddMessage(table.concat(arg, ' ')) end func(" .. text .. ")")
        end, "PRINT")
end

function AceConsole:TabCompleteInfo(cmdpath)
        local _, _, cmd =  string.find(cmdpath, "(/%S+)")
        local path = string.sub(cmdpath, string.len(cmd) + 2)
        for name in pairs(SlashCmdList) do --global
                if AceConsole.registry[name] then
                        local i = 0
                        while true do
                                i = i + 1
                                local scmd = _G["SLASH_"..name..i]
                                if not scmd then break end
                                if cmd == scmd then
                                        return name, cmd, path
                                end
                        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 == "AceTab-2.0" then
                instance:RegisterTabCompletion("AceConsole", "%/.*", function(t, cmdpath, pos)
                        local ac = AceLibrary("AceConsole-2.0")
                        local name, cmd, path = ac:TabCompleteInfo(string.sub(cmdpath, 1, pos))

                        if not ac.registry[name] then
                                return false
                        else
                                local validArgs = findTableLevel(ac, ac.registry[name], cmd, path or "")
                                if validArgs.args then
                                        for arg in pairs(validArgs.args) do
                                                table.insert(t, arg)
                                        end
                                end
                        end
                end, function(u, matches, gcs, cmdpath)
                        local ac = AceLibrary("AceConsole-2.0")
                        local name, cmd, path = ac:TabCompleteInfo(cmdpath)
                        if ac.registry[name] then
                                local validArgs, path2, argwork = findTableLevel(ac, ac.registry[name], cmd, path)
                                printUsage(ac, validArgs.handler, ac.registry[name], validArgs, path2, argwork, not gcs or gcs ~= "", gcs)
                        end
                end)
        elseif major == "Dewdrop-2.0" then
                Dewdrop = instance
        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)