/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************
 * timer.cc
 * (c) Murat Deligonul 2002
 */

#include "autoconf.h"
#include "timer.h"
#include "linkedlist.h"
#include "debug.h"

/**
 * This a simple implementation of interval timers ..
 */

list<timer> timer::timers;
int timer::poll_interval = 0;
time_t timer::counter = 0;

timer::timer(int _int, int _opt, int _id)
{
	DEBUG("timer::timer() -- %p started at %d, [interval: %d options: %d]\n",
		this, counter, _int, _opt);
	assert(_int != 0);
	interval = _int;
	id = _id;
	options = _opt;
	epoch = counter;
}

timer::~timer()
{
	DEBUG("timer::~timer() -- %p\n", this);
}

/**
 * Add this timer to the active timers list.
 * Figure out the poll interval and return
 * Never call from a timer_proc
 */
int timer::enable(timer * t)
{
	DEBUG("timer::enable() -- %p\n", t);
	timers.add(t);
	poll_interval = calc_interval();
	return get_poll_interval();
}

/**
 * Remove from the active timers list
 * Never call from a timer_proc
 * FIXME: current we do not re-calculate the poll-interval after a disable
 *
 */
int timer::disable(timer * t)
{
	DEBUG("timer::disable() -- %p\n", t);
	timers.remove(t);

//	poll_interval = calc_interval();
	return get_poll_interval();
}

/**
 * Figure out what interval to poll the timers
 * (with the gcd algorithm) */
int timer::calc_interval()
{
	if (!timers.size())
		return 0;

	list_iterator<timer> i(&timers);
	int * array = new int[timers.size()];
	int idx = 0;

	while (i.has_next())
	{
		timer * t = i.next();
		array[idx++] = t->interval;
	}
	int r = gcd(array, idx);
	delete[] array;
	return r;
}

/**
 * Recursively compute greatest common divisor
 * of two ints
 */
int timer::gcd(int i, int j)
{
	if (j > i)
	{
		int t = i;
		i = j;
		j = t;
	}
	int r = i % j;
	if (!r)
		return j;
	return gcd(j, r);
}

/**
 * Find the GCD of an array of int
 */
int timer::gcd(int * array, int num)
{
	if (num < 2)
		return array[0];
	int r = gcd(array[0], array[1]);
	for (int i = 2; i < num; ++i)
		r = gcd(r, array[i]);
	return r;
}

/**
 * Poll the timers
 */
int timer::poll(time_t realtime)
{
	list_iterator<timer> i(&timers);
	counter += poll_interval;

	DEBUG("timer::poll() @ time index %lu [real: %lu]\n",
		counter, realtime);

	while (i.has_next())
	{
		/**
		 * Call the handler:
		 * Check for < 0 return value, and remove that
		 * timer if necessary */
		timer * t = i.next();
		if (!((counter - t->epoch) % t->interval) )
			if ( (t->timer_proc(realtime) < 0) ||
			     (t->options & TIMER_ONESHOT))
				i.remove();
	}
	return 1;
}


int generic_timer::timer_proc(time_t t)
{
    DEBUG("generic_timer()::timer_proc() -- %p\n", this);
	return callback(t, get_id(), data);
}


syntax highlighted by Code2HTML, v. 0.9.1