/*
 * \brief  Declaration of a round robin scheduler
 * \author Martin stein
 * \date   2010-06-25
 */

/*
 * Copyright (C) 2010-2012 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _KERNEL__GENERIC__INCLUDE__SCHEDULER_H_
#define _KERNEL__GENERIC__INCLUDE__SCHEDULER_H_

/* generic includes */
#include <generic/timer.h>
#include <generic/verbose.h>

/* util includes */
#include <util/queue.h>

namespace Kernel {

	enum { SHOW_SCHEDULING = 0 };

	enum { SCHEDULER__TRACE   = 1,
	       SCHEDULER__VERBOSE = 0,
	       SCHEDULER__ERROR   = 1,
	       SCHEDULER__WARNING = 1 };

	class Exec_context;
	class Platform;

	class Scheduler : public Tracks_time
	{
		enum { MS_PER_ROUND_PER_CLIENT    = SCHEDULING_MS_INTERVAL,
		       SCHEDULE__VERBOSE__SUCCESS = SCHEDULER__VERBOSE,
		       SCHEDULE__ERROR            = SCHEDULER__ERROR };

		public:

			typedef Scheduling_timer Timer;
			typedef unsigned int     Quota;
			typedef Kernel::Platform Ressource;

		private:

			Timer * const _timer;
			const Quota  _quota_per_round_per_client;
			Ressource*   _ressource;
			bool         _new_clients;

			void _schedule();

			inline Quota _ms_to_quota(unsigned int const &ms);

			/**
			 * Utilise idle client as current client
			 */
			inline void _prep_idle_round();

		public:

			inline void time_consumed(Quota const & q);

			enum { CLIENT__WARNING = 1,
			       CLIENT__VERBOSE = SCHEDULER__VERBOSE };

			class Client_queue;

			class Client : public Kernel::Queue<Client>::Item
			{
				friend class Scheduler;
				friend class Client_queue;

				typedef Kernel::Scheduler::Quota Quota;

				Quota      _quota;
				Scheduler *_scheduler;
				bool       _sleeping;

				protected:

					typedef Kernel::Exec_context Context;

				private:

					inline Quota    _consume(Quota const &consumed);
					inline void     _earn_quota(Quota const &q);
					inline Context *_schedulable_context();

				protected:

					enum{ SCHEDULABLE_context__VERBOSE = 1 };

					inline Client();

					virtual ~Client();

					inline void _sleep();
					inline void _wake();

					virtual Context *_context() = 0;
					virtual bool     _preemptable() = 0;

				public:

					virtual int label() = 0;
			};


			struct Client_queue : public Kernel::Queue<Client>
			{
				inline void print_state();
			};

		private:

			Client_queue _client_queue;
			Client*      _current_client;
			Client*      _last_client;
			Client*      _idle_client;

		public:

			enum{ ADD__VERBOSE    = SCHEDULER__VERBOSE||SHOW_SCHEDULING,
			      REMOVE__VERBOSE = SCHEDULER__VERBOSE||SHOW_SCHEDULING,
			      RUN__VERBOSE    = SCHEDULER__VERBOSE||SHOW_SCHEDULING };

			/**
			 * Constructor
			 * \param  r  Ressource that is shared by the clients
			 * \param  t  Timer to measure exclusive access duration
			 * \param  idle_client  this client gets scheduled if there's
			 *                      no other client, it can't be removed
			 */
			Scheduler(Ressource* r, Scheduling_timer * const t, Client* idle_client);

			void add(Client* c);
			void remove(Client* c);
			void run();

			inline Client* current_client();

			inline void skip_next_time(Client* c);

		protected:

			/* debugging */
			inline void _print_clients_via_labels();
			inline void _run__verbose__success();
			inline void _run__error__no_ready_client();
			inline void _run__trace__client_checks();
			inline void _schedule__error__no_clients();
			inline void _schedule__verbose__success() ;
			inline void _remove__warning__invalid_client();
			inline void _remove__verbose__success(Client* c);
			inline void _remove__trace(Client *c);
			inline void _add__warning__invalid_client();
			inline void _add__verbose__success();
	};

	/**
	 * Pointer to kernels static scheduler for execution time
	 */
	Scheduler *scheduler();
}


/***********************************
 ** Kernel::Scheduler definitions **
 ***********************************/


void Kernel::Scheduler::_prep_idle_round()
{
	if(_current_client) { _client_queue.enqueue(_current_client); }
	_current_client=_idle_client;
}


void Kernel::Scheduler::time_consumed(Quota const & q)
{
	_current_client->_consume(q);
}


Kernel::Scheduler::Client* Kernel::Scheduler::current_client()
{
	return _current_client;
}


void Kernel::Scheduler::skip_next_time(Client *c) { c->_quota=0; }


Kernel::Scheduler::Quota Kernel::Scheduler::_ms_to_quota(unsigned int const & ms)
{
	return _timer->msec_to_native(ms);
}


void Kernel::Scheduler::_print_clients_via_labels()
{
	printf("scheduled ");
	_last_client ? printf("%i", _last_client->label())
	             : printf("ø");
	printf("→");
	_current_client ? printf("%i", _current_client->label())
	                : printf("ø");
	printf(", queue ");
	_client_queue.print_state();
}


void Kernel::Scheduler::_run__verbose__success()
{
	if (!RUN__VERBOSE)
		return;

	printf("Kernel::Scheduler::run, ");
	_print_clients_via_labels();
	printf("\n");
}


void Kernel::Scheduler::_run__error__no_ready_client()
{
	if (!SCHEDULER__ERROR) return;

	printf("Error in Kernel::Scheduler::run, no client is ready, halt\n");
	halt();
}


void Kernel::Scheduler::_remove__trace(Client* c)
{
	if (SCHEDULER__TRACE && Verbose::trace_current_kernel_pass())
		printf("rm(%i) ", c->label());
}


void Kernel::Scheduler::_run__trace__client_checks()
{
	if (SCHEDULER__TRACE && Verbose::trace_current_kernel_pass())
		printf("ask(%i,%i) ",
		       _current_client->label(), _current_client->_quota);
}


void Kernel::Scheduler::_schedule__error__no_clients()
{
	if (!SCHEDULER__ERROR) return;

	printf("Error in Kernel::Scheduler::_schedule, no clients registered, halt\n");
	halt();
}


void Kernel::Scheduler::_remove__warning__invalid_client()
{
	if (!SCHEDULER__WARNING) return;

	printf("Warning in Kernel::Scheduler::remove, client invalid, skip\n");
}


void Kernel::Scheduler::_add__warning__invalid_client()
{
	if (!SCHEDULER__WARNING) return;

	printf("Warning in Kernel::Scheduler::add, client invalid, skip\n");
}


void Kernel::Scheduler::_add__verbose__success()
{
	if (!ADD__VERBOSE) return;

	printf("Kernel::Scheduler::add, ");
	_print_clients_via_labels();
	printf(" ← )\n");
}


void Kernel::Scheduler::_remove__verbose__success(Client* c)
{
	if (!REMOVE__VERBOSE) return;

	printf("Kernel::Scheduler::remove, ");
	_print_clients_via_labels();
	printf(" → %i\n", c->label());
}


void Kernel::Scheduler::_schedule__verbose__success()
{
	if (!SCHEDULER__VERBOSE) return;

	Client* const a = _last_client;
	Client* const b = _current_client;

	Verbose::indent(10);
	if (a) printf("from %i", a->label());
	else   printf("from NULL");

	Verbose::indent(10);
	printf("to %i\n", b->label());
}


/*******************************************
 ** Kernel::Scheduler::Client definitions **
 *******************************************/

Kernel::Scheduler::Client::Context *
Kernel::Scheduler::Client::_schedulable_context()
{
	Context *result = 0;
	if (!_sleeping) {
		result = _context();
		if (_sleeping)
			result = 0;
	}
	return result;
}


Kernel::Scheduler::Client::Quota
Kernel::Scheduler::Client::_consume(Quota const &consumed)
{
	if (consumed > _quota) {
		_quota = 0;
	} else{
		_quota = _quota - consumed;
	}
	return _quota;
}


Kernel::Scheduler::Client::Client()
: _quota(0), _scheduler(0), _sleeping(false) { }


void Kernel::Scheduler::Client::_earn_quota(Quota const &q) { _quota += q; }


void Kernel::Scheduler::Client::_sleep() { _sleeping = true; }


void Kernel::Scheduler::Client::_wake(){ _sleeping = false; }


/*************************************************
 ** Kernel::Scheduler::Client_queue definitions **
 *************************************************/

void Kernel::Scheduler::Client_queue::print_state()
{
	Client *i = _head;
	if (!i) printf("ø");
	while (i) {
		printf("%i", i->label());
		if (i != _tail) printf("→");
		i = i->_next;
	}
}

#endif /* _KERNEL__INCLUDE__SCHEDULER_H_ */

