#include <../source/TAL/objbase.h>
#include <../arch/arch.h>

/*
 * 22.09.2001 - rewrote timer code to avoid namespace collisions with GLIBC.
 * 22.01.2003 - more stuff: timerTime(), timerResume(), timerRate().
 * 04.09.2003 - bugfixes, predictable reactions and warnings in the case
 *  of incorrect calling sequencies.
 * 10.02.2004 - rewrote to use archTimeXXX() facilities.
 */

u32_t   V_GLOBAL
timeTickCount()
{
	return archTickCount();
}

void    V_GLOBAL
timeDelay(u32_t Milliseconds)
{
	archDelayExecution(Milliseconds);
}

typedef struct  libv_timer_s
{
	libv_object_t   base;
	vboolean        active;
	u32_t           start_time;
	u32_t           last_time;
	vec_t           rate;
}   libv_timer_t, *libv_timer_p;

#define LIBV_TIMER_MAGIC    0x44544D52  /* DTMR */
V_LOCAL char *libv_timer_object = "timer";

#define GET_TIMER(handle,caller) \
		(CHECK_AND_GET_OBJECT(libv_timer_t,handle,caller, \
			libv_timer_object,LIBV_TIMER_MAGIC))

V_LOCAL u32_t V_CALL
libv_timer_destroy(vhandle_t handle)
{
	libv_timer_p self=(libv_timer_p)handle;
	if(--self->base.ref_count)
		return self->base.ref_count;    /* still references */
	memFreeObject(self);
	return 0;
}

vhandle_t   V_GLOBAL
timerCreate(void)
{
	libv_timer_p self;
	ALLOC_OBJECT(self,libv_timer_t,0,LIBV_TIMER_MAGIC);
	self->base.destroy=libv_timer_destroy;
	self->rate = (vec_t)1e-3; /* seconds per millisecond */
	self->active = false;
	self->last_time = 0;
	self->start_time = archTickCount();
	return self;
}

void    V_GLOBAL
timerDestroy(vhandle_t timer)
{
	libvCloseHandle(timer,"timerDestroy",libv_timer_object,
		LIBV_TIMER_MAGIC);
}

/*
 * Sets timer to active state.
 * Subsequent calls of timer[Time,Milliseconds]() will return 
 * time elapsed _between_ timerStart() and these routines.
 */
void    V_GLOBAL
timerStart(vhandle_t timer)
{
	libv_timer_p self = GET_TIMER(timer,"timerStart");
	if(self == LIBV_INVALID_HANDLE)
		return;
	/*
	 * Calling timerStart() in active state isn't meaningless,
	 * though there is a clear way to restart: timerStop() and
	 * than timerStart().
	 */
#ifdef  CHECKED_BUILD
	if(self->active)
		conlPuts(LOG_TRACE,"timerStart(): called in active state.\n");
#endif  /* CHECKED_BUILD */
	self->active = true;
	self->last_time = self->start_time = archTickCount();
}

/*
 * Same as above, but subsequent calls of timerMilliseconds()
 * will return time elapsed since latest call to timerStart()
 * minus time between calls to timerStop() and timerResume().
 */
void    V_GLOBAL
timerResume(vhandle_t timer)
{
	u32_t cur_time;
	libv_timer_p self = GET_TIMER(timer,"timerResume");
	if(self == LIBV_INVALID_HANDLE)
		return;
	if(self->active)
	{
#ifdef  CHECKED_BUILD
		conlPuts(LOG_WARNING,
			"timerResume(): should be called after timerStop().\n");
#endif  /* CHECKED_BUILD */
		return;
	}
	self->active = true;
	if(self->last_time)
	{
		/* Timer was started */
		cur_time = archTickCount();
		self->start_time = cur_time - self->last_time;
		self->last_time = cur_time;
	}
	else
	{
#ifdef  CHECKED_BUILD
		conlPuts(LOG_WARNING,
			"timerResume(): shouldn't be called without timerStart().\n");
#endif  /* CHECKED_BUILD */
		self->last_time = self->start_time = archTickCount();
	}
}

void    V_GLOBAL
timerRate(vhandle_t timer,vec_t rate)
{
	u32_t gtc;
	vec_t nr;
#ifdef  CHECKED_BUILD
	static char *ptf = "past-to-future";
	static char *ftp = "future-to-past";
#endif  /* CHECKED_BUILD */
	libv_timer_p self = GET_TIMER(timer,"timerRate");
	if(self == LIBV_INVALID_HANDLE)
		return;
	nr = (vec_t)1e-3 * rate;
	gtc = archTickCount();
	if(nr * self->rate < 0)
	{
#ifdef  CHECKED_BUILD
		conlPrintf(LOG_WARNING,
			"timerRate(%g): restarting to adjoin %s with %s time streams.\n",
			rate, (self->rate > 0) ? ptf : ftp, (rate > 0) ? ptf : ftp);
#endif  /* CHECKED_BUILD */
		self->start_time = self->last_time = gtc;
	}
	else
	{
		self->start_time = (u32_t)(gtc - (self->rate / nr) * (gtc - self->start_time));
		self->last_time = gtc;
	}
	self->rate = nr;
}

/*
 * Sets timer to passive state: subsequent calls of timerMilliseconds()
 * will return time of this call.
 */ 
void    V_GLOBAL
timerStop(vhandle_t timer)
{
	libv_timer_p self = GET_TIMER(timer,"timerStop");
	if(self == LIBV_INVALID_HANDLE)
		return;
	if(!self->active)
	{
#ifdef  CHECKED_BUILD
		conlPuts(LOG_WARNING,"timerStop(): called in passive state.\n");
#endif  /* CHECKED_BUILD */
		return;
	}
	self->active = false;
	self->last_time = archTickCount() - self->start_time;
}

u32_t   V_GLOBAL
timerMilliseconds(vhandle_t timer)
{
	libv_timer_p self = GET_TIMER(timer,"timerMilliseconds");
	if(self == LIBV_INVALID_HANDLE)
		return 0;
	if(self->active)
		return (archTickCount() - self->start_time);
	else
		return self->last_time;
}

/*
 * Returns time (in seconds) since last call to 'timerStart'
 */
vec_t   V_GLOBAL
timerTime(vhandle_t timer)
{
	libv_timer_p self = GET_TIMER(timer,"timerTime");
	if(self == LIBV_INVALID_HANDLE)
		return 0;
	if(self->active)
		return (archTickCount() - self->start_time) * self->rate;
	else
		return self->last_time * self->rate;
}

void    V_GLOBAL
timerWait(vhandle_t timer,u32_t return_time)
{
	u32_t cur_time;
	libv_timer_p self = GET_TIMER(timer,"timerWait");
	if(self == LIBV_INVALID_HANDLE)
		return;
	if(self->active)
	{
		cur_time = archTickCount() - self->start_time;
		if(return_time > cur_time)
			archDelayExecution(return_time - cur_time);
#ifdef  CHECKED_BUILD
		else
			conlPrintf(LOG_ERROR,
				"timerWait(%u): curtime is %d; no time machine to go past :)\n",
				return_time,cur_time);
#endif  /* CHECKED_BUILD */
	}
#ifdef  CHECKED_BUILD
	else
		conlPrintf(LOG_ERROR,
			"timerWait(%u): timer is stopped at time %d\n",
			return_time,self->last_time);
#endif  /* CHECKED_BUILD */
}

/*
 *  The most useful routine: returns time since last call in seconds;
 *  correctly handles counter overflow.
 */
vec_t   V_GLOBAL
timerDiffTime(vhandle_t timer)
{
	u32_t cur_time, time_ret;
	libv_timer_p self = GET_TIMER(timer,"timerDiffTime");
	if(self == LIBV_INVALID_HANDLE)
		return 0; /* no timer - no time :) */
	if(!self->active)
		return self->last_time * self->rate;
	cur_time = archTickCount();
	/* Check for overflow */
	if(cur_time < self->last_time)
	{
		/* counter overflowed; restart timer. */
		self->start_time=self->last_time=cur_time;
		time_ret = 0;
	}
	else
	{
		if(self->active)
			time_ret = cur_time - self->last_time;
		else
			time_ret = 0;
	}
	self->last_time = cur_time;
	return time_ret * self->rate;
}
