﻿
local mod = klhtm
local me = { }
mod.netin = me

me.myevents = { "CHAT_MSG_CHANNEL", "CHAT_MSG_SYSTEM", "CHAT_MSG_ADDON" }

-- Special onevent function from Core.lua
me.onevent = function()

	if event == "CHAT_MSG_ADDON" then
		
		-- check the message is KLHTM, and comes from the party or raid
		if (arg1 ~= "KLHTM") or ((arg3 ~= "PARTY") and (arg3 ~= "RAID")) then
			me.logmessage("nonklhtm", string.len(arg1))
			return 
		end
		
		-- ok
		me.messagein(arg4, arg2, 1)

	elseif event == "CHAT_MSG_CHANNEL" then
		
		-- ignore messages outside our channel
		if arg8 ~= mod.net.channelnumber then
			return 
		end
		
		-- ignore non-KLHTM messages
		if string.sub(arg1, 1, 6) ~= "KLHTM " then
			me.logmessage("nonklhtm", string.len(arg1))
			return 
		end
		
		-- ignore messages from people outside the raid group
		if mod.unit.isplayeringroup(arg2) == nil then
			me.logmessage("nonklhtm", string.len(arg1))
			return 
		end
		
		-- remove " ...hic!" suffix, and localisations
		local start
		local finish
		start, finish, body = string.find(arg1, "(.-) ?%.%.%..+")
		
		if finish == string.len(arg1) then
			if mod.out.checktrace("info", me, "drunk") then
				mod.out.printtrace(string.format("Trimming <drunk> from this message: %s", arg1))
			end
			arg1 = body
		end
		
		-- to get here, it's fine
		me.messagein(arg2, arg1, 7)
	
	elseif event == "CHAT_MSG_SYSTEM" then
		
		-- in 0.12 and higher, the mod is sending ADDON_MESSAGEs, so we don't care about afk
		if mod.isnewwowversion == nil then
			if string.find(arg1, string.format(MARKED_AFK_MESSAGE, "")) then
				mod.my.setstate("afk", true)
					
			elseif string.find(arg1, CLEARED_AFK) then
				mod.my.setstate("afk", false)
			end
		end
	else
		
		return
	end

end

--[[ 
me.messagein(author, message, startindex)
Processes a message from the chat channel.
<author> is the name of the player who sent the message.
<message> is the original string sent.
<startindex> is the 1-based string index where the message starts.
]]
me.messagein = function(author, message, startindex)
	
	-- get first bit and rest
	local command
	local data
	
	_, _, command, data = string.find(message, "(%a+)(.*)", startindex)
	
	if (command == nil) or (me.commands[command] == nil) then
		
		-- log as invalid
		me.logmessage("invalid", string.len(message))
		
		if mod.out.checktrace("warning", me, "badmessage") then
			mod.out.printtrace(string.format("Received the invalid message '|cffffff00%s|r' from %s.", message, author))
		end
		
	else
		if me.commands[command](author, message, startindex + string.len(command)) then
			-- log as good
			me.logmessage("good", string.len(message))
		else
			-- log as invalid
			me.logmessage("invalid", string.len(message))
			
			if mod.out.checktrace("warning", me, "badmessage") then
				mod.out.printtrace(string.format("Received the invalid message '|cffffff00%s|r' from %s.", message, author))
			end
		end
	end
	
end

--[[ 
me.officercheck(author, message)
Checks if the author of an officer-only command is an officer. Otherwise, prints an error to trace.
<author> is the name of the player who sent the message.
<message> is the complete text of the message.
Returns: non-nil iff <author> is an officer.
]]
me.officercheck = function(author, message)
	
	if mod.unit.isplayerofficer(author) then
		return true
	
	else
		if mod.out.checktrace("warning", me, "badmessage") then
			mod.out.printtrace(string.format("|cffffff00%s|r is not an officer, but sent the message |cffffff00%s|r.", author, message))
		end
		
		return nil
	end

end

--[[
each function is called with 1) author, 2) message, 3) index of first character after the command (probably a space)
they should return non-nil if the message was good, nil otherwise
]]
me.commands = 
{
	-- clearing the raid threat
	["clear"] = function(author, message)
		
		-- author must be officer
		if me.officercheck(author, message) then
			
			mod.out.print(string.format(mod.string.get("print", "network", "threatreset"), author))
			
			mod.table.resetraidthreat()
			mod.table.clearraidtable()
			
			return true
		end
		
	end,
	
	-- updating your threat value
	["threat"] = function(author, message, start)
	
		local value = tonumber(string.sub(message, start + 1))
		
		-- check for validity
		if value == nil then 
			return
		end
	
		mod.table.updateplayerthreat(author, value)
		KLHTM_RequestRedraw("raid")
		
		return true 
		
	end,
	
	-- 1.12: "t" is a synonym for "threat"
	["t"] = function(author, message, start)
		
		local value = tonumber(string.sub(message, start + 1))
		
		-- check for validity
		if value == nil then 
			return
		end
	
		mod.table.updateplayerthreat(author, value)
		KLHTM_RequestRedraw("raid")
		
		return true 
		
	end,
	
	-- setting the master target
	["target"] = function(author, message, start)
	
		-- check player is an officer
		if me.officercheck(author, message) then
			
			local newmt = string.sub(message, start + 1)
			-- check newmt makes sense
			
			if newmt ~= nil and string.len(newmt) > 0 then	
				mod.boss.newmastertarget(author, newmt)
				KLHTM_RequestRedraw("raid")
				
				-- update poll values
				mod.net.lastmtstring = newmt
				mod.net.lastmtsender = author
				mod.net.lastmttime = GetTime()
				
				return true
			end
		end
	end,

	-- polling the master target
	["mtpoll"] = function(author, message, start)
		
		-- check player is an officer
		if me.officercheck(author, message) then
		
			local newmt = string.sub(message, start + 1)
			-- check newmt makes sense
			
			if newmt ~= nil and string.len(newmt) > 0 then
				
				-- check if this is different
				if newmt ~= mod.net.lastmtstring then
					
					-- set the string as MT, but with a caution
					mod.boss.mastertarget = newmt
					mod.out.print(string.format(mod.string.get("print", "network", "mtpollwarning"), newmt, author))
					KLHTM_RequestRedraw("raid")
					
					-- update poll values
					mod.net.lastmtstring = newmt
					mod.net.lastmtsender = author
					mod.net.lastmttime = GetTime()
					
				end
				
				-- message was valid so return true
				return true
			end
			
		end
		
	end,

	-- clearing the master target
	["cleartarget"] = function(author, message)
		
		-- check player is an officer
		if me.officercheck(author, message) then
			
			-- ignore polls / repeated clears
			if mod.net.lastmtstring == "clear" then
				-- do nothing
				
			else
				mod.boss.clearmastertarget()
				mod.out.print(string.format(mod.string.get("print", "network", "mtclear"), author))
				KLHTM_RequestRedraw("raid")
		
				-- update poll values
				mod.net.lastmtstring = "clear"
				mod.net.lastmtsender = author
				mod.net.lastmttime = GetTime()
				
			end
			
			-- message was valid so return true
			return true
		end
	end,
	
	-- starting knockback discovery
	["spellstart"] = function(author, message)
		
		-- check author is an officer
		if me.officercheck(author, message) == nil then
			return
		end
		
		mod.boss.isspellreportingactive = true
		
		if author == UnitName("player") then
			mod.boss.istrackingspells = true
		end
		
		-- only print out if you are a tracker
		if mod.boss.istrackingspells == true then
			mod.out.print(string.format(mod.string.get("print", "network", "knockbackstart"), author))
		end
		
		return true
		
	end,
	
	-- stopping knockback discovery
	["spellstop"] = function(author, message)
		
		-- check author is an officer
		if me.officercheck(author, message) == nil then
			return
		end
		
		mod.boss.isspellreportingactive = false
	
		if mod.boss.istrackingspells == true then
			mod.out.print(string.format(mod.string.get("print", "network", "knockbackstop"), author))
		end
		
		return true
		
	end,
	
	-- When boss spell reporting is enabled and someone suffers a boss spell
	["spelleffect"] = function(author, message, start)

		-- if we aren't monitoring reports, ignore
		if mod.boss.istrackingspells == false then
			return true -- we don't actually check correctness.
		end
		
		local _, _, spell, mob, result, data = string.find(message, "\"(.+)\" \"(.*)\" (%l+) ?(.*)", start + 1)
		
		if spell == nil then 
			return
		end
		
		if result == "miss" then
			mod.out.print(string.format(mod.string.get("print", "boss", "reportmiss"), author, mob, spell))
			
		else
			local value1, value2
			
			_, _, value1, value2 = string.find(tostring(data), "(-?%d+) (-?%d+)")
						
			if (tostring(number) == nil) or (tostring(number) == nil) then
				return
			end
			
			if result == "proc" then
				mod.out.print(string.format(mod.string.get("print", "boss", "reportproc"), author, mob, spell, value1, value2))
				
			elseif result == "tick" then
				mod.out.print(string.format(mod.string.get("print", "boss", "reporttick"), author, mob, spell, value1, value2))
				
			else
				return
			end
		end
		
		return true
	end,
	
	-- when someone changes a parameter of a boss spell
	["spellvalue"] = function(author, message, start)
		
		-- check author is an officer
		if me.officercheck(author, message) == nil then
			return
		end
		
		-- argh!! Memory creation! 64 bytes for this method! ... 
		local arglist = {}
		local x
		
		for x in string.gfind(message, "[^ ]+") do
			table.insert(arglist, x)
		end
		
		table.remove(arglist, 1)
		
		-- parse
		local value, errormsg = mod.net.checkspellvaluesyntax(arglist)
		
		-- did it work?
		if value then
			if arglist[2] == "default" then
				
				--[[ 
				suppose we receive the message "spellvalue timelapse default ticks 6". Then this would print out
					"Kenco sets the ticks parameter of the Time Lapse ability to 6."
				]]
				mod.out.print(string.format(mod.string.get("print", "boss", "spellsetall"), author, "|cffffff00" .. arglist[3] .. "|r", mod.string.get("boss", "spell", arglist[1]), value, mod.boss.bossattacks[arglist[1]][arglist[2]][arglist[3]]))
			
			else				
				mod.out.print(string.format(mod.string.get("print", "boss", "spellsetmob"), author, "|cffffff00" .. arglist[3] .. "|r", mod.string.get("boss", "spell", arglist[2]), mod.string.get("boss", "spell", arglist[1]), value, mod.boss.bossattacks[arglist[1]][arglist[2]][arglist[3]]))
			end
			
			mod.boss.bossattacks[arglist[1]][arglist[2]][arglist[3]] = value
		
			return true
		else
			
			if mod.out.checktrace("warning", me, "spellvalue") then
				mod.out.printtrace(string.format("The error message was '%s'.", errormsg))
			end
			
			return
		end
	end,
	
	-- telling people to upgrade to a new version
	["version"] = function(author, message, start)
		
		-- check author is an officer
		if me.officercheck(author, message) == nil then
			return
		end
		
		-- next argument should be the version number
		local release, revision
		_, _, release, revision = string.find(message, "(%d+)%.?(%d*)", start + 1)
		
		release = tonumber(release)
		revision = tonumber(revision)
		
		if release == nil then
			return
		end
		
		-- previously, only the release was sent as the version code. Therefore if someone sends in this format, 
		-- their version must be old.
		if revision == nil then

			mod.out.print(string.format(mod.string.get("print", "network", "remoteoldversion"), author, release .. ".x", mod.release .. "." .. mod.revision))
		
		else
		
			-- newer versions send their release and revision numbers, in a dot delimited string.			
			if (release < mod.release) or (release == mod.release and revision < mod.revision) then
				-- other guy has an old version
				mod.out.print(string.format(mod.string.get("print", "network", "remoteoldversion"), author, release .. "." .. revision, mod.release .. "." .. mod.revision))
				
			elseif (release == mod.release) and (revision == mod.revision) then
				-- we have the same version; do nothing
				
			else
				-- we have an older version - upgrade!
				mod.out.print(string.format(mod.string.get("print", "network", "upgraderequest"), author, release .. "." .. revision, mod.release .. "." .. mod.revision))
			end
		end
		
		return true
		
	end,
	
	-- asking the raid group what versions they are using
	["versionquery"] = function(author, message)
		
		-- check author is an officer
		if me.officercheck(author, message) == nil then
			return
		end
	
		-- send our version
		mod.net.sendmessage("versionresponse " .. mod.release .. "." .. mod.revision)
		
		return true
		
	end,
	
	-- response to versionquery
	["versionresponse"] = function(author, message, start)
		
		-- next argument should be the version number
		local value = tonumber(string.sub(message, start + 1))
		if value == nil then
			return
		end
		
		mod.net.addversionresponse(author, value)		
		return true
		
	end,
	
	-- Boss Spell Event
	["event"] = function(author, message, start)
		
		local eventid = string.sub(message, start + 1)
		
		-- check the event is valid
		if (eventid == nil) or (mod.boss.bossevents[eventid] == nil) then
			return
		end
		
		-- it is valid. send it to boss handler
		mod.boss.reportevent(eventid, author)
		return true
		
	end
	
}
	

------------------------------------------------------------------------

---------------------------------
--   lololol Message Logging   --
---------------------------------

me.messagelog = 
{
	["nonklhtm"] = 
	{
		["count"] = 0,
		["bytes"] = 0,
	},
	["invalid"] = 
	{
		["count"] = 0,
		["bytes"] = 0,
	},
	["good"] = 
	{
		["count"] = 0,
		["bytes"] = 0,
	},
	["total"] = 
	{
		["count"] = 0,
		["bytes"] = 0,
	},
}

--[[ 
me.logmessage(category, length)
Record a message from the mod's chat channel.
<category> is one of the keys in me.messagelog.
<length> is the lenght in bytes of the complete message.
]]
me.logmessage = function(category, length) 

	me.messagelog[category].count = me.messagelog[category].count + 1
	me.messagelog[category].bytes = me.messagelog[category].bytes + length

	me.messagelog["total"].count = me.messagelog["total"].count + 1
	me.messagelog["total"].bytes = me.messagelog["total"].bytes + length

end
