local function EmitMessage(text)
    if text == "" then
        text = "[Empty String]"
    end

    if CHAT_ROUTER then
        CHAT_ROUTER:AddDebugMessage(text)
    elseif RequestDebugPrintText then
        RequestDebugPrintText(text)
    end
end

local function EmitTable(t, indent, tableHistory)
    indent          = indent or "."
    tableHistory    = tableHistory or {}
    
    for k, v in pairs(t)
    do
        local vType = type(v)

        EmitMessage(indent.."("..vType.."): "..tostring(k).." = "..tostring(v))
        
        if(vType == "table")
        then
            if(tableHistory[v])
            then
                EmitMessage(indent.."Avoiding cycle on table...")
            else
                tableHistory[v] = true
                EmitTable(v, indent.."  ", tableHistory)
            end
        end
    end    
end

function d(...)    
    for i = 1, select("#", ...) do
        local value = select(i, ...)
        if(type(value) == "table")
        then
            EmitTable(value)
        else
            EmitMessage(tostring (value))
        end
    end
end

function df(formatter, ...)
    return d(formatter:format(...))
end

function countGlobals(desiredType)
    if(desiredType == nil)
    then
        countGlobals("number")
        countGlobals("string")
        countGlobals("boolean")
        countGlobals("table")
        countGlobals("function")
        countGlobals("thread")
        countGlobals("userdata")
        return
    end

    local count = 0
    desiredType = tostring(desiredType)
    
    for k, v in pairs(_G)
    do 
        if(type(v) == desiredType)
        then 
            count = count + 1 
        end 
    end 
    
    d("There are "..count.." variables of type "..desiredType)
end

function eventArgumentDebugger(...)
    local arg = { ... }    
    local numArgs = #arg
    
    if(numArgs == 0)
    then
        return "[Unamed event, without arguments]"
    end
    
    local eventName = arg[1]
    
    if(EVENT_NAME_LOOKUP) -- nice, we can get a string look up for this event code!  (TODO: just expose these as strings?)
    then
        eventName = EVENT_NAME_LOOKUP[eventName] or eventName
    end
    
    local argString = "["..eventName.."]"
    local currentArg
    
    for i = 2, numArgs
    do
        currentArg = arg[i]
        if(type(currentArg) ~= "userdata")
        then
            argString = argString.."|c00ff00["..i.."]:|r "..tostring(currentArg)
        else
            -- Assume it's a control...which may not always be correct
            argString = argString.."|c00ff00["..i.."]:|r "..currentArg:GetName()
        end
        
        if(i < numArgs) then argString = argString..", " end
    end
    
    return argString
end

local eventRegistry = {}

function ZO_Debug_EventNotification(eventCode, register, allEvents)
    local eventManager = GetEventManager()
    local eventName = "ZO_Debug_EventNotification"..eventCode
    
    if(register and not eventRegistry[eventName])
    then
        eventRegistry[eventName] = true
        
        if(allEvents)
        then
            eventManager:RegisterForAllEvents(eventName, function(...) d(eventArgumentDebugger(...)) end)
        else
            eventManager:RegisterForEvent(eventName, eventCode, function(...) d(eventArgumentDebugger(...)) end)
        end
    else
        eventRegistry[eventName] = nil
        eventManager:UnregisterForEvent(eventName)
    end
end

-- Because typing is painful.
e = ZO_Debug_EventNotification

function all() e(0, true, true) end

-- Convenience for multiple event registration only using event variables.
-- Pass in multiple comma delimited events as the arguments:
-- ZO_Debug_MultiEventRegister(EVENT_QUEST_LIST_UPDATED, EVENT_QUEST_CONDITION_COUNTER_CHANGED, EVENT_QUEST_TOOL_UPDATED, etc...)
function ZO_Debug_MultiEventRegister(...)
    for i = 1, select("#", ...)
    do
        -- NOTE: should be able to use arg[i] here, but it's not reliable...
        local eventCode = select(i, ...)
        e(eventCode, true, false)
    end
end

m = ZO_Debug_MultiEventRegister

--
-- Execute a command with a well-known pattern over a range of numbers.
-- You give a base command (like: "]createitem")
-- and a range (start id, end id)
-- This function executes it using ExecuteChatCommand(finalCommand) n times where n = end - start
-- and where the id fed to ExecuteChatCommand is start + i.
function ExecutePatternedChatCommand(commandBase, startId, endId)
    if(type(startId) == "number" and type(endId) == "number" and endId > startId)
    then
        for i = startId, endId
        do
            ExecuteChatCommand(string.format("%s %s", commandBase, tostring(i)))
        end
    end
end

expat = ExecutePatternedChatCommand

--
-- Utility to grab the current mouse over window and display its details.
-- stands for MouseOverName
--
function mon()
    local control = moc()
    
    if(control and type(control) == "userdata") then
        d(control)
    else
        d("Mouse isn't over a control, or isn't over a mouseEnabled control.")
    end
    
    return control
end

function moc()
    return WINDOW_MANAGER:GetMouseOverControl()
end

local tierToString =
{
    [DT_LOW] = "LOW",
    [DT_MEDIUM] = "MEDIUM",
    [DT_HIGH] = "HIGH",
    [DT_PARENT] = "PARENT",
}

local layerToString =
{
    [DL_BACKGROUND] = "BACKGROUND",
    [DL_CONTROLS] = "CONTROLS",
    [DL_TEXT] = "TEXT",
    [DL_OVERLAY] = "OVERLAY",
}

local function drawInfo(control)
    local drawTier = tierToString[control:GetDrawTier()] or tostring(control:GetDrawTier())
    local drawLayer = layerToString[control:GetDrawLayer()] or tostring(control:GetDrawLayer())

    d(string.format("|c00ff00DrawInfo of: %s\n    Tier[%s] Layer[%s] Level[%d]", control:GetName(), drawTier, drawLayer, control:GetDrawLevel()))
end

di = drawInfo

do
    local memoryUsedBeforeKB = {}
    local memoryUsedAfterKB = {}
    local memoryDeltaKB = {}
    function ZO_StartMemProfile(tag)
        -- Don't want adding elements to the table to count the test
        if not memoryUsedBeforeKB[tag] then
            memoryUsedBeforeKB[tag] = 0
            memoryUsedAfterKB[tag] = 0
            memoryDeltaKB[tag] = 0
        end

        -- Manually run the garbage collector, to avoid measuring the garbage alongside the used memory
        -- Supposedly garbage can take two full cycles to clear if you're using the __gc hook or weak tables or something such,
        -- and that might be superstition but can't hurt
        for i=1, 2 do collectgarbage('collect') end
        memoryUsedBeforeKB[tag] = collectgarbage('count')
    end

    function ZO_StopMemProfile(tag)
        for i=1, 2 do collectgarbage('collect') end
        local afterKB = collectgarbage('count')
        memoryUsedAfterKB[tag] = afterKB
        local deltaKB = afterKB - memoryUsedBeforeKB[tag]
        memoryDeltaKB[tag] = deltaKB
        WriteToInterfaceLog(string.format("Lua mem profile '%s' result: %.3f kb", tag, deltaKB))
        return deltaKB
    end

    function ZO_GetLastMemProfileDelta(tag)
        return memoryDeltaKB[tag]
    end
end