
KLHTM_Trace = 
{
	--["TraceAll"] = true,	
	--["TableRenderFail"] = true,
	--["TableRedrawDenial"] = true,
	--["ChannelMessages"] = true,
	--["ChannelFail"] = true,
	--["ThreatUpdates"] = true,
	--["TargetNotInRaid"] = true,
	--["MessageSent"] = true,
	--["BadMessage"] = true,
	--["RecordTimeTaken"] = true,
	["Assertion"] = true,
	--["FeignDeathDebug"] = true,
	--["TimingDebug"] = true,
	--["TargetDebug"] = true,
	--["SunderDebug"] = true,
	--["RaidColours"] = true,
	--["MonsterEmote"] = true,
	--["Knockback"] = true,
	--["SpellRank"] = true,
	--["LocalNoOverride"] = true,
	["LocalInvalidKey"] = true,
	--["InCombat"] = true,
	--["Buff"] = true,
	--["StateTimeout"] = true,
}

KLHTM_Clui_Version = 
{
	["Description"] = 
	function()
		KLHTM_Print(string.format("This is Release |cff33ff33%s|r Revision |cff33ff33%s|r. These commands require you to be the raid leader or an officer.", KLHTM_Release, KLHTM_Revision))
	end,
	["Branches"] = 
	{
		{
			["Command"] = "notify",
			["Description"] = "Notifies users with an older version of the mod to upgrade.",
			["Output"] = 
				function()
					if KLHTM_IsPlayerOfficer(UnitName("player")) then
						KLHTM_SendMessage("version " .. KLHTM_Release)
						KLHTM_Print("Older versions of the mod have been notified to upgrade.")
					else
						KLHTM_Print("You have to be an officer to do that.")
					end
				end
		},
		{
			["Command"] = "query",
			["Description"] = "Asks everyone in the raid to report their mod version.",
			["Output"] = KLHTM_VersionQuery
		}
	}
}

KLHTM_Clui_Main = 
{	
	["Description"] = nil,
	["Branches"] = 
	{
		{
			["Command"] = "show",
			["Description"] = "Shows the main window.",
			["Output"] = 
				function()
					KLHTM_Frame:Show()
					KLHTM_MainWindow.IsShown = true
					KLHTM_SavedVariables.IsMainWindowVisible = true
				end,
		},
		{
			["Command"] = "table",
			["Description"] = "Shows or hides the main window table.",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTM_Frame, "Minimise")
				end,
		},
		{
			["Command"] = "clear",
			["Description"] = "Clears your personal threat data.",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTM_Frame, "Clear")
				end,
		},
		{
			["Command"] = "close",
			["Description"] = "Hides the main window.",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTM_Frame, "Close")
				end,
		},
		{
			["Command"] = "pin",
			["Description"] = "Pins or unpins the main window (prevents it being moved)",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTM_Frame, "Pin")
				end,
		},
		{
			["Command"] = "reset",
			["Description"] = "Puts the main window back in the middle of the screen.",
			["Output"] = 
				function()
					KLHTM_Frame:ClearAllPoints()
					KLHTM_Frame:SetPoint("CENTER", UIParent)
					
					KLHTM_Frame:Show()
					KLHTM_MainWindow.IsShown = true
					KLHTM_SavedVariables.IsMainWindowVisible = true
				end,
		},
	}
}

KLHTM_Clui_Raid = 
{
	["Description"] = nil,
	["Branches"] = 
	{
		{
			["Command"] = "show",
			["Description"] = "Shows the raid window.",
			["Output"] = 
				function()
					KLHTM_RaidFrame:Show()
					KLHTM_RaidWindow.IsShown = true
					KLHTM_SavedVariables.IsRaidWindowVisible = true
				end,
		},
		{
			["Command"] = "table",
			["Description"] = "Shows or hides the raid window table.",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTM_RaidFrame, "Minimise")
				end,
		},
		{
			["Command"] = "clear",
			["Description"] = "Refresh the raid threat table. For an officer, will also clear everyone's threat.",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTM_RaidFrame, "Clear")
				end,
		},
		{
			["Command"] = "close",
			["Description"] = "Hides the raid window.",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTM_RaidFrame, "Close")
				end,
		},
		{
			["Command"] = "pin",
			["Description"] = "Pins or unpins the raid window (prevents it being moved)",
			["Output"] = 
				function()
					KLHTM_Button_OnClick(KLHTMRaid_Frame, "Pin")
				end,
		},
		{
			["Command"] = "reset",
			["Description"] = "Puts the raid window back in the middle of the screen.",
			["Output"] = 
				function()
					KLHTM_RaidFrame:ClearAllPoints()
					KLHTM_RaidFrame:SetPoint("CENTER", UIParent)
					
					-- also do a show
					KLHTM_RaidFrame:Show()
					KLHTM_RaidWindow.IsShown = true
					KLHTM_SavedVariables.IsRaidWindowVisible = true
				end,
		},
		{
			["Command"] = "rows",
			["Description"] = "Set the maximum number of rows visible.",
			["Output"] = 
				function(Rows)
					local newrows = tonumber(Rows)
	
					if newrows ~= nil and newrows > 0 and newrows <= KLHTM_TableMaxRows then
						KLHTM_RaidWindow.Table.RowsVisible = newrows
						KLHTM_SavedVariables.RaidRowsVisible = newrows
						KLHTM_RedrawTable(KLHTM_RaidWindow)
					else
						KLHTM_Print("|cffff8888Usage: |cffffff00/ktm raid rows x |cffff8888- where x is a number between 1 and " .. KLHTM_TableMaxRows .. ". The current value is " .. KLHTM_SavedVariables.RaidRowsVisible .. ".")
					end
				end
		},
	}
}

KLHTM_Clui_Test = 
{
	["Description"] = nil,
	["Branches"] = 
	{
		{
			["Command"] = "talents",
			["Description"] = "Prints out your talent points in any talents that affect your threat.",
			["Output"] = KLHTM_TestTalents
		},
		{
			["Command"] = "gear",
			["Description"] = "Prints out the set pieces you are wearing for sets that affect your threat.",
			["Output"] = KLHTM_DebugItemSets
		},
		{
			["Command"] = "threat",
			["Description"] = "Prints out a variety of threat parameters.",
			["Output"] = KLHTM_TestThreatParameters
		},
		{
			["Command"] = "time",
			["Description"] = "Prints out processor time information.",
			["Output"] = 
				function()
					local value
					local key
					local timenow = GetTime()
					
					-- 1) Events:
					for _, key in KLHTM_Events do
						value = KLHTM_TimingData[key .. "Event"]
						KLHTM_Print(string.format("|cff00ffff%s|r: Total = %sms, Max = %sms/s, Average = %sms/s", key, KLHTM_FormatDecimal(value.Total), KLHTM_FormatDecimal(value.RecordSequence / value.BackTrackLength), KLHTM_FormatDecimal(value.Total / (timenow - KLHTM_OriginalStartTime))))
						
					end
					
					value = KLHTM_TimingData["Events"]
					KLHTM_Print(string.format("|cffff00ff%s|r: Total = %sms, Max = %sms/s, Average = %sms/s", "Events", KLHTM_FormatDecimal(value.Total), KLHTM_FormatDecimal(value.RecordSequence / value.BackTrackLength), KLHTM_FormatDecimal(value.Total / (timenow - KLHTM_OriginalStartTime))))
					
					-- 2) Updates:
					for _, key in KLHTM_Updates do
						value = KLHTM_TimingData[key .. "Update"]
						KLHTM_Print(string.format("|cff00ffff%s|r: Total = %sms, Max = %sms/s, Average = %sms/s", key, KLHTM_FormatDecimal(value.Total), KLHTM_FormatDecimal(value.RecordSequence / value.BackTrackLength), KLHTM_FormatDecimal(value.Total / (timenow - KLHTM_OriginalStartTime))))
					end					
					
					value = KLHTM_TimingData["Updates"]
					KLHTM_Print(string.format("|cffff00ff%s|r: Total = %sms, Max = %sms/s, Average = %sms/s", "Updates", KLHTM_FormatDecimal(value.Total), KLHTM_FormatDecimal(value.RecordSequence / value.BackTrackLength), KLHTM_FormatDecimal(value.Total / (timenow - KLHTM_OriginalStartTime))))
					
					-- 3) Total
					value = KLHTM_TimingData["Total"]
					KLHTM_Print(string.format("|cffffff00%s|r: Total = %sms, Max = %sms/s, Average = %sms/s", "Total", KLHTM_FormatDecimal(value.Total), KLHTM_FormatDecimal(value.RecordSequence / value.BackTrackLength), KLHTM_FormatDecimal(value.Total / (timenow - KLHTM_OriginalStartTime))))
					
				end	
		},
		{
			["Command"] = "channel",
			["Description"] = "Checks whether the CTRA channel is properly set up.",
			["Output"] = 
				function()
					if CT_RA_Channel == nil then
						KLHTM_Print("Warning: there is no CTRA Channel.")
		
					else	
						local channelnumber = GetChannelName(CT_RA_Channel)
			
						if channelnumber > 0 then
							KLHTM_Print(string.format("The channel appears to be working. You are using channel %d, '%s'.", channelnumber, CT_RA_Channel))
								
						else
							KLHTM_Print(string.format("Warning: The CTRA channel is set to '%s', but you have not joined this channel.", CT_RA_Channel))
						end
					end
				end	
		},
		{
			["Command"] = "netlog",
			["Description"] = "Information about the channel messages received.",
			["Output"] = 
				function()
					local key
					local value
					
					for key, value in KLHTM_MessageLog do
						KLHTM_Print(string.format("|cffffff00%s: |r%d bytes in %d messages (%d average).", key, value.TotalBytes, value.NumMessages, math.floor(0.5 + value.TotalBytes / value.NumMessages)))
					end 
				end	
		},
		{
			["Command"] = "states",
			["Description"] = "Check that the mod has the correct value for its state variables",
			["Output"] = 
				function()
					local key
					local value
					
					local doformat = function(Time)
						if Time == 0 then
							return "never"
						end
						
						return string.format("%d seconds ago", GetTime() - Time)
					end				
					
					for key, value in KLHTM_States do
						KLHTM_Print(string.format("The state '%s' is '%s'. The last change was %s.", key, KLHTM_BoolToString(value.value), doformat(value.lastchange)))
					end
				end
		}
	}
}

KLHTM_Clui_MasterTarget = 
{
	["Description"] = "If a master target is assigned, only damage and abilities against that target count towards your threat. You must be a raid officer to edit the master target.",
	["Branches"] = 
	{
		{
			["Command"] = "set",
			["Description"] = "Sets the raid's master target to your current target. You must have a unit targetted.",
			["Output"] = 
				function()
					if UnitName("target") == nil then
						KLHTM_Print("Target the mob to select as the master target first.")
					else
						KLHTM_SendMasterTarget(UnitName("target"))
					end
				end
		},
		{
			["Command"] = "clear",
			["Description"] = "Clears the raid's master target. Abilities against any target will count towards your threat.",
			["Output"] = KLHTM_ClearMasterTarget
		},
	}
}

KLHTM_Clui_Knockback = 
{
	["Description"] = "Knockback discovery will make raid members notify when they get aggro from the master target, or when they suffer 'Knock Away' attacks from him. This will just print lots of raw data to your screen, which can hopefully be used to determine the knockback values. You must be a raid officer to perform these commands.",	
	["Branches"] = 
	{
		{
			["Command"] = "start",
			["Description"] = "Sets the raid's master target to your current target. You must have a unit targetted.",
			["Output"] = KLHTM_StartKnockbackDiscovery,
		},
		{
			["Command"] = "stop",
			["Description"] = "Clears the raid's master target. Abilities against any target will count towards your threat.",
			["Output"] = KLHTM_StopKnockbackDiscovery,
		},
		{
			["Command"] = "value",
			["Description"] = "Set the threat reduction percentage of a specific attack from the master target.",
			["Output"] = 
				function(FirstValue, AllValues)
					
					if FirstValue == nil then
						FirstValue = "<nil>"
					end
					local value = tonumber(FirstValue)
					
					if value == nil or value > 0 or value < -1 then
						KLHTM_Print("|cffff8888Usage: |cffffff00/ktm knockback value x y |cffff8888- where x is a number between -1 (for a complete aggro wipe) and 0 (no threat reduction), and y is an attack. The value '" .. FirstValue .. "' is not in the valid range.")
						return
					end
					
					-- construct the attack from the other values
					table.remove(AllValues, 1)
					
					local attack = ""
					local x
					
					for x = 1, table.getn(AllValues) do
						if x > 1 then 
							attack = attack .. " "
						end
						
						attack = attack .. AllValues[x]
					end
					
					if attack == "" then
						attack = "<nil>"
					end 
					
					-- see if this is a valid knockback value
					local key
					for key, _ in KLHTM_Knockbacks do
						if string.lower(key) == attack then
							-- run it
							KLHTM_SetRaidKnockbackValue(value, key) 
							return
						end
					end
					
					-- no hit. error message.
					local message = "|cffff8888Usage: |cffffff00/ktm knockback value x y |cffff8888- where y is one of { "
					local key
						
					for key, _ in KLHTM_Knockbacks do
						message = message .. key .. ", "
					end
						
					message = message .. "}. The attack '" .. attack .. "' is not in the valid set." 
					KLHTM_Print(message)
				end
		},
	}
}

KLHTM_Clui =
{
	--["RootString"] = <a string>. Gets filled in at RunTime - KLHTM_ConsoleUIStartup()
	["Description"] = nil,
	["Branches"] = 
	{
		{
			["Command"] = "test",
			["Description"] = "A set of debugging commands.",
			["Output"] = KLHTM_Clui_Test
		},
		{
			["Command"] = "main",
			["Description"] = "Commands for the main window.",
			["Output"] = KLHTM_Clui_Main
		},
		{
			["Command"] = "raid",
			["Description"] = "Commands for the raid window.",
			["Output"] = KLHTM_Clui_Raid
		},
		{
			["Command"] = "scale",
			["Description"] = "Sets the size of the graphical elements.",
			["Output"] = 
				function(Scale)
					local newscale = tonumber(Scale)
	
					if newscale ~= nil and newscale >= 10 and newscale <= 30 then
						KLHTM_SavedVariables.GlobalScale = newscale
						KLHTM_SetGlobalScale(newscale)
					else
						KLHTM_Print("|cffff8888Usage: |cffffff00/ktm scale x |cffff8888- where x is a number between 10 and 30. 18 is the default, 10 small, 30 large. Your current scale is " .. KLHTM_GlobalScale .. ".")
					end
				end	
		},
		{
			["Command"] = "version",
			["Description"] = "Commands to check and upgrade the version of other users.",
			["Output"] = KLHTM_Clui_Version,
		},
		{
			["Command"] = "mastertarget",
			["Description"] = "Set a primary target for the raid group.",
			["Output"] = KLHTM_Clui_MasterTarget,
		},	
		{
			["Command"] = "knockback",
			["Description"] = "Knockback discovery methods.",
			["Output"] = KLHTM_Clui_Knockback
		},
	},
}

function KLHTM_ConsoleIOStartup()

	-- set the values of ["RootString"] for all block thingies
	KLHTM_Clui.RootString = "/ktm "
	
	KLHTM_FillChildRootStrings(KLHTM_Clui)
	
end

function KLHTM_FillChildRootStrings(Clui)

	local key
	local value
	
	for key, value in Clui.Branches do
		if type(value.Output) ~= "function" then
			
			-- emergency debug:
			if value.Output == nil then
				klhprint("output is nil for value = " .. value.Description)
			end
			
			value.Output.RootString = Clui.RootString .. value.Command .. " "
			KLHTM_FillChildRootStrings(value.Output)
		end
	end
end

-- Commands = everything after, space delimited
-- ClueBranch = a branch, above.
function KLHTM_RunClui(Commands, Clui)
	
	local command = Commands[1]
	local key
	local branch
	
	if command == nil then
		-- just print out help information for this one
		KLHTM_PrintHelpForClui(Clui)
		
	else
		
		-- find the branches that match the command
		local matchingbranches = { }
		
		for key, branch in Clui.Branches do
			if string.len(branch.Command) >= string.len(command) and string.sub(branch.Command, 1, string.len(command)) == command then
				-- this branch matches the command
				table.insert(matchingbranches, branch)
			end
		end
	
		-- 1) Not enough branches
		if table.getn(matchingbranches) == 0 then
			
			-- print error, print help, abort.
			KLHTM_Print("|cffff8888No command matching |cffffff00" .. Clui.RootString .. command .. "|cffff8888 could be found.")
			
			KLHTM_PrintHelpForClui(Clui)
			
			-- too many branches that match the abbreviation. Error then exit
		elseif table.getn(matchingbranches) > 1 then
			
			local errorstring = "|cffff8888Could not disambiguate your command |cffffff00" .. Clui.RootString .. command .. " |cffff8888, after |cffffff00" .. Clui.RootString .. "|cffff8888 you could mean {"
			for key, branch in matchingbranches do
				if key > 1 then
					errorstring = errorstring .. ", "
				end
				
				errorstring = errorstring .. "|cffffff00" .. branch.Command .. "|cffff8888"
			end
			
			errorstring = errorstring .. "}."
			KLHTM_Print(errorstring)
			
		else -- just one branch matches the abbreviation. run it.
			
			branch = matchingbranches[1]
			if type(branch.Output) == "function" then
				
				-- base command
				local message = "|cff8888ffRunning the command |cffffff00" .. Clui.RootString .. branch.Command 
				
				-- arguments
				table.remove(Commands, 1)
				
				for _, key in Commands do
					message = message .. " " .. key
				end
				
				-- print
				message = message .. "|cff8888ff."
				KLHTM_Print(message)
				
				-- run
				branch.Output(Commands[1], Commands)
				
			else
				-- run the block
				table.remove(Commands, 1)
				KLHTM_RunClui(Commands, branch.Output)
			end
		end
	end	
end


function KLHTM_PrintHelpForClui(Clui)

	KLHTM_Print("|cff8888ffThis is the help topic for |cffffff00" .. Clui.RootString .. "|cff8888ff.")

	if type(Clui.Description) == "string" then
		KLHTM_Print(Clui.Description)
	
	elseif type(Clui.Description) == "function" then
		Clui.Description()
	end
		
	local key
	local branch
	local message
	
	for key, branch in Clui.Branches do
		message = "|cffffff00" .. Clui.RootString .. branch.Command .. "|r - "
		
		if type(branch.Description) == "function" then
			message = message .. branch.Description()
		else
			message = message .. branch.Description
		end
		
		KLHTM_Print(message)
	end

end

-- For now, just show the main window, in case they both get killed
function KLHTM_ConsoleCommand(message)
	
	-- parse space-delimited words into a list
	local commandlist = { }
	local command
	
	for command in string.gfind(message, "[^ ]+") do
		table.insert(commandlist, string.lower(command))
	end
	
	KLHTM_RunClui(commandlist, KLHTM_Clui)

end

function KLHTM_PrintTrace(Condition, Message, ChatFrameIndex)

	if KLHTM_Trace[Condition] or KLHTM_Trace["TraceAll"] then
		KLHTM_Print("<Trace> " .. Message, ChatFrameIndex)
	end

end

function KLHTM_Print(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
	if Message == nil then
		Message = ""
	end
	
	if NoHeader == nil then
		Message = "KTM: " .. Message 
	end
	
	-- write
	chatframe:AddMessage(Message)

end


function KLHTM_BoolToString(Boolean)
	
	if Boolean == true then
		return "true"
	elseif Boolean == false then
		return "false"
	else
		return "nil"
	end
	
end