/*
	Runtime Engine
		By Craig Kadziolka ( Nov 1999 - Dec 2000 )

	Modifications and copying is permitted providing this header
	is retained
*/

#include <signal.h>
#include <stdlib.h>

#include "Synchronization.h"
#include "events.h"
#include "Types.h"

struct Event_Node {
	struct Event_Node * next;
	Handler hook;
};

struct Thread_Node; /* Externally defined in ithreads.h */

/* A map for all the operating system events */
#define NUMBER_PLATFORM_EVENTS 14
static int	signal_map[ NUMBER_PLATFORM_EVENTS ] = 
	{ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGEMT,
	  SIGFPE, SIGBUS, SIGSYS, SIGPIPE, SIGALRM, SIGTERM, SIGURG };

/* An array for the events, enables quick finding of the events */
#define MAX_EVENTS 32
#define NO_SUCH_EVENT (void *) 1
/* Some compile time sanity checking */
#if MAX_EVENTS < NUMBER_PLATFORM_EVENTS
#error MAX_EVENTS must be bigger than the NUMBER_PLATFORM_EVENTS
#endif
static struct Event_Node * Event_Table [ MAX_EVENTS ]; 
/* Not static as accessed by threads.c */
struct Thread_Node * Wait_Queue [ MAX_EVENTS ]; 
/* Wait Queues :
	The first node of a wait queue always has a NULL previous
	pointer, just like the last node always has a NULL next
	pointer. All other links remain valid */

#define NEW_EVENT_NODE (struct Event_Node *) malloc \
	(sizeof(struct Event_Node))

void cause_event( EventID event )
{
	struct Event_Node * scan;

	if (event > MAX_EVENTS) {
		printf("Attempt to raise an invalid EventID\n");
		return;
	}

	printf("Event %d was generated \n", event);

	if ( Event_Table [ event ] == NULL ) {
		printf(" No handler for event %d\n");
		return;
	}

	if ( Event_Table [ event ] == NO_SUCH_EVENT ) {
		printf(" Attempt to cause an event which doesnt exist!\n");
		return;
	}

	scan = Event_Table [ event ];

	while ( scan != NULL ) {
		if (scan == NULL) break;

		if ( scan->hook( event ) )
			break;	
		
		scan = scan -> next;
	}

}

EventID signal_to_event ( int signal )
{
	int i;

	for (i = 0; i < NUMBER_PLATFORM_EVENTS; i++) {
		if (signal == signal_map[ i ]) {
			return i ;
		}
	}

}

void default_handler(int exception_number)
{
	printf("An event occurred\n");

	cause_event( signal_to_event ( exception_number ) );

}

void init_events()
{
	int i;

	/* For each os event, create an event to go with it */

	for (i = 0; i < NUMBER_PLATFORM_EVENTS; i++) {
		Event_Table[ i ] = NULL;
		Wait_Queue[ i ] = NULL;
		signal( signal_map[i], default_handler );
	}

	for (i = NUMBER_PLATFORM_EVENTS; i < MAX_EVENTS; i++) {
		Event_Table[ i ] = NO_SUCH_EVENT;
		Wait_Queue[ i ] = NULL;
	}

}

void add_handler(EventID i, Handler h)
{

	struct Event_Node * temp;

	if ( Event_Table[ i ] == NO_SUCH_EVENT )
		return;

#ifdef DEBUG
	printf(" Call to add_handler for event %d\n", i);
#endif

	temp = NEW_EVENT_NODE;
	temp -> next = Event_Table[ i ];
	temp -> hook = h;
	Event_Table[ i ] = temp;

}

void remove_handler(EventID i, Handler h)
{

	struct Event_Node * temp, *tail = NULL;

	if ( Event_Table[ i ] == NO_SUCH_EVENT )
		return;

	temp = Event_Table[ i ];

	while (temp != NULL) {
	
		if (temp->hook == h) {
			/* Remove this node */
			
			if (tail == NULL) {
				Event_Table[ i ] = temp -> next;
			} else {
				tail -> next = temp -> next;
			}
			/* Assuming that temp will be collected by the 
			 * garbage collector */

			return;
		}
	}

	printf("No such handler attached to that vector\n");
}

/* Adds an event. Returns the event id, or -1 if an error occurred */
EventID add_event()
{
	int i;

	for (i = 0; i < MAX_EVENTS; i++) {

		if (Event_Table[ i ] == NO_SUCH_EVENT) {
			Event_Table[ i ] = NULL;
			return i;
		}
	}	

	return -1;
}

/* remove_event
 *  removes the specified event.
 *  returns 0 if successfull and -1 if failed */
int remove_event ( EventID e )
{
	
	if (e > MAX_EVENTS)
		return -1;

	if (Event_Table[ e ] == NO_SUCH_EVENT)
		return -1;

	Event_Table[ e ] = NO_SUCH_EVENT;

	return 0;
}


