-- The global timer library
timer = {}

-- Some definitions
local PAUSED = -1
local STOPPED = 0
local RUNNING = 1

-- Declare our locals
local Timer = {}
local TimerSimple = {}

---------- ---------- ---------- ---------- ---------- 
-- Name: CreateTimer( Name )
-- Desc: Internal function
---------- ---------- ---------- ---------- ---------- 
local function CreateTimer( Name )
	if not ( Timer[Name] ) then
		Timer[Name] = {}
		Timer[Name].Status = STOPPED
		
		return true
	end
	
	return false
end 

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.IsTimer( Name )
-- Desc: Returns boolean whether or not Name is a valid timer
---------- ---------- ---------- ---------- ---------- 
function timer.IsTimer( Name )
	if not ( Timer[Name] ) then 
		return false
	end
	
	return true
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Create( Name, Delay, Reps, Function, ... )
-- Desc: Setup and start a timer by Name
---------- ---------- ---------- ---------- ---------- 
function timer.Create( Name, Delay, Reps, Function, ... )
	if ( timer.IsTimer( Name ) ) then
		timer.Destroy( Name )
	end
	
	timer.Adjust( Name, Delay, Reps, Function, unpack( arg ) )
	timer.Start( Name )
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Start( Name )
-- Desc: (Re)start the timer by Name
---------- ---------- ---------- ---------- ---------- 
function timer.Start( Name )
	if not ( timer.IsTimer( Name ) ) then return false end
	
	Timer[Name].n = 0
	Timer[Name].Status = RUNNING
	Timer[Name].Last = CurTime()
	
	return true
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Adjust( Name, Delay, Reps, Function, ... )
-- Desc: Adjust a running, stopped or paused timer by Name
---------- ---------- ---------- ---------- ---------- 
function timer.Adjust( Name, Delay, Reps, Function, ... )
	CreateTimer( Name )
	Timer[Name].Delay = Delay
	Timer[Name].Repetitions = Reps
	
	if ( Function ) then 
		Timer[Name].Func = Function
	end
	
	if ( arg ) then 
		Timer[Name].Args = arg
	end
	
	return true
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Pause( Name )
-- Desc: Pause a running timer by Name
---------- ---------- ---------- ---------- ---------- 
function timer.Pause( Name )
	if not ( timer.IsTimer( Name ) ) then return false end
	
	if ( Timer[Name].Status == RUNNING ) then
		Timer[Name].Diff = CurTime() - Timer[Name].Last
		Timer[Name].Status = PAUSED
		
		return true
	end
	
	return false
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.UnPause( Name )
-- Desc: Unpause a paused timer by Name
---------- ---------- ---------- ---------- ---------- 
function timer.UnPause( Name )
	if not ( timer.IsTimer( Name ) ) then return false end
	
	if ( Timer[Name].Status == PAUSED ) then
		Timer[Name].Diff = nil
		Timer[Name].Status = RUNNING
		
		return true
	end
	
	return false
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Toggle( Name )
-- Desc: Toggle a timer's pause state by Name
---------- ---------- ---------- ---------- ---------- 
function timer.Toggle( Name )
	if ( timer.IsTimer( Name ) ) then
		if ( Timer[Name].Status == PAUSED ) then
			return timer.UnPause( Name )
		elseif ( Timer[Name].Status == RUNNING ) then
			return timer.Pause( Name )
		end
	end
	
	return false
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Stop( Name )
-- Desc: Stop a running or paused timer by Name
---------- ---------- ---------- ---------- ---------- 
function timer.Stop( Name )
	if not ( timer.IsTimer( Name ) ) then return false end
	
	if ( Timer[Name].Status ~= STOPPED ) then
		Timer[Name].Status = STOPPED
		return true
	end
	
	return false
end

---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Check()
-- Desc: Check all timers and complete any tasks needed
-- 	This is runned every frame
---------- ---------- ---------- ---------- ---------- 
function timer.Check()
	for k, v in pairs( Timer ) do
		if ( v.Status == PAUSED ) then
			v.Last = CurTime() - v.Diff
		elseif ( v.Status == RUNNING ) and ( ( v.Last + v.Delay ) <= CurTime() ) then
			v.Last = CurTime()
			v.n = v.n + 1
			
			local Success, Error = pcall( v.Func, unpack( v.Args ) )
			if not ( Success ) then
				print( "#1Timer '".. k .."' Error: " .. tostring( Error ) )
			end
			
			if ( v.n >= v.Repetitions ) and ( v.Repetitions ~= 0 ) then
				timer.Stop( k )
			end
		end
	end
	
	-- Run Simple timers
	for k, v in pairs( TimerSimple ) do
		if ( v.Finish <= CurTime() ) then
			local Success, Error = pcall( v.Func, unpack( v.Args ) )
			if not ( Success ) then
				print( "#1Simple Timer Error: " .. tostring( Error ) )
			end
			
			TimerSimple[k] = nil -- Kill timer
		end
	end
end 
  
---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Destroy( Name )
-- Name: timer.Remove( Name )
-- Desc: Destroy the timer by Name and remove all evidence
---------- ---------- ---------- ---------- ---------- 
function timer.Destroy( Name )
	Timer[Name] = nil
end
timer.Remove = timer.Destroy
  
---------- ---------- ---------- ---------- ---------- 
-- Name: timer.Simple( Delay, Function, ... )
-- Desc: Make a simple "create and forget" timer
---------- ---------- ---------- ---------- ---------- 
function timer.Simple( Delay, Function, ... )
	local Timer = {}
	Timer.Finish = CurTime() + Delay
	
	if ( Function ) then
		Timer.Func = Function
	end
	
	if ( arg ) then
		Timer.Args = arg
	end
	
	table.insert( TimerSimple, Timer )
	
	return true
end