--! This module references these other modules:
--! boss:	starttrigger, 
--! data:	testtalents, testitemsets, 
--! diag:	printalldata, 
--! my:	testthreat, states, 
--! net:	checkpermission, clearmastertarget, sendmastertarget, clearraidthreat, startspellreporting, stopspellreporting, setspellvalue, checkspellvaluesyntax, versionnotify, versionquery, toggleadvertise, 
--! netin:	messagelog, 
--! out:	print, booltostring, 
--! string:	get, testlocalisation, 

--! This module is referenced by these other modules:

local mod = klhtm
local me = {}
mod.console = me

-- Special onload method called from Core.lua
me.onload = function()
	
	-- Set up a command line handler
	SLASH_KLHThreatMeter1 = "/ktm"
	SLASH_KLHThreatMeter2 = "/klhtm"
	SLASH_KLHThreatMeter3 = "/klhthreatmeter"
	SlashCmdList["KLHThreatMeter"] = me.consolecommand
	
	-- create all the CLUI tables
	me.defineclui()
	
	-- Add their .rootstring values
	me.clui.rootstring = "/ktm "
	me.clui.colourrootstring = "|cffffff00/ktm "
	
	me.fillchildrootstrings(me.clui)
	
end

--[[
me.fillchildrootstrings(clui)
Computes the value of clui.rootstring and .rootstring for all its child branches.
the .rootstring value is what the user has to type to get into that branch. The rootstring for the
topmost node is just "/ktm"; for the test child of the main node, the rootstring is "/ktm test".
This method is called recursively on all child branches.
]]
me.fillchildrootstrings = function(clui)

	local key
	local value
	
	-- debug checks
	if clui == nil then
		mod.out.print("clui = nil")
	elseif clui.branches == nil then
		mod.out.print("branches = nil")
	end
	
	local colourcommands = { }
	local key2
	local value2
	local length
	
	for key, value in clui.branches do
		length = 1
		
		for key2, value2 in clui.branches do
			
			if value ~= value2 then
			
				for x = length, string.len(value.command) - 1 do
				
					if string.sub(value.command, 1, x) == string.sub(value2.command, 1, x) then
						length = x + 1 
					else
						break
					end
				end
			end
		end
		
		value.colourcommand = "|cff33ff88" .. string.sub(value.command, 1, length) .. "|cffffff00" .. string.sub(value.command, length + 1)
		
		-- debug
		if value == nil then
			mod.out.print("oops, nil for key = " .. key)
		end
		
		if type(value.output) ~= "function" then
			value.output.rootstring = clui.rootstring .. value.command .. " "
			value.output.colourrootstring = clui.colourrootstring .. value.colourcommand .. " "
			me.fillchildrootstrings(value.output)
		end
	end
	
end

--[[ 
me.runclui(commands, clui)
Process the commands <commands> on <clui>.
<commands> is an array with 0 or more strings.
<clui> is a branch of the console tree, e.g. me.cluitest
]]
me.runclui = function(commands, clui)
	
	local command = commands[1]
	local key
	local branch
	
	if command == nil then
		-- just print out help information for this one
		me.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.
			mod.out.print("|cffff8888No command matching " .. clui.colourrootstring .. command .. "|cffff8888 could be found.")
			
			me.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 " .. clui.colourrootstring .. command .. " |cffff8888, after " .. clui.colourrootstring .. "|cffff8888 you could mean {"
			for key, branch in matchingbranches do
				if key > 1 then
					errorstring = errorstring .. ", "
				end
				
				errorstring = errorstring .. branch.colourcommand .. "|cffff8888"
			end
			
			errorstring = errorstring .. "}."
			mod.out.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 " .. clui.colourrootstring .. branch.colourcommand 
				
				-- arguments
				table.remove(commands, 1)
				
				for _, key in commands do
					message = message .. " " .. key
				end
				
				-- print
				message = message .. "|cff8888ff."
				mod.out.print(message)
				
				-- run
				branch.output(commands[1], commands)
				
			else
				-- run the block
				table.remove(commands, 1)
				me.runclui(commands, branch.output)
			end
		end
	end	
end


me.printhelpforclui = function(clui)

	mod.out.print("|cff8888ffThis is the help topic for " .. clui.colourrootstring .. "|cff8888ff.")

	if type(clui.description) == "string" then
		mod.out.print(clui.description)
	
	elseif type(clui.description) == "function" then
		mod.out.print(clui.description())
	end
		
	local key
	local branch
	local message
	
	for key, branch in clui.branches do
		message = clui.colourrootstring .. branch.colourcommand .. "|r - "
		
		if type(branch.description) == "function" then
			message = message .. branch.description()
		else
			message = message .. branch.description
		end
		
		mod.out.print(message)
	end

end

--[[ 
This method is called by typing a "/ktm" command in the console.
]]
me.consolecommand = function(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
	
	me.runclui(commandlist, me.clui)

end

--[[ 
These are static variables, but they depend on static variables defined in other modules (function pointers and such).
Therefore they are initialised at onload(), not when the code is read.
]]
me.defineclui = function()

me.subclui = { }

me.subclui.version = 
{
	["description"] = 
	function()
		mod.out.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.", mod.release, mod.revision))
	end,
	["branches"] = 
	{
		{
			["command"] = "notify",
			["description"] = "Notifies users with an older version of the mod to upgrade.",
			["output"] = mod.net.versionnotify
		},
		{
			["command"] = "query",
			["description"] = "Asks everyone in the raid to report their mod version.",
			["output"] = mod.net.versionquery
		},
		{
			["command"] = "advertise",
			["description"] = "Will occasionally tell people who pull aggro and don't have the mod to get it. Run this command again to stop it.",
			["output"] = mod.net.toggleadvertise,
		},
	}
}

-- GUI commands. Most commands would be fairly redundany, because you can just use the GUI, after all.
-- You can use this to bring up the window if it has been closed, or reset it completely if you have lost it.
me.subclui.gui = 
{	
	["description"] = nil,
	["branches"] = 
	{
		{
			["command"] = "show",
			["description"] = "Shows the window.",
			["output"] = 
				function()
					KLHTM_SetVisible(true)
				end,
		},
		{
			["command"] = "hide",
			["description"] = "Hides the window.",
			["output"] = 
				function()
					KLHTM_SetVisible(false)
				end,
		},
		{
			["command"] = "reset",
			["description"] = "Puts the window back in the middle of the screen.",
			["output"] = KLHTM_ResetFrame,
		},
	}
}

me.subclui.test = 
{
	["description"] = nil,
	["branches"] = 
	{
		{
			["command"] = "talents",
			["description"] = "Prints out your talent points in any talents that affect your threat.",
			["output"] = mod.data.testtalents
		},
		{
			["command"] = "gear",
			["description"] = "Prints out the set pieces you are wearing for sets that affect your threat.",
			["output"] = mod.data.testitemsets
		},
		{
			["command"] = "threat",
			["description"] = "Prints out a variety of threat parameters.",
			["output"] = mod.my.testthreat
		},
		{
			["command"] = "time",
			["description"] = "Prints out processor time information.",
			["output"] = 
				function()
					mod.diag.printalldata("time", "Milliseconds, or Milliseconds per Second")
				end
		},
		{
			["command"] = "memory",
			["description"] = "Prints out memory usage information.",
			["output"] = 
				function()
					mod.diag.printalldata("memory", "Kilobytes, or Kilobytes per Second")
				end
		},
		{
			["command"] = "channel",
			["description"] = "Checks whether the communication channel is properly set up.",
			["output"] = function()
				
				local number
				local name
				local source
				
				number, name, source = mod.net.getchannel()
				
				if number == 0 then
					mod.out.print("The mod could not find a suitable channel!")
				
				else
					mod.out.print(string.format("You are using channel number %s, %s, from %s.", number, name, source))
					
				end
				
			end
		},
		{
			["command"] = "netlog",
			["description"] = "Information about the channel messages received.",
			["output"] = 
				function()
					local key
					local value
					
					for key, value in mod.netin.messagelog do
						mod.out.print(string.format("|cffffff00%s: |r%d bytes in %d messages (%d average).", key, value.bytes, value.count, math.floor(0.5 + value.bytes / math.max(value.count, 0))))						
					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 mod.my.states do
						mod.out.print(string.format("The state '%s' is '%s'. The last change was %s.", key, mod.out.booltostring(value.value), doformat(value.lastchange)))
					end
				end
		},
		{
			["command"] = "local",
			["description"] = "Check for localisations that are missing in your locale.",
			["output"] = mod.string.testlocalisation,
		},
	}
}

me.subclui.boss = 
{
	description = "To run these commands you must be a raid assistant or the group leader.",
	branches = 
	{
		{
			command = "report",
			description = "Make players notify you when their threat is changed by boss abilities.",
			output = mod.net.startspellreporting,
		},
		{
			command = "endreport",
			description = "Stop players reporting when their threat is changed by boss abilities.",
			output = mod.net.stopspellreporting,
		},
		{
			command = "setspell",
			description = "Change a parameter of a known boss ability.",
			
			output = function(firstvalue, allvalues)

				local value, errormessage = mod.net.checkspellvaluesyntax(allvalues)
				
				if errormessage then
					mod.out.print("|cffff8888Syntax: setspell <spellid> <bossid> <parameter> <value>")
					mod.out.print(errormessage)
					
				else
					-- set the value
					mod.net.setspellvalue(allvalues[1], allvalues[2], allvalues[3], allvalues[4])
				end
			end,
		},
		{
			command = "autotarget",
			description = "Clear the meter and set the master target automatically when you next target a world boss.",
			output = function()
				if mod.net.checkpermission() then
					mod.boss.starttrigger("autotarget")
					mod.out.print(mod.string.get("print", "boss", "autotargetstart"))
				end
			end,
		},
	}
}

me.subclui.autohide = 
{
	description = function()	
		return "When enabled, the window will be shown when you join a party or raid, and it will be hidden when you leave. The current setting is |cffffff00" .. tostring(KLHTM_SavedVariables.autohide) .. "|r."
	end,
	
	branches = 
	{
		{
			command = "true",
			description = "Enable the autohide behaviour.",
			output = function()
				KLHTM_SavedVariables.autohide = true
				mod.out.print(mod.string.get("print", "table", "autohideon"))
			end
		},
		{
			command = "false",
			description = "Stop the autohide behaviour happening.",
			output = function()
				KLHTM_SavedVariables.autohide = false
				mod.out.print(mod.string.get("print", "table", "autohideoff"))
			end
		}
	}
}

me.clui =
{
	["description"] = nil,
	["branches"] = 
	{
		{
			["command"] = "test",
			["description"] = "A set of debugging commands.",
			["output"] = me.subclui.test
		},
		{
			["command"] = "gui",
			["description"] = "Commands to show the window.",
			["output"] = me.subclui.gui
		},
		{
			["command"] = "version",
			["description"] = "Commands to check and upgrade the version of other users.",
			["output"] = me.subclui.version,
		},
		{
			["command"] = "disable",
			["description"] = "Emergency stop: disables events / onupdate.",
			["output"] = function()
				if mod.isenabled == false then
					mod.out.print("The mod is already disabled. Run the 'enable' command to restart it.")
					
				else
					mod.isenabled = false
					mod.out.print("The mod has been disabled, and won't work until you run the 'enable' command.")
				end
			end
		},
		{
			["command"] = "enable",
			["description"] = "Restart the mod after an emergency stop.",
			["output"] = function()
				if mod.isenabled == true then
					mod.out.print("The mod is already running.")
					
				else
					mod.isenabled = true
					mod.out.print("The mod has been restarted, and will now receive events / onupdate.")
				end
			end
		},
		{
			["command"] = "mastertarget",
			["description"] = "Set or clear the Master Target.",
			["output"] = function()
				if UnitExists("target") then
					mod.net.sendmastertarget()
				else
					mod.net.clearmastertarget()
				end
			end
		},
		{
			["command"] = "resetraid",
			["description"] = "Reset the threat of everyone in the raid group.",
			["output"] = mod.net.clearraidthreat,
		},
		{
			["command"] = "boss",
			["description"] = "Functions to work out and set boss abilities.",
			["output"] = me.subclui.boss,
		},
		{
			["command"] = "autohide",
			["description"] = "Automatically show the window in a raid and hide when you leave.",
			["output"] = me.subclui.autohide,
		},
	},
}

end
