/* www.c, liboop, copyright 1999 Dan Egnor

   This is free software; you can redistribute it and/or modify it under the
   terms of the GNU Lesser General Public License, version 2.1 or later.
   See the file COPYING for details. */

#ifdef HAVE_WWW

/* Yecch: the libwww header files need this. */
#define HAVE_CONFIG_H
#undef PACKAGE
#undef VERSION

#include <stdarg.h> /* Needed for our cut-down libwww headers. */

#include "oop.h"
#include "HTEvent.h"
#include "HTMemory.h"
#include "oop-www.h"

#include <assert.h>

struct event {
	HTEvent *event;
	struct timeval time;
};

typedef struct event descriptor[HTEvent_TYPES];

static int num = 0,size = 0;
static descriptor *array = NULL;
static oop_source *oop = NULL;

static struct event *get_event(SOCKET sock,HTEventType type) {
	assert(sock < size && "invalid file descriptor");
	return &array[sock][HTEvent_INDEX(type)];
}

static void *on_time(oop_source *s,struct timeval tv,void *x) {
	struct event *event;
	SOCKET sock = (int) x;
	int j;

	for (j = 0; j < HTEvent_TYPES; ++j) {
		event = &array[sock][j];
		if (NULL != event->event && 0 <= event->event->millis
		&&  tv.tv_sec == event->time.tv_sec
		&&  tv.tv_usec == event->time.tv_usec) 
			break;
	}

	assert(j < HTEvent_TYPES);
	event->event->cbf(sock,event->event->param,HTEvent_TIMEOUT);
	return OOP_CONTINUE;
}

static void set_timer(struct event *event) {
	if (0 <= event->event->millis) {
		gettimeofday(&event->time,NULL);
		event->time.tv_sec += event->event->millis / 1000;
		event->time.tv_usec += event->event->millis % 1000;
		if (event->time.tv_usec > 1000000) {
			event->time.tv_usec -= 1000000;
			++event->time.tv_sec;
		}
		oop->on_time(oop,event->time,on_time,event);
	}
}

static void *on_fd(oop_source *s,int fd,oop_event type,void *x) {
	HTEventType www_type;
	struct event *event;

	switch (type) {
	case OOP_READ:
		www_type = HTEvent_READ;
		break;
	case OOP_WRITE:
		www_type = HTEvent_WRITE;
		break;
	default:
		assert(0);
	}

	event = get_event(fd,www_type);
	if (0 <= event->event->millis) {
		oop->cancel_time(oop,event->time,on_time,event);
		set_timer(event);
	}
	event->event->cbf(fd,event->event->param,www_type);
	return OOP_CONTINUE;
}

static void dereg(SOCKET sock,HTEventType www_type,oop_event oop_type) {
	struct event *event = get_event(sock,www_type);
	assert(sock < size && "invalid file descriptor");
	if (NULL != event->event) {
		--num;
		oop->cancel_fd(oop,sock,oop_type);
		if (0 <= event->event->millis)
			oop->cancel_time(oop,event->time,on_time,event);
		event->event = NULL;
	}
}

static int reg(SOCKET sock,HTEventType type,HTEvent *www_event) {
	oop_event oop_type;
	struct event *event;

	switch (HTEvent_INDEX(type)) {
	case HTEvent_INDEX(HTEvent_READ):
		oop_type = OOP_READ;
		break;
	case HTEvent_INDEX(HTEvent_WRITE):
		oop_type = OOP_WRITE;
		break;
	case HTEvent_INDEX(HTEvent_OOB):
		/* XXX: we don't handle this; does anything use it? */
		return HT_ERROR;
	default:
		assert(0 && "invalid HTEvent type specified");
	}

	if (sock >= size) {
		int newsize = size ? (2*size) : 16;
		descriptor *newarray = oop_malloc(sizeof(*newarray) * newsize);
		int i,j;

		if (NULL == newarray) return HT_ERROR;

		memcpy(newarray,array,sizeof(*newarray) * size);
		for (i = size; i < newsize; ++i)
		for (j = 0; j < HTEvent_TYPES; ++j) {
			newarray[i][j].event = NULL;
		}

		array = newarray;
		size = newsize;
	}

	dereg(sock,type,oop_type);
	event = get_event(sock,type);
	event->event = www_event;
	oop->on_fd(oop,sock,oop_type,on_fd,NULL);
	set_timer(event);
	++num;

	return HT_OK;
}

static int unreg(SOCKET sock,HTEventType type) {
	oop_event oop_type;
	switch (HTEvent_INDEX(type)) {
	case HTEvent_INDEX(HTEvent_READ):
		oop_type = OOP_READ;
		break;
	case HTEvent_INDEX(HTEvent_WRITE):
		oop_type = OOP_WRITE;
		break;
	case HTEvent_INDEX(HTEvent_OOB):
		/* XXX: we don't handle this; does anything use it? */
		return HT_ERROR;
	default:
		assert(0 && "invalid HTEvent type specified");
	}

	dereg(sock,type,oop_type);
	return HT_OK;
}

void oop_www_register(oop_source *source) {
	oop = source;
	HTEvent_setRegisterCallback(reg);
	HTEvent_setUnregisterCallback(unreg);
}

void oop_www_cancel() {
	assert(0 == num && "cannot unregister with pending libwww events");
	HTEvent_setRegisterCallback(NULL);
	HTEvent_setUnregisterCallback(NULL);
	oop = NULL;
}

void oop_www_memory() {
	oop_malloc = HTMemory_malloc;
	oop_realloc = HTMemory_realloc;
	oop_free = HTMemory_free;
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1