/*
 * \brief  Queue with first-in first-out semantics
 * \author Norman Feske
 * \date   2008-08-15
 */

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

#ifndef _INCLUDE__UTIL__FIFO_H_
#define _INCLUDE__UTIL__FIFO_H_

namespace Genode {
	
	template<typename> class Fifo;
	template <typename> class Fifo_element;
}


/**
 * First-in first-out (FIFO) queue
 *
 * \param QT  queue element type
 */
template <typename QT>
class Genode::Fifo
{
	public:

		class Element
		{
			protected:

				friend class Fifo;

				QT  *_next     { nullptr };
				bool _enqueued { false   };

			public:

				Element() { }

				/**
				 * Return true is fifo element is enqueued in a fifo
				 */
				bool enqueued() const { return _enqueued; }

				/**
				 * Return next element in queue
				 *
				 * \deprecated  use 'Fifo::for_each' instead
				 */
				QT *next() const { return _next; }
		};

	private:

		QT      *_head { nullptr };  /* oldest element */
		Element *_tail { nullptr };  /* newest element */

	public:

		/**
		 * Return true if queue is empty
		 */
		bool empty() const { return _tail == nullptr; }

		/**
		 * Constructor
		 */
		Fifo() { }

		/**
		 * Call 'fn' of type 'void (QT&)' the head element
		 */
		void head(auto const &fn) const { if (_head) fn(*_head); }

		/**
		 * Remove element explicitly from queue
		 */
		void remove(QT &qe)
		{
			if (empty()) return;

			/* if specified element is the first of the queue */
			if (&qe == _head) {
				_head = qe.Fifo::Element::_next;
				if (!_head)
					_tail = nullptr;

			} else {

				/* search specified element in the queue */
				Element *e = _head;
				while (e->_next && (e->_next != &qe))
					e = e->_next;

				/* element is not member of the queue */
				if (!e->_next) return;

				/* e->_next is the element to remove, skip it in list */
				e->Fifo::Element::_next = e->Fifo::Element::_next->Fifo::Element::_next;
				if (!e->Element::_next) _tail = e;
			}

			qe.Fifo::Element::_next = nullptr;
			qe.Fifo::Element::_enqueued = false;
		}

		/**
		 * Attach element at the end of the queue
		 */
		void enqueue(QT &e)
		{
			e.Fifo::Element::_next = nullptr;
			e.Fifo::Element::_enqueued = true;

			if (empty()) {
				_tail = _head = &e;
				return;
			}

			_tail->Fifo::Element::_next = &e;
			_tail = &e;
		}

		/**
		 * Call 'fn' of type 'void (QT&)' for each element in order
		 */
		void for_each(auto const &fn) const
		{
			QT *elem = _head;
			while (elem != nullptr) {
				/* take the next pointer so 'fn' cannot modify it */
				QT *next = elem->Fifo::Element::_next;;
				fn(*elem);
				elem = next;
			}
		}

		/**
		 * Remove head and call 'fn' of type 'void (QT&)'
		 */
		void dequeue(auto const &fn)
		{
			QT *result = _head;

			/* check if queue has only one last element */
			if (_head == _tail) {
				_head = nullptr;
				_tail = nullptr;
			} else
				_head = _head->Fifo::Element::_next;

			/* mark fifo queue element as free */
			if (result) {
				result->Fifo::Element::_next = nullptr;
				result->Fifo::Element::_enqueued = false;

				/* pass to caller */
				fn(*result);
			}
		}

		/**
		 * Remove all fifo elements
		 *
		 * This method removes all elements in order and calls the lambda
		 * 'fn' of type 'void (QT&)' for each element. It is intended to be
		 * used prior the destruction of the FIFO.
		 */
		void dequeue_all(auto const &fn)
		{
			while (_head != nullptr) dequeue(fn);
		}
};


/**
 * Helper for using member variables as FIFO elements
 *
 * \param T  type of compound object to be organized in a FIFO
 *
 * This helper allows the creation of FIFOs that use member variables to
 * connect their elements. This way, the organized type does not need to
 * publicly inherit 'Fifo<QT>::Element'. Furthermore, objects can easily
 * be organized in multiple FIFOs by embedding multiple 'Fifo_element'
 * member variables.
 */
template <typename T>
class Genode::Fifo_element : public Fifo<Fifo_element<T> >::Element
{
	T &_object;

	public:

		Fifo_element(T &object) : _object(object) { }

		T       &object()       { return _object; }
		T const &object() const { return _object; }

};

#endif /* _INCLUDE__UTIL__FIFO_H_ */
