﻿
-- Add the module to the tree
local mod = klhtm
local me = {}
mod.out = me

--[[ 
Output.lua. These comments last updated R17.8.

	This module controls printing. For a normal print to the user that will always occur, call mod.out.print(<message>).
	The second part of this module is trace printing. This is a printout that assists with debugging, which you might not want most users to see. The module provides methods to determine which debug prints should be sent to the user. Then to make a debug print, first call mod.out.checktrace(). If it returns non-nil, call mod.out.printtrace() with the actual message.
	Defaults are set by <me.default> around line 27, overrides are set by <me.setprintstatus()> calls around line 128.
]]

--[[ 
--------------------------------------------------------------------------
			Trace Printing for Debugging or Extra Information
--------------------------------------------------------------------------

	The idea of this section is to provide a detailed method to evaluate whether a specific trace message should be printed. We have a few data structures that are print options of the form "if the print is <x>, do / dont print it".
	The more specific a print option is, the higher the priority it has. So the defaults, which just say "always print on <error>" or "never print on <info>" are the least specific and will be overridden by <me.setprintstatus>.
	For a release version, it is sufficient to set "error" = true, and the rest = "false", but for a debug version, you might only want to focus on specific sections of code for trace prints.

]]

-- These are default printing options. For a release version, error only. For a debug version, maybe warnings too.
me.default = 
{
	info = false,
	warning = false,
	error = true,
}

me.onload = function()

	-- optional debug specification
	
	--me.setprintstatus("boss", nil, "info", true)
	--me.setprintstatus("boss", "target", "info", false)

end

--[[  
Suppose the following method calls were made:
	me.setprintstatus("boss", nil, "warning", true)
	me.setprintstatus("boss", "event", "info", true)
	
Then me.override would look like
me.override = 
{
	boss = 
	{
		warning = true
		error = true
		sections = 
		{
			event = 
			{
				info = true
				warning = true
				error = true
			}
		}
	}
}

See <me.setprintstatus> for more information
]]
me.override = { }

--[[ 
me.setprintstatus(modulename, sectionname, messagetype, value)
Overrides the default print option for a specific trace print.
<modulename> is a string, the source of the print, e.g. "out" for this module.
<sectionname> is a string, a feature in the source module. e.g. "trace" for this section.
<messagetype> is either "info" or "warning" or "error".
<value> is a boolean, true to enable the print, false to disable it.

<sectionname> is an optional parameter. If it is nil, the override will apply to the whole module, but it is now less specific, so an individual section inside that module may be overriden again.
<messagetype> will automatically cascade. "error" is assumed to be more important than "warning", which is more important than "info". So if you turn "warning" off, it will turn "info off as well"; if you turn "info" on, "warning" and "error" will be turned on too.
]]
me.setprintstatus = function(modulename, sectionname, messagetype, value)

	-- check module exists
	if me.override[modulename] == nil then
		me.override[modulename] = { }
	end
	
	local printdata = me.override[modulename]
	
	-- is this for the whole module, or more specific?
	if sectionname then
		
		-- check whether any sections have been defined for this module
		if printdata.sections == nil then
			printdata.sections = { }
		end
		
		printdata = printdata.sections
		
		-- check whether this section has been defined in the sections list
		if printdata[sectionname] == nil then
			printdata[sectionname] = { }
		end
		
		printdata = printdata[sectionname]
	end
	
	-- set
	printdata[messagetype] = value
	
	-- cascade
	if value == true then
		if messagetype == "info" then
			printdata.warning = true
			messagetype = "warning"
		end
		
		if messagetype == "warning" then
			printdata.error = true
		end
	
	elseif value == false then
		if messagetype == "error" then
			printdata.warning = false
			messagetype = "warning"
		end
		
		if messagetype == "warning" then
			printdata.info = false
		end
	end

end



--[[
This is a reverse lookup of the top level of the list <mod>. <mod> has keys that are strings like "out", and values that are modules (lists), like <me>. <me.modulelookup> reverses this, giving us the name of a module from a reference to it.
Calls to <me.checktrace> supply a module reference and we might like to name the module. However we wouldn't want to search for the module name every time that method is called, since we want it in particular to be fast. 
We don't fill me.modulelookup at runtime, but each time <me.checktrace> is called with a <module> parameter that is not a key to <me.modulelookup>, we will search to find that module.
]]
me.modulelookup = { }

--[[
me.getmodulename(module)
Given a reference to a module (subtree of <local mod>), returns the name, which is the key in <mod> of the module.
]]
me.getmodulename = function(module)
	
	-- have we already found this module before?
	local try = me.modulelookup[module]
	if try then
		return try
	end
	
	-- manual search
	local key, value
	
	for key, value in mod do
		if value == module then
			me.modulelookup[module] = key
			return key
		end
	end
	
	return "unknown"
	
end

--[[ 
mod.out.checktrace(messagetype, module, sectionname)
Checks whether a debug print with the given properties should be printed.
Return: non-nil iff the message should be printed.
<messagetype> must be one of "error", "warning" or "info"
<module> should always be <me> in the calling context
<sectionname> is a description of the feature in <module> that the message concerns.
]]
me.checktrace = function(messagetype, module, sectionname)
	
	-- start with default print value
	local value = me.default[messagetype]
	me.printargs.overridelevel = "default"
	
	-- convert module reference to name
	local modulename = me.getmodulename(module)
	
	-- are there any overrides for that module?
	local printdata = me.override[modulename]
	
	if printdata then
		if printdata[messagetype] then
			value = printdata[messagetype]
			me.printargs.overridelevel = "module"
		end
		
		-- are there overrides for this section of the module?		
		if printdata.sections and printdata.sections[sectionname] and (printdata.sections[sectionname][messagetype] ~= nil) then
			value = printdata.sections[sectionname][messagetype]
			me.printargs.overridelevel = "section"
		end
	end
	
	-- pre-return: load arguments for me.printtrace
	me.printargs.modulename = modulename
	me.printargs.sectionname = sectionname
	me.printargs.messagetype = messagetype
	
	-- return: nil or non-nil
	if value == true then
		return true
	end
	
end

-- This stores the options supplied to <me.checktrace>, which will slightly affect the printout.
me.printargs = 
{
	messagetype = "",
	modulename = "",
	sectionname = "",
	overridelevel = "",
}

--[[
mod.out.printtrace(message)
Prints a message that has been OK'd by <me.checktrace>.
]]
me.printtrace = function(message)

	-- setup the colour. Error = red, warning = yellow, info = blue. Lightish colours.
	local header = ""
	
	if me.printargs.messagetype == "info" then
		header = "|cff8888ff"
	
	elseif me.printargs.messagetype == "warning" then
		header = "|cffffff44"
	
	elseif me.printargs.messagetype == "error" then
		header = "|cffff8888"
	end
	
	header = header .. "<" .. me.printargs.modulename .. "." .. me.printargs.sectionname .. "> "
	
	-- print!
	me.print(header .. message)
	
end


--[[ 
----------------------------------------------------------------------
			Normal Printing to the Console
----------------------------------------------------------------------
]]

--[[ 
mod.out.print(message, [chatframeindex, noheader])
Prints out <message> to chat.
To print to ChatFrame3, set <chatframeindex> to 3, etc.
Adds a header "KTM: " to the message, unless <noheader> is non-nil.
]]
me.print = function(message, chatframeindex, noheader)

	-- Get a Frame to write to
	local chatframe

	if chatframeindex == nil then
		chatframe = DEFAULT_CHAT_FRAME
		
	else
		chatframe = getglobal("ChatFrame" .. chatframeindex)
		
		if chatframe == nil then
			chatframe = DEFAULT_CHAT_FRAME
		end
	end

	-- touch up message
	message = message or "<nil>"
		
	if noheader == nil then
		message = "KTM: " .. message 
	end
	
	-- write
	chatframe:AddMessage(message)

end

--[[
mod.out.booltostring(boolean)
Converts a Boolean value (true or false) to a string representation.
true -> "true", false -> "false", nil -> "nil"
]]
me.booltostring = function(boolean)
	
	if boolean == true then
		return "true"
	elseif boolean == false then
		return "false"
	else
		return "nil"
	end
	
end

--[[ 
mod.out.announce(message)
Sends a chat message to Raid if possible, or Party if possible, or finally Say.
]]
me.announce = function(message)
		
	local channel = "SAY"

	if GetNumRaidMembers() > 0 then
		channel = "RAID"

	elseif GetNumPartyMembers() > 0 then
		channel = "PARTY"
	end

	SendChatMessage(message, channel)

end
