/* $Id$
 *
 * Scheduler for VHDL processes.
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "vhdl_sched.h"
#include <stdlib.h>
#include <assert.h>
#include "glue-log.h"

struct vhdl_sched_process {
	/** callback object instance */
	void *s;
	/** callback to execute the process */
	void (*run)(void *s);
	/** name of the process */
	const char *name;
};

static void
vhdl_sched_destroy_process(
	struct vhdl_sched_process *p,
	const struct glue_vhdl_cb *callbacks
)
{
	callbacks->free(p);
}

struct vhdl_sched *
vhdl_sched_create(const struct glue_vhdl_cb *callbacks)
{
	struct vhdl_sched *ret;

	ret = callbacks->malloc(sizeof(struct vhdl_sched));
	assert(ret != NULL);

	ret->run_q = slset_create(NULL, callbacks->malloc);
	ret->wait_q = slset_create(NULL, callbacks->malloc);

	return ret;
}

void
vhdl_sched_destroy(
	struct vhdl_sched *sched,
	const struct glue_vhdl_cb *callbacks
)
{
	struct slset_entry *i;
	assert(sched != NULL);

	for (i = sched->run_q->first; i != NULL; i = i->next) {
		vhdl_sched_destroy_process(
			(struct vhdl_sched_process *)i->data,
			callbacks);
	}

	for (i = sched->wait_q->first; i != NULL; i = i->next) {
		vhdl_sched_destroy_process(
			(struct vhdl_sched_process *)i->data,
			callbacks);
	}

	slset_destroy(sched->run_q, callbacks->free);
	slset_destroy(sched->wait_q, callbacks->free);

	callbacks->free(sched);
}

void
vhdl_sched_add_process(
	struct vhdl_sched *sched, 
	void *s, 
	void (*run)(void *s),
	const char *name,
	const struct glue_vhdl_cb *callbacks
)
{
	struct vhdl_sched_process *p;

	p = callbacks->malloc(sizeof(struct vhdl_sched_process));
	assert(p != NULL);

	/* TODO sort by time? */
	assert(s != NULL);
	assert(run != NULL);

	p->s = s;
	p->run = run;
	p->name = name;
	slset_add(sched->run_q, p, callbacks->malloc);
}

void
vhdl_sched_run(
	struct vhdl_sched *sched, 
	const struct glue_vhdl_cb *callbacks
)
{
	struct slset_entry *i;

	for (i = sched->run_q->first; i != NULL; i = i->next) {
		struct vhdl_sched_process *p = 
			(struct vhdl_sched_process *)i->data;

#if 0 /* not needed atm. */
		faum_log(FAUM_LOG_DEBUG, "fauhdli", "sched",
			"running process \"%s\"\n", p->name);
#endif
		p->run(p->s);
	}
	
	slset_move_all(
			sched->run_q, 
			sched->wait_q, 
			callbacks->malloc,
			callbacks->free);
}

bool
vhdl_sched_has_runnable(struct vhdl_sched *sched)
{
	return ! slset_empty(sched->run_q);
}

void
vhdl_sched_wakeup(
	struct vhdl_sched *sched, 
	void *s,
	const struct glue_vhdl_cb *callbacks
)
{
	struct slset_entry *i;
	struct vhdl_sched_process *p = NULL;

	for (i = sched->wait_q->first; i != NULL; i = i->next) {
		p = (struct vhdl_sched_process *)i->data;

		if (p->s == s) {
			break;
		}
	}

	if (p != NULL) {
#if 0 /* not needed atm. */
		faum_log(FAUM_LOG_DEBUG, "fauhdli", "sched", 
			"waking up %s\n", p->name);
#endif
		slset_remove(sched->wait_q, p, callbacks->free);
		slset_add(sched->run_q, p, callbacks->malloc);
	}
}
