#ifndef POLLABLE_H
#define POLLABLE_H

typedef void *(*future_poll_func)(void *env, void *data);
typedef struct future (*future_then_func)(void *env, void *result);

struct future {
	future_poll_func f;
	future_then_func then;
	void *env;
};

enum future_err_code {
	FUTURE_ERR_OK,
	FUTURE_ERR_MISSING_FUNC
};

struct future_err {
	enum future_err_code code;
	const char *name;
};

void *future_poll(struct future *p, void *data, struct future_err *err);

void _future_err_set(struct future_err *err, enum future_err_code code);

#if defined(IMP) || defined(POLLABLE_IMP)

#include <stdlib.h>

void *
future_poll(struct future *p, void *data, struct future_err *err)
{
	void *res = p->f(p->env, data);
	if ( p->then != NULL ) {
		if ( res == NULL ) {
			goto exit_ok;
		}

		{
			struct future new_ftr;
			new_ftr = p->then(p->env, res);
			p->f = new_ftr.f;
			p->env = new_ftr.env;
			p->then = new_ftr.then;
		}

		res = NULL;
		goto exit_ok;
	}

	if ( p->f == NULL ) {
		_future_err_set(err, FUTURE_ERR_MISSING_FUNC);
		return NULL;
	}

exit_ok:
	_future_err_set(err, FUTURE_ERR_OK);
	return res;
}

void
_future_err_set(struct future_err *err, enum future_err_code code)
{
	if ( err == NULL ) {
		return ;
	}

	err->code = code;
	switch ( code ) {
#define _FUTURE_ERR_SET_CASE(c) case c: err->name = #c; break
	_FUTURE_ERR_SET_CASE(FUTURE_ERR_OK);
	_FUTURE_ERR_SET_CASE(FUTURE_ERR_MISSING_FUNC);
#undef _FUTURE_ERR_SET_CASE
	}
}

#endif /* defined(IMP) || defined(POLLABLE_IMP) */

#endif /* POLLABLE_H */
