#ifndef   LWT_SCHEDULER_HEADER
#define   LWT_SCHEDULER_HEADER

#include "lwt.h"

#include <semaphore.h>
#include <errno.h>

#include "util/die.h"
#include "mem/alloc.h"
#include "sched/thread.h"
#include "adt/simplequeue.h"
#include "spinlocks/readlock.h"

extern "C" void lwt_scheduler_loop();

namespace lwt {

class Scheduler
{
	private:
	sem_t readySem;
	lwt::SimpleQueue<lwt::Thread> queue;
	lwt::ReadSpinlock spinlock;

	public:
	void signalReadySem()
	{
		if(sem_post(&readySem) != 0) {
			lwt::fatal("sem_post in signalReadySem");
		}
	}

	void waitReadySem()
	{
		while(sem_wait(&readySem) != 0) {
			const int errnoVal = errno;
			if(errnoVal != EINTR) {
				lwt::fatal("sem_wait in waitReadySem");
			}
		}
	}

	bool init()
	{
		if(sem_init(&readySem, 0, 0) != 0) {
			lwt::die("sem_init");
			return false;
		}

		queue.init();
		spinlock.init();

		return true;
	}

	bool readyFunc(lwt_func func, void *a, void *b, bool killFunc=false)
	{
		lwt::Thread *thread = lwt::Alloc::alloc<lwt::Thread>();

		if(thread == 0) {
			return false;
		}

		if(killFunc) {
			thread->init(func, thread, 0);
		}
		else {
			thread->init(func, a, b);
		}

		spinlock.lock();
		queue.enqueue(thread);
		spinlock.unlock();

		signalReadySem();

		return true;
	}

	lwt::Thread* getNext()
	{
		lwt::Thread *out;
		
		spinlock.lock();
		out = queue.dequeue();
		spinlock.unlock();

		return out;
	}
};


} // namespace llwt

extern lwt::Scheduler scheduler;

#endif /* LWT_SCHEDULER_HEADER */
