/* Copyright (C) 2000 edwards@bitchx.dimension6.com
   This is free software distributed under the terms of the
   GNU Public License.  See the file COPYING for details.

   Modified by drscholl@users.sourceforge.net 2/29/2000.

   $Id: timer.c,v 1.1.1.1 2002/06/01 14:06:53 the_turner Exp $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <time.h>
#include <stdlib.h>
#include "opennap.h"
#include "debug.h"

typedef struct _timerstruct
{
    struct _timerstruct *next;
    timer_cb_t func;
    void   *arg;
    time_t  next_time;
    time_t  interval;
    int     events;
    unsigned int refnum;
}
TIMER;

static TIMER *Pending_Timers = NULL;
static unsigned int TimerRef = 0;

static void
schedule_timer (TIMER * ntimer)
{
    TIMER **slot;
    int c = 0;

    if (!ntimer->events) {
        return;
    }

    /* we've created it, now put it in order */
    for (slot = &Pending_Timers; *slot; slot = &(*slot)->next) {
        if (ntimer->next_time < (*slot)->next_time) {
            break;
        }
        c++;
    }
/*    log_message_level (LOG_LEVEL_DEBUG, "schedule_timer: position %d, func: 0x%08x", c, ntimer->func); */
    ntimer->next = *slot;
    *slot = ntimer;
}

/* returns the refnum */
int
add_timer (int interval, int events, timer_cb_t func, void *arg)
{
    TIMER  *new;

    if (!events)
	return -1;
    new = CALLOC (1, sizeof (TIMER));
    if (!new)
    {
	OUTOFMEMORY ("add_timer");
	return -1;
    }
    new->next_time = global.current_time + interval;
    new->interval = interval;
    new->func = func;
    new->arg = arg;
    new->events = events;
    new->refnum = TimerRef++;
    schedule_timer (new);

/*    log_message_level (LOG_LEVEL_DEBUG, "Added timer function: 0x%08x, interval: %d", func, interval); */
    return new->refnum;
}

void
exec_timers (time_t now)
{
    TIMER  *current;
    int c = 0;

    while (Pending_Timers && Pending_Timers->next_time <= now) {
        c++;
        current = Pending_Timers;
        Pending_Timers = current->next;
        log_message_level (LOG_LEVEL_DEBUG, "Timer function: 0x%08x", current->func);
        (*current->func) (current->arg);
        switch (current->events) {
            case 0:     FREE (current); break;
            default:    current->events--;
            case -1:    /* reschedule */
                current->next_time = global.current_time + current->interval;
                schedule_timer (current);
                break;
        }
    }
    if (c > 10) {
        log_message_level (LOG_LEVEL_DEBUG, "exec_timers: %d events", c); }
}

/* returns the time offset at which the next pending event is scheduled */
time_t next_timer (void)
{
    if (Pending_Timers) {
        if (Pending_Timers->next_time < global.current_time) {
            log_message_level (LOG_LEVEL_DEBUG, "next_timer: 0x%08x, func: 0x%08x, time: %d, current time: %u, returning 0.",
                Pending_Timers, Pending_Timers->func, Pending_Timers->next_time, global.current_time);
            return 0;       /* now! */
        }
        return (Pending_Timers->next_time - global.current_time);
    }
    return -1;
}

void
free_timers (void)
{
    TIMER  *ptr;

    while (Pending_Timers)
    {
	ptr = Pending_Timers;
	Pending_Timers = Pending_Timers->next;
	FREE (ptr);
    }
}

/* change the interval at which a timer occurs.  note that this does not
 * reschedule a currently pending event, and only affect subsequent calls
 */
void
timer_set_interval (unsigned int refnum, int interval)
{
    TIMER  *t = Pending_Timers;

    for (; t; t = t->next)
	if (t->refnum == refnum)
	{
	    t->interval = interval;
	    break;
	}
}
