#include "driver.h"

#define BILLION            1000000000L
#define nonempty(list)  (q[(list)].qnext > 1)
#define firsttick(list) (q[q[(list)].qnext].qticks)

#define get_ticks(t_id) (q[(t_id)].qticks)
#define get_next(t_id)  (q[(t_id)].qnext)

struct qtick
{
	unsigned long			small;
	unsigned long			big;
};

struct qent
{
   int                     qprev;
   int                     qnext;
   struct qtick            qticks;
   snd_timer_entry_t	   *timer;
};

static struct _timer_ctrl {
	int    			tickque;
	struct qtick	qtick;
	struct qent 	*q;
	int				num_timers, min_timers, max_timers;
} _timer_ctrl;

static int x_get_tick_list_id(void)
{
   int i;
   
	for (i = _timer_ctrl.tickque+1; i < _timer_ctrl.num_timers; i++)
		if (_timer_ctrl.q[i].timer == NULL)
			return(i);
	return( -1 );	
}

#ifdef __QNXNTO__
static int x_timer_tick(message_context_t *cpt, int code, unsigned flags, void *handle)
{
	int next;
	struct qtick *cur_ticks;
	struct qent	*q;
	snd_timer_entry_t *timer;
	   
	q = _timer_ctrl.q;
   
	if(++_timer_ctrl.qtick.small == BILLION) {
		_timer_ctrl.qtick.small = 0;
		_timer_ctrl.qtick.big++;              
	}
	cur_ticks = &_timer_ctrl.qtick;

	if((next=get_next(_timer_ctrl.tickque)) != _timer_ctrl.tickque && 
		(((q[next].qticks.big < cur_ticks->big) || 
		 (q[next].qticks.big == cur_ticks->big)) && 
		 (q[next].qticks.small <= cur_ticks->small))) {
		for (next = get_next(_timer_ctrl.tickque); next != _timer_ctrl.tickque; next = get_next(next)) {
			if (cur_ticks->big > get_ticks(next).big || 
			   (cur_ticks->big == get_ticks(next).big && 
			   (cur_ticks->small >= get_ticks(next).small))) {
			 	if ( timer = _timer_ctrl.q[next].timer ) {
					snd_del_timer(timer);
					timer->function(timer->data);
				}
			}
		}
	}
	return 0;
}

extern dispatch_t *snd_dpp;

#endif

#define MIN_TIMERS	10
#define MAX_TIMERS	10

int snd_timer_init(void)
{
	
	if ((_timer_ctrl.q = snd_calloc(MIN_TIMERS * sizeof(struct qent))) == NULL)
		return( -1 );
	_timer_ctrl.num_timers = _timer_ctrl.min_timers = MIN_TIMERS;
	_timer_ctrl.max_timers = MAX_TIMERS;		
	_timer_ctrl.tickque = 0;
	_timer_ctrl.q[0].qnext = _timer_ctrl.q[0].qprev = 0;
	_timer_ctrl.q[0].qticks.small = -1;
	_timer_ctrl.q[0].qticks.big = -1;
	_timer_ctrl.qtick.small = 0;                         // Init global ticks
	_timer_ctrl.qtick.big = 0;
#ifdef __QNXNTO__
	pulse_attach(snd_dpp, MSG_FLAG_ALLOC_PULSE, 0, x_timer_tick, NULL);
#endif
	return(0);
}

int snd_add_timer( snd_timer_entry_t *timer)
{
	int next, t_id;
	struct qent	*q;
   
	if (timer->id || (t_id = x_get_tick_list_id()) == -1)
		return(-1);

	q = _timer_ctrl.q;
	q[t_id].qticks.small = timer->expires;
	if ((q[t_id].qticks.small += _timer_ctrl.qtick.small) >= BILLION) {
		q[t_id].qticks.small -= BILLION;
		q[t_id].qticks.big++;
	}
	q[t_id].qticks.big += _timer_ctrl.qtick.big;
   
	// Place in time queue.
	for ( next = get_next(_timer_ctrl.tickque); next != _timer_ctrl.tickque; next = get_next(next)) {
		if ( q[t_id].qticks.big < get_ticks(next).big || 
		   ( q[t_id].qticks.big == get_ticks(next).big && 
		   ( q[t_id].qticks.small < get_ticks(next).small)))
			break;
	}
	q[t_id].qnext = next;
	q[t_id].qprev = q[next].qprev;
	q[q[next].qprev].qnext = t_id;
	q[next].qprev = t_id;
	q[t_id].timer = timer;
	timer->id = t_id;
	return(t_id);
}

void snd_del_timer(snd_timer_entry_t *timer)
{
	struct qent	*q;

	if ( timer->id <= 0 )
		return;
   
	q = _timer_ctrl.q;
   
	q[q[timer->id].qprev].qnext = q[timer->id].qnext;
	q[q[timer->id].qnext].qprev = q[timer->id].qprev;
	q[timer->id].timer = NULL;
	timer->id = 0;
	return;
}
   
