
-- table setup
local me = thismod

--[[
Framework\Core.lua

This file is the backbone of the addon. It provides <OnEvent>, <OnUpdate> and <OnLoad> services, and mediates Printing, Saved Variables, and Diagnostics, and other optional services.

To become a module of the addon, add a table containing all the code of your addon to the <thismod> variable. The top of the file should look like this:

		-- module setup
		local me = { name = "spell"}
		local mod = thismod
		mod[me.name] = me
	
The only thing that changes from file to file is the part in inverted commas, "spell" here. That is the name of your module. Other modules would reference you by that name, e.g. <mod.spell> . As a result, the name has to be unique among all files.

The following services are provided by Core.lua

1) OnLoad handler. Implement a method called "onload" in your module and it will get called when the OnLoad handler runs. e.g.

		me.onload = function()
			(stuff)
		end

Note that the order in which OnLoad handlers are run on modules is not defined. Having said that:

2) OnLoadComplete handler. Implement a method called "onloadcomplete" in your module and it will get called after the OnLoad handlers have been run on every module. This is useful if your module's initialisation code requires another module to be initialised, but in general try to avoid this.

3) OnUpdate handler. Implement a method called "onupdate" in your module and it will get called on every OnUpdate trigger. Note that OnUpdates will not run until after all OnLoadComplete handlers have run.

Alternatively, if you have a number of different methods that you want called periodically with different, specific periods, implement a table "myonupdates" as in this example

		me.myonupdates = 
		{
			updatescores = 0.0,
			updatenames = 0.5,
		}
		
		me.updatescores = function()
			(...)
		end
		
		me.updatenames = function()
			(...)
		end
		
The keys of the table must match functions in your module. The values are the minimum number of seconds allowed between subsequent method calls. 

4) OnEvent handler. First implement a list of strings "myevents" with the names of the events you want, e.g.

		me.myevents = { "CHAT_MSG_SPELL_SELF_BUFF", "CHAT_MSG_SYSTEM", }
	
Then implement a method "onevent", e.g.

		me.onevent = function()
			(...)
		end
		
No arguments will be passed to this method, so you'll have to refer to the global variables event, arg1, arg2, etc. Make sure not to edit them, since other addons might need them!

5) Saved Variables Service. Implement a table "save" with anything you want maintained when the user logs out in key-value pairs, e.g.

		me.save = 
		{
			hits = 100,
			moves = 
			{
				physical = 42,
				spell = 20,
			},
		}

This table will be automatically saved. You should put the default values into this table. See Framework\SavedVariables.lua for more info.

6) [Optional] Net Message Service. Your module can be informed of specific addon messages sent by other players with this addon. Implement a table <me.mynetmessages> with a list of commands you want and a function <me.onnetmessage> to receive them, e.g.

		me.mynetmessages = { "t", "clear", "bossevent" }
	
		me.onnetmessage = function(author, command, data)
			
			if messagetype = "clear" then
				...
		end
		
See Services\NetIn.lua for more info.

7) [Optional] Parser Service. This is like an OnEvent service, but you pick up specific combat log messages that are parsed for you. Implement a table <me.myparsers> with descriptions of the events you want to receive, and a callback method <me.onparse>, like so:
		
		me.myparsers = 
		{
			{"powergain", "POWERGAINSELFSELF", "CHAT_MSG_SPELL_SELF_BUFF"},
			{"healonself", "HEALEDCRITSELFSELF", "CHAT_MSG_SPELL_SELF_BUFF"},
			{"healonself", "HEALEDSELFSELF", "CHAT_MSG_SPELL_SELF_BUFF"},
		}
		
		me.onparse = function(identifier, ...)			
			...
		end
		
See Services\Regex.lua for more info.

8) Print Service. The method <mod.print(message, [...])> exists. See below in Core.lua.
]]


me.events = { } --[[ 
Remember which module wants which events.
It will look like
{
	["combat"] = 
	{
		["CHAT_MSG_SPELL_SELF_BUFF"] = true,
	},
}
if the "combat" module has registered the "CHAT_MSG_SPELL_SELF_BUFF" event
]]

me.onupdates = { }
--[[
Remembers which module's methods will update when. This is only for modules that have a <myonupdates> table.
It will look like
{
	["combat"] = 
	{
		updatedamage = 
		{
			lastupdate = 1234.203,
			interval = 0.4,
		},
		updatehealth = 
		{
			lastupdate = 1235.120,
			interval = 0.0,
		}
	}
}
if there is a method <mod.combat.updatedamage()> that should be called at most every 0.4 seconds, and a method <mod.combat.updatehealth()> that should be called every OnUpdate.
]]

me.isloaded = false -- true when .onload has been called for all sub-modules
me.isenabled = true -- iif false, onupdate and onevent will not be called

-- onload. Order is quite important here. Saved Variables first. OnLoad second. Then the rest.
me.onload = function()
	
	-- load saved variables
	me.save.loadsavedvariables()
	
	-- initialise all submodules
	for key, subtable in pairs(me) do
		if type(subtable) == "table" and subtable.onload and subtable.isenabled ~= "false" then
			subtable.onload()
		end
	end
	
	-- onloadcomplete
	for key, subtable in pairs(me) do
		if type(subtable) == "table" and subtable.onloadcomplete and subtable.isenabled ~= "false" then
			subtable.onloadcomplete()
		end
	end
	
	-- Setup NetIn service, if it exists
	if me.netin then
		for key, subtable in pairs(me) do
			if type(subtable) == "table" then 
				
				-- check for .mynetmessages / onnetmessage mismatch
				if subtable.mynetmessages and not subtable.onnetmessage then
					if me.trace.check("warning", me, "events") then
						me.trace.print(string.format("The module |cffffff00%s|r has a |cffffff00.mynetmessages|r list but no |cffffff00.onnetmessage|r function.", key))
					end
					
				elseif subtable.onnetmessage and not subtable.mynetmessages then
					if me.trace.check("warning", me, "events") then
						me.trace.print(string.format("The module |cffffff00%s|r has a |cffffff00.onnetmessage|r function but no |cffffff00.mynetmessages|r list.", key))
					end
				
				-- normal modules: if they have both
				elseif subtable.onnetmessage then
					
					for _, messagetype in pairs(subtable.mynetmessages) do
						
						me.netin.registermessagetype(key, messagetype)
						
					end
				end
			end
		end
	end
	
	-- Setup Regex service, if it exists
	if me.regex then
		for key, subtable in pairs(me) do
			if type(subtable) == "table" then 
				
				-- check for .myparsers / onparse mismatch
				if subtable.myparsers and not subtable.onparse then
					if me.trace.check("warning", me, "events") then
						me.trace.print(string.format("The module |cffffff00%s|r has a |cffffff00.myparsers|r list but no |cffffff00.onparse|r function.", key))
					end
					
				elseif subtable.onparse and not subtable.myparsers then
					if me.trace.check("warning", me, "events") then
						me.trace.print(string.format("The module |cffffff00%s|r has a |cffffff00.onparse|r function but no |cffffff00.myparsers|r list.", key))
					end
				
				-- normal modules: if they have both
				elseif subtable.myparsers then
					
					for _, data in pairs(subtable.myparsers) do
						
						me.regex.registerparser(key, unpack(data))
						
					end
				end
			end
		end
	end	
	
	me.isloaded = true 
	
	-- register events. Strictly after all modules have been loaded.
	for key, subtable in pairs(me) do
		if type(subtable) == "table" then
			
			-- check for .onevent / .myevents mismatch
			if subtable.myevents and not subtable.onevent then
				if me.trace.check("warning", me, "events") then
					me.trace.print(string.format("The module |cffffff00%s|r has a |cffffff00.myevents|r list but no |cffffff00.onevent|r function.", key))
				end
				
			elseif subtable.onevent and not subtable.myevents then
				if me.trace.check("warning", me, "events") then
					me.trace.print(string.format("The module |cffffff00%s|r has a |cffffff00.onevent|r function but no |cffffff00.myevents|r list.", key))
				end
			
			-- normal modules: if they have both
			elseif subtable.myevents then
						
				for _, event in pairs(subtable.myevents) do
					
					-- first level table is by the event
					if me.events[event] == nil then
						me.frame:RegisterEvent(event)
						me.events[event] = { }
					end
					
					-- second level table is by frame (value always non-nil)
					me.events[event][key] = true
				end
			end
		end
	end
	
	-- register onupdates.
	for key, subtable in pairs(me) do
		if type(subtable) == "table" and subtable.myonupdates then
			
			me.onupdates[key] = { }
			
			for method, interval in pairs(subtable.myonupdates) do
				
				if (subtable[method] == nil) or (type(subtable[method]) ~= "function") then
					-- the module has not defined the method it promised
					if me.trace.check("error", me, "onupdates") then
						me.trace.print(string.format("The function |cffffff00%s|r in the onupdate list of the module |cffffff00%s|r is not defined.", method, key))
					end
					
				else
					me.onupdates[key][method] = 
					{
						lastupdate = 0,
						["interval"] = interval,
					}
				end
			end
		end
	end
		
	-- Print load message
	me.print(string.format(me.string.get("print", "main", "startupmessage"), me.global.release, me.global.revision), true, nil)
		
end

-- OnUpdate
me.onupdate = function()
		
	-- only call when everything has been loaded
	if me.isloaded ~= true then
		return
	end
	
	-- don't call if the entire addon is disabled
	if me.isenabled == false then
		return
	end
	
	-- call all onupdates
	for key, subtable in pairs(me) do
		if type(subtable) == "table" and subtable.onupdate and subtable.isenabled ~= "false" then
			me.diag.logmethodcall(key, "onupdate")
		end
	end
	
	-- call all timed update methods
	local module, moduledata, functiondata
	local timenow = GetTime()
	
	for module, moduledata in pairs(me.onupdates) do
		 
		for functionname, functiondata in pairs(moduledata) do
			if timenow > functiondata.lastupdate + functiondata.interval then
				me.diag.logmethodcall(module, "onupdate", me[module][functionname])
				functiondata.lastupdate = timenow
			end
		end
	end
	
end

-- OnEvent
me.onevent = function()
 
	-- load up the mod if the event is ADDON_LOADED
	if (event == "ADDON_LOADED") and (me.tryloaded == nil) then
		
		me.frame:UnregisterEvent("ADDON_LOADED")
		me.tryloaded = true
		
		local success, message = pcall(me.onload)
		
		if not success then
			me.print("|cffff0000The mod failed to load properly!")
			_ERRORMESSAGE(message)
		end
		
		return
	end

	-- don't call if the entire addon is disabled
	if me.isenabled == false then
		return
	end

	-- get all the modules that have registered this event
	local keylist = me.events[event]
	local subtable
		
	for key, _ in pairs(keylist) do
		subtable = me[key]
		
		if subtable.isenabled ~= false then
			me.diag.logmethodcall(key, "onevent")
		end
	end
	
end

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

	-- 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 = me.global.printheader .. message 
	end
	
	-- write
	chatframe:AddMessage(message)

end

-- frame stuff
me.frame = CreateFrame("Frame", string.upper(me.namespace) .. "_Frame", UIParent)
me.frame:Show()

me.frame:SetScript("OnEvent", me.onevent)
me.frame:SetScript("OnUpdate", me.onupdate)

-- capture the ADDON_LOADED event for our mod and call me.onload()
me.frame:RegisterEvent("ADDON_LOADED")

-- KLHBL compatability
me.frame.klhblignore = true