/* 
 * Copyright (C) 2000-2001 Computer & Communications Research Laboratories,
 *			   Industrial Technology Research Institute
 */
/*
 * msgEvent.c
 *
 * $Id: msgEvent.c,v 1.16 2001/06/15 09:10:21 hcc Exp $
 */

#include <string.h>
#include <stdlib.h>
#ifdef UNIX
#include <sys/time.h>
#include <sys/select.h>
#endif
#include "msgEvent.h"
#include "common.h"

TCR	evt_tracer = 0;

typedef struct {
	int		valid; /* 0=invalid; 1=valid */
	/*msgEventType	event;*/
	int		event;
	msgEventCB	cb;
	msgSock		sock;
	void*		context;
} EventNode;

struct msgEventObj
{
	EventNode	nodes[FD_SETSIZE-1];
	int		maxi;
	SOCKET		maxfd;
	fd_set		selectRd;
	fd_set		selectWr;
	fd_set		selectEx;
};

msgEvent msgEventNew(void)
{
	msgEvent _this;
	int i;

	_this = (msgEvent)malloc(sizeof *_this);
	if (_this == NULL) return NULL;

	for (i=0; i<=FD_SETSIZE; i++) {
		_this->nodes[i].valid = 0;
		_this->nodes[i].event = msgEventUnknown;
		_this->nodes[i].cb = NULL;
		_this->nodes[i].sock = NULL;
		_this->nodes[i].context = NULL;
	}

	_this->maxi = -1;
	_this->maxfd = INVALID_SOCKET;

	FD_ZERO(&_this->selectRd);
	FD_ZERO(&_this->selectWr);
	FD_ZERO(&_this->selectEx);

	return _this;
}

void msgEventFree(msgEvent _this)
{
	if (_this!=NULL) free(_this);

	return;
}

int msgEventRegister(msgEvent _this, msgSock sock, msgEventType event, msgEventCB cb, void* context)
{
	SOCKET fd;
	int i, j;

	if (_this == NULL || sock == NULL || !event || !cb)
		return -1;

	fd = msgSockGetSock(sock);
	j = _this->maxi + 1;

	for (i=0; i<=_this->maxi; i++)
		if (_this->nodes[i].valid)
			if (msgSockGetSock(_this->nodes[i].sock) == fd) {
				/* re-register */
				j = i;
				break;
			} else
				continue;
		else
			if (i < j) j = i;

	if (j >= FD_SETSIZE) 
		return -1;
	
	/* registration */
	if (event & msgEventRd) {
		FD_SET(fd, &_this->selectRd);
		if ((_this->maxfd == INVALID_SOCKET) || (fd > _this->maxfd)) _this->maxfd = fd;
	} else
		FD_CLR(fd, &_this->selectRd);

	if (event & msgEventWr) {
		FD_SET(fd, &_this->selectWr);
 		if ((_this->maxfd == INVALID_SOCKET) || (fd > _this->maxfd)) _this->maxfd = fd;
	} else
		FD_CLR(fd, &_this->selectWr);

	if (event & msgEventEx) {
		FD_SET(fd, &_this->selectEx);
		if ((_this->maxfd == INVALID_SOCKET) || (fd > _this->maxfd)) _this->maxfd = fd;
	} else
		FD_CLR(fd, &_this->selectEx);

	if (j > _this->maxi) _this->maxi = j;
	_this->nodes[j].valid=1;
	_this->nodes[j].event = event;
	_this->nodes[j].cb = cb;
	_this->nodes[j].sock = sock;
	_this->nodes[j].context = context;
    
	return 0;
}

int msgEventUnregister(msgEvent _this, msgSock sock)
{
	SOCKET fd, sockfd, maxfd;
	int i, index, maxi;
	int found=0;

	if (_this==NULL)
		return -1;

	fd = msgSockGetSock(sock);
	maxi = -1;
	maxfd = INVALID_SOCKET;

	for (i=0; i<=_this->maxi; i++)
		if (_this->nodes[i].valid) {
			sockfd = msgSockGetSock(_this->nodes[i].sock);
			index = i;
			if (sockfd == fd) {
				_this->nodes[i].valid = 0;
				_this->nodes[i].event = msgEventUnknown;
				_this->nodes[i].cb = NULL;
				_this->nodes[i].sock = NULL;
				/* free here ? */
				/*if (_this->nodes[i].context)
					free(_this->nodes[i].context);*/
				_this->nodes[i].context = NULL;

				FD_CLR(fd, &_this->selectRd);
				FD_CLR(fd, &_this->selectWr);
				FD_CLR(fd, &_this->selectEx);

				sockfd = INVALID_SOCKET;
				index = -1;
				found = 1;
			}
			if (index > maxi) 
				maxi = index;
			if ((maxfd == INVALID_SOCKET) || 
				((sockfd != INVALID_SOCKET) && (sockfd > maxfd)))
				maxfd = sockfd;
		}

	if (!found) return -1;

	_this->maxi = maxi;
	_this->maxfd = maxfd;

	return 0;
}

int msgEventDispatch(msgEvent _this, int timeout)
{
	struct timeval tv;
	int numEvents=0;
	SOCKET fd;
	/*msgEventType event;*/
	int event;
	int i;
	fd_set rset, wset, xset;
	int retval;

	rset = _this->selectRd;
	wset = _this->selectWr;
	xset = _this->selectEx;
	/*msec=100;*/
	if ( timeout == -1)
		numEvents = select(_this->maxfd+1, &rset, &wset, &xset, NULL);
	else {
		tv.tv_sec = timeout/1000;
		tv.tv_usec = (timeout%1000)*1000;    
		numEvents = select(_this->maxfd+1, &rset, &wset, &xset, &tv);
	}
	if (numEvents == SOCKET_ERROR)
		retval  = -1;
	else
		retval = numEvents;

	/* invoke callbacks if event occurs*/
	event = msgEventUnknown;
	if ( numEvents > 0 ) {
		for (i=0; i<=_this->maxi; i++) {
			if (_this->nodes[i].valid) {
				fd = msgSockGetSock(_this->nodes[i].sock);
				if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset) || FD_ISSET(fd, &xset)) {
					if ((_this->nodes[i].event & msgEventRd) && FD_ISSET(fd, &rset) )
						event |= msgEventRd;
 
					if ( (_this->nodes[i].event & msgEventWr) && FD_ISSET(fd, &wset) )
						event |= msgEventWr;

					if ((_this->nodes[i].event & msgEventEx) && FD_ISSET(fd, &xset) )
						event |= msgEventEx;
					/* strange type conversion from int to msgEventType ??? */
					_this->nodes[i].cb(_this->nodes[i].sock, (msgEventType)event, 0, _this->nodes[i].context);

					numEvents--;
					if (numEvents == 0) break;
				}
			}
		}
	}

	return retval;
}

/*========================================================================
// msgEvent Tracer */
void	msgEventSetTracer(TCR tracer)
{
	evt_tracer = tracer;
}

TCR	msgEventGetTracer(void)
{
	return evt_tracer;
}


