TIMERS_VERSION = "1.05"

TIMERS_THINK = 0.01

if Timers == nil then
	print ( '[Timers] creating Timers' )
	Timers = {}
	setmetatable(Timers, {
		__call = function(t, ...)
			return t:CreateTimer(...)
		end
	})
	--Timers.__index = Timers
end

function Timers:start()
	Timers = self
	self.timers = {}

	local ent = SpawnEntityFromTableSynchronous("info_target", {targetname="timers_lua_thinker"})
	ent:SetThink("Think", self, "timers", TIMERS_THINK)
end

function Timers:Think()
	--if GameRules:State_Get() >= DOTA_GAMERULES_STATE_POST_GAME then
		--return
	--end

	-- Track game time, since the dt passed in to think is actually wall-clock time not simulation time.
	local now = GameRules:GetGameTime()

	-- Process timers
	for k,v in pairs(Timers.timers) do
		local bUseGameTime = true
		if v.useGameTime ~= nil and v.useGameTime == false then
			bUseGameTime = false
		end
		local bOldStyle = false
		if v.useOldStyle ~= nil and v.useOldStyle == true then
			bOldStyle = true
		end

		local now = GameRules:GetGameTime()
		if not bUseGameTime then
			now = Time()
		end

		if v.endTime == nil then
			v.endTime = now
		end
		-- Check if the timer has finished
		if now >= v.endTime then
			-- Remove from timers list
			Timers.timers[k] = nil

			Timers.runningTimer = k
			Timers.removeSelf = false

			-- Run the callback
			local status, nextCall
			if v.context then
				status, nextCall = xpcall(function() return v.callback(v.context, v) end, function (msg)
																		return msg..'\n'..debug.traceback()..'\n'
																	end)
			else
				status, nextCall = xpcall(function() return v.callback(v) end, function (msg)
																		return msg..'\n'..debug.traceback()..'\n'
																	end)
			end

			Timers.runningTimer = nil

			-- Make sure it worked
			if status then
				-- Check if it needs to loop
				if nextCall and not Timers.removeSelf then
					-- Change its end time

					if bOldStyle then
						v.endTime = v.endTime + nextCall - now
					else
						v.endTime = v.endTime + nextCall
					end

					Timers.timers[k] = v
				end

				-- Update timer data
				--self:UpdateTimerData()
			else
				-- Nope, handle the error
				Timers:HandleEventError('Timer', k, nextCall)
			end
		end
	end

	return TIMERS_THINK
end

function Timers:HandleEventError(name, event, err)
	print(err)

	-- Ensure we have data
	name = tostring(name or 'unknown')
	event = tostring(event or 'unknown')
	err = tostring(err or 'unknown')

	-- Tell everyone there was an error
	--Say(nil, name .. ' threw an error on event '..event, false)
	--Say(nil, err, false)

	-- Prevent loop arounds
	if not self.errorHandled then
		-- Store that we handled an error
		self.errorHandled = true
	end
end

function Timers:CreateTimer(name, args, context)
	if type(name) == "function" then
		if args ~= nil then
			context = args
		end
		args = {callback = name}
		name = DoUniqueString("timer")
	elseif type(name) == "table" then
		args = name
		name = DoUniqueString("timer")
	elseif type(name) == "number" then
		args = {endTime = name, callback = args}
		name = DoUniqueString("timer")
	end
	if not args.callback then
		print("Invalid timer created: "..name)
		return
	end


	local now = GameRules:GetGameTime()
	if args.useGameTime ~= nil and args.useGameTime == false then
		now = Time()
	end

	if args.endTime == nil then
		args.endTime = now
	elseif args.useOldStyle == nil or args.useOldStyle == false then
		args.endTime = now + args.endTime
	end

	args.context = context

	Timers.timers[name] = args

	return name
end

function Timers:RemoveTimer(name)
	Timers.timers[name] = nil
	if Timers.runningTimer == name then
		Timers.removeSelf = true
	end
end

function Timers:RemoveTimers(killAll)
	local timers = {}
	Timers.removeSelf = true

	if not killAll then
		for k,v in pairs(Timers.timers) do
			if v.persist then
				timers[k] = v
			end
		end
	end

	Timers.timers = timers
end

if not Timers.timers then Timers:start() end

-- Attach to Gamerules
GameRules.Timers = Timers

