/* Selecting fds */
/* TODO: rather use poll()? select can't handle big number of fds.. */

#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>

#include "select.h"
#include "socket.h"

void
classify_sock(sockset_t *sockset, sock_t *sock) {
	if (sock->handler_r)
		FD_SET(sock->fd, &sockset->fdr);
	else
		FD_CLR(sock->fd, &sockset->fdr);

	if (sock->handler_w)
		FD_SET(sock->fd, &sockset->fdw);
	else
		FD_CLR(sock->fd, &sockset->fdw);

	if (sock->handler_e)
		FD_SET(sock->fd, &sockset->fde);
	else
		FD_CLR(sock->fd, &sockset->fde);
}


sockset_t *
sockset_get() {
	sockset_t *sockset = malloc(sizeof(sockset_t));

	if (! sockset)
		return NULL;

	sockset->socks = NULL;

	sockset->fdm = 0;
	FD_ZERO(&sockset->fdr);
	FD_ZERO(&sockset->fdw);
	FD_ZERO(&sockset->fde);

	return sockset;
}

int
sockset_add(sockset_t *sockset, sock_t *sock) {
	sock_t *current, *prev;

	if (! sock || sock->set)
		return 0;

	libaio_foreach(sockset->socks) { /* walk to the last one */
		if (current->fd == sock->fd)
			return 0; /* not the way */
	}

	current = sock;

	if (! prev)
		sockset->socks = current;
	else
		prev->next = current;

	if (current->fd > sockset->fdm)
		sockset->fdm = current->fd;

	current->set = sockset;

	classify_sock(sockset, sock);

	return 1;
}

int
sockset_del(sockset_t *sockset, sock_t *sock) {
	sock_t *current, *prev, *that = NULL, *tprev;

	if (! sock || sock->set != sockset)
		return 0;

	sockset->fdm = 0;

	libaio_foreach(sockset->socks) {
		/* FIXME: should we compare rather the whole ptr? */
		if (current->fd == sock->fd) {
			that = current; tprev = prev;
		} else if (current->fd > sockset->fdm) {
			sockset->fdm = current->fd;
		}
	}

	if (! that)
		return 0; /* not found */

	/* we are doing it here coz we need to update sockset->fdm */

	tprev->next = that->next;
	that->set = NULL;

	FD_CLR(that->fd, &sockset->fdr);
	FD_CLR(that->fd, &sockset->fdw);
	FD_CLR(that->fd, &sockset->fde);

	return 1;
}

/*
 * FIXME? we now trust you to give us a valid sock,
 * not checking if it's structure member. imho it's useless
 * overhead, maybe add a #define for checking it for
 * debuging purposes?
 */
int
sockset_notify(sockset_t *sockset, sock_t *sock) {
	if (! sock)
		return 0;

	classify_sock(sockset, sock);

	return 1;
}

/* lala, the interesting stuff at last! */
/* TODO: add alarm support */
int
sockset_check(sockset_t *sockset) {
	sock_t *current, *prev;
	fd_set fdr = sockset->fdr,
	       fdw = sockset->fdw,
	       fde = sockset->fde;

	if (select(sockset->fdm + 1, &fdr, &fdw, &fde, NULL) < 0)
		return 0;

	/* FIXME: checks for handler_* useless? just a sanity.. */
	/* we won't fail because they may have changed the handler in
	 * the previous handler, even calling notify, but we won't get
	 * that.  */
	libaio_foreach(sockset->socks) {
		int can_change = (current->state == SS_IDLE);

		if (FD_ISSET(current->fd, &fdr)) {
			if (can_change)
				current->state = SS_READABLE;
			if (current->handler_r)
				current->handler_r(current, current->handler_r_data);
		}

		if (FD_ISSET(current->fd, &fdw)) {
			if (can_change)
				current->state = SS_WRITABLE;
			if (current->handler_w)
				current->handler_w(current, current->handler_w_data);
		}

		if (FD_ISSET(current->fd, &fde)) {
			if (can_change)
				current->state = SS_EXCEPTION;
			if (current->handler_e)
				current->handler_e(current, current->handler_e_data);
		}

		if (can_change)
			current->state = SS_IDLE;
	}

	return 1;
}
