/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1997  Riley Rainey
 *
 *  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; version 2 dated June, 1991.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program;  if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <sys/param.h>
#include "../util/memory.h"
#include "pm.h"

#define alarm_IMPORT
#include "alarm.h"

typedef struct _alarm_descriptor_t {
	struct _alarm_descriptor_t *next;
	double    t;
	alarm_id_t id;
	void      (*proc) (void *, void *);
	char     *arg1;
	char     *arg2;
} alarm_descriptor_t;

/*
	Pending alarms ordered by increasing time t.
*/
static alarm_descriptor_t  *alarm_list = NULL;

/*
	Recycled list of available blocks.
*/
static alarm_descriptor_t  *alarm_free_list = NULL;

static alarm_id_t next_alarm_id = 0;


/**
 * Module cleanup.
 */
static void alarm_cleanup()
{
	alarm_descriptor_t * p;

	while( alarm_list != NULL ){
		p = alarm_list;
		alarm_list = p->next;
		memory_dispose(p);
	}

	while( alarm_free_list != NULL ){
		p = alarm_free_list;
		alarm_free_list = p->next;
		memory_dispose(p);
	}
}


static alarm_descriptor_t * alarm_alloc()
{
	alarm_descriptor_t *p;

	if( alarm_free_list == NULL ){
		memory_registerCleanup(alarm_cleanup);
		return memory_allocate(sizeof(alarm_descriptor_t), NULL);
	} else {
		p = alarm_free_list;
		alarm_free_list = p->next;
		return p;
	}
}


static void alarm_free(alarm_descriptor_t *p)
{
	if( alarm_free_list == NULL ){
		alarm_free_list = p;
		p->next = NULL;
	} else {
		p->next = alarm_free_list;
		alarm_free_list = p;
	}
}


void alarm_update(void)
{
	alarm_descriptor_t *p;

	while( alarm_list != NULL && alarm_list->t <= curTime ){
		
		/*
			We have to remove the entry first because some
			call-back function also adds another entry to the list
			itself, so changing the value of alarm_list itself:
		*/

		p = alarm_list;
		alarm_list = p->next;

		/* Invoke the call-back function: */
		(*p->proc) (p->arg1, p->arg2);

		/* We can now safely release the used entry: */
		alarm_free(p);
	}
}


alarm_id_t alarm_add(double delta, void (*proc) (void *, void *), void *arg1, void *arg2)
{
	alarm_descriptor_t *n, *p, *q;

	n = alarm_alloc();
	n->id = next_alarm_id++;
	n->t = curTime + delta;
	n->proc = proc;
	n->arg1 = arg1;
	n->arg2 = arg2;

	/*
		Add entry to the list preserving the increasing order of time:
	*/

	if( alarm_list == NULL ){
		n->next = NULL;
		alarm_list = n;
	
	} else {
		q = alarm_list;
		p = NULL;
		while( q != NULL && q->t <= n->t ){
			p = q;
			q = q->next;
		}
		if( p == NULL ){
			n->next = alarm_list;
			alarm_list = n;
		} else {
			p->next = n;
			n->next = q;
		}
	}

	return n->id;
}


void alarm_cancel(alarm_id_t id)
{
	alarm_descriptor_t *p, *q;

	if( alarm_list == NULL ){
		return;
	
	} else if( alarm_list->id == id ){
		p = alarm_list;
		alarm_list = alarm_list->next;
		alarm_free(p);
	
	} else {
		p = alarm_list;
		q = p->next;
		while( q != NULL && q->id != id ){
			p = q;
			q = q->next;
		}
		if( q == NULL )
			return;
		p->next = q->next;
		alarm_free(q);
	}

}
