--[[
	MOD/lua/bin/lua/client/libs/glib/timer.lua
	Joseph | STEAM_0:0:69348930 <2.26.87.183:27005> | [19-10-13 05:31:41PM]
	===BadFile===
]]

/* * * * * * * * * * * * * * * * 
 * 	 ______     __  __     ______     __  __     ______     ______     __  __    
 *	/\  __ \   /\ \/\ \   /\  == \   /\ \_\ \   /\  __ \   /\  ___\   /\ \/ /    
 *	\ \ \/\ \  \ \ \_\ \  \ \  __<   \ \  __ \  \ \  __ \  \ \ \____  \ \  _"-.  
 *	 \ \_____\  \ \_____\  \ \_____\  \ \_\ \_\  \ \_\ \_\  \ \_____\  \ \_\ \_\ 
 *	  \/_____/   \/_____/   \/_____/   \/_/\/_/   \/_/\/_/   \/_____/   \/_/\/_/ 
 *
 *  - Version : 3L
 *  - By Oubliette
 * * * * * * * * * * * * * * * */

--
local arg = { ... };
local g = arg[ 1 ]
--

local timer = g.table.Copy( timer or {} );

--[[---------------------------------------------------------
   Name: timer
   Desc: A module implementing timed execution of functions.
-----------------------------------------------------------]]

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

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

local function CreateTimer( name )

	if ( Timer[name] == nil ) then
		Timer[name] = {}
		Timer[name].Status = STOPPED
		return true
	end

	return false

end

-- Timer class stuff.

local TIMER_CLASS = {__index = TIMER_CLASS};

function TIMER_CLASS.New()
	local object = {};
	
		g.setmetatable( object, TIMER_CLASS );
		TIMER_CLASS.__index = TIMER_CLASS;
		
	return object;
end;

function TIMER_CLASS:IsRunning()
	if ( !Exists( self.name ) ) then return false; end;
	
	return ( Timer[self.name].Status == RUNNING );
end;

function TIMER_CLASS:IsPaused()
	if ( !Exists( self.name ) ) then return false; end;
	
	return ( Timer[self.name].Status == PAUSED );
end;

function TIMER_CLASS:GetReps()
	if ( !Exists( self.name ) ) then return 0; end;
	
	return Timer[self.name].Repetitions;
end;

function TIMER_CLASS:GetDelay()
	if ( !Exists( self.name ) ) then return 0; end;
	
	return Timer[self.name].Delay;
end;

function TIMER_CLASS:GetRepsLeft()
	if ( !IsRunning() ) then return -1; end;
	if ( Timer[self.name].Repetitions == 0 ) then return 0; end;
	
	return ( Timer[self.name].Repetitions - Timer[self.name].n );
end;

function TIMER_CLASS:GetNextCallTime()
	if ( !IsRunning() ) then return -1; end;
	
	return ( Timer[self.name].Last + Timer[self.name].Delay ) - CurTime();
end;

function TIMER_CLASS:Destroy()
	if ( !Exists( self.name ) ) then return; end
	
	Destroy( self.name );
end;

function TIMER_CLASS:Pause()
	if ( !Exists( self.name ) ) then return; end
	
	Pause( self.name );
end;

function TIMER_CLASS:UnPause()
	if ( !Exists( self.name ) ) then return; end
	
	UnPause( self.name );
end;

function TIMER_CLASS:Toggle()
	if ( !Exists( self.name ) ) then return; end
	
	Toggle( self.name );
end;

function TIMER_CLASS:Stop()
	if ( !Exists( self.name ) ) then return; end
	
	Stop( self.name );
end;
	
--[[---------------------------------------------------------
   Name: Exists( name )
   Desc: Returns boolean whether or !name is a timer.
-----------------------------------------------------------]]
function timer.Exists( name )

	return Timer[name] != nil

end

--[[---------------------------------------------------------
   Name: Create( name, delay, reps, func )
   Desc: Setup and start a timer by name.
-----------------------------------------------------------]]
function timer.Create( name, delay, reps, func, a, b, c )

	if ( !isfunction( func ) || a != nil || b != nil || c != nil || !isnumber( delay ) || !isnumber( reps ) ) then
		g.error( "timer.Create - called wrong!\n" )
		return
	end

	if ( timer.Exists( name ) ) then
		timer.Destroy( name )
	end
	
	timer.Adjust( name, delay, reps, func )
	timer.Start( name )

	local timerObj = TIMER_CLASS.New()
		timerObj.name = name
	return timerObj;
end

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

--[[---------------------------------------------------------
   Name: Adjust( name, delay, reps, func )
   Desc: Adjust a running, stopped or paused timer by name.
-----------------------------------------------------------]]
function timer.Adjust( name, delay, reps, func )
	CreateTimer( name )
	Timer[name].Delay = delay
	Timer[name].Repetitions = reps
	if ( func != nil ) then Timer[name].Func = func end
	return true;
end

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

--[[---------------------------------------------------------
   Name: UnPause( name )
   Desc: Unpause a paused timer by name.
-----------------------------------------------------------]]
function timer.UnPause( name )
	if ( !Exists( 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: Toggle( name )
   Desc: Toggle a timer's pause state by name.
-----------------------------------------------------------]]
function timer.Toggle( name )
	if ( timer.Exists( 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: Stop( name )
   Desc: Stop a running or paused timer by name.
-----------------------------------------------------------]]
function timer.Stop( name )
	if ( !timer.Exists( name ) ) then return false; end
	if ( Timer[name].Status != STOPPED ) then
		Timer[name].Status = STOPPED
		return true
	end
	return false
end

--[[---------------------------------------------------------
   Name: Check()
   Desc: Check all timers and complete any tasks needed.
		This should be run every frame.
-----------------------------------------------------------]]
function timer.Check()

	for key, value in g.pairs( Timer ) do

		-- The timer is not string, assume IsValid to work on it
		if ( !g.isstring( key ) && !g.IsValid( key ) ) then

			Timer[key] = nil

			continue

		end
	
		if ( value.Status == PAUSED ) then
		
			value.Last = g.CurTime() - value.Diff
			
		elseif ( value.Status == RUNNING && ( value.Last + value.Delay ) <= g.CurTime() ) then
				
			value.Last = g.CurTime()
			value.n = value.n + 1 
			
			if ( value.n >= value.Repetitions && value.Repetitions != 0) then
				timer.Stop( key )
			end

			value.Func(key)
				
		end
		
	end
	
	-- Run Simple timers
	for key, value in g.pairs( TimerSimple ) do

		if ( value.Finish <= g.CurTime() ) then
			
			g.table.remove( TimerSimple, key )
			
			value.Func()
						
		end
	end
	
end

--[[---------------------------------------------------------
   Name: Destroy( name )
   Desc: Destroy the timer by name and remove all evidence.
-----------------------------------------------------------]]
function timer.Destroy( name )
	Timer[ name ] = nil
end

timer.Remove = timer.Destroy

--[[---------------------------------------------------------
   Name: Simple( delay, func )
   Desc: Make a simple "create and forget" timer
-----------------------------------------------------------]]
function timer.Simple( delay, func, a, b, c )
	
	if ( !g.isfunction(func) || a != nil || b != nil || c != nil || !g.isnumber( delay ) ) then
		g.error( "timer.Simple - called wrong!\n" )
		return
	end

	local new_timer = {}
	
	new_timer.Finish	= g.UnPredictedCurTime() + delay
	new_timer.Func		= func 
	new_timer.Debug		= g.debug.traceback()
	
	g.table.insert( TimerSimple, new_timer )
	
	return true;
	
end

return timer;