/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.3 $
 *********************************************************************/

#ifndef _FIFO_H
#define _FIFO_H

namespace util
{
	/**
	 * Fifo is a first-in-first-out buffer built on top of List. When an
	 * element is put on a Fifo, the last element will "pop out". For
	 * performance reasons, Fifo is implemented as a circular buffer.
	 * For that reason, one might get unexpected results when using
	 * List's elementAt method or the [] operator.
	 **/
	template <class T> class Fifo : public List<T>
	{
	public:
		/**
		 * Create a new Fifo with the given capacity. The contents of the
		 * Fifo will be filled with <i>initialValue</i>.
		 **/
		Fifo(int capacity, T initialValue = 0);
		/**
		 * Copy a Fifo.
		 **/
		Fifo(const Fifo& other) : List<T>(other), _iCurrentIndex(other._iCurrentIndex) {}
		/**
		 * Copy a List.
		 **/
		Fifo(const List<T>& other) : List<T>(other), _iCurrentIndex(0) {}
		/**
		 * Copy a Fifo.
		 **/
		Fifo& operator= (const Fifo& other);
		/**
		 * Copy a List.
		 **/
		Fifo& operator= (const List<T>& other);
		/**
		 * Put a value into a fifo. This will pop out the last value.
		 **/
		T put(T value);
		/**
		 * Put a value into a fifo.
		 **/
		void addElement(const T& element);

		/**
		 * Get the value at <i>index</i>. Use this operator if you want to
		 * access the circularly indexed buffer so that index 0 always
		 * references the oldest item.
		 **/
		T& operator[] (int index) { return _internalArray[(_iCurrentIndex+index) % _iCurrentItems]; }
		/**
		 * Get the value at <i>index</i>. Const version.
		 **/
		const T& operator[] (int index) const { return _internalArray[(_iCurrentIndex+index) % _iCurrentItems]; }

		/**
		 * @see #operator[](int)
		 **/
		T getElementAt(int index) { return elementAt(index); }
		/**
		 * @see #operator[](int)
		 **/
		T& elementAt(int index) { return _internalArray[(_iCurrentIndex+index) % _iCurrentItems]; }
		/**
		 * @see #operator[](int)
		 **/
		const T& elementAt(int index) const { return _internalArray[(_iCurrentIndex+index) % _iCurrentItems]; }

	private:
		int _iCurrentIndex;
	};

	template <class T> Fifo<T>::Fifo(int capacity, T initialValue) :
		List<T>(capacity), _iCurrentIndex(0)
	{
		setLength(capacity,initialValue);
	}

	template <class T> void Fifo<T>::addElement(const T& value)
	{
		if (_iCurrentIndex >= _iCurrentItems)
			_iCurrentIndex = 0;
		_internalArray[_iCurrentIndex] = value;
		_iCurrentIndex = (_iCurrentIndex+1)%_iCurrentItems;
	}

	template <class T> T Fifo<T>::put(T value)
	{
		if (_iCurrentIndex >= _iCurrentItems)
			_iCurrentIndex = 0;
		T tmp = _internalArray[_iCurrentIndex];
		_internalArray[_iCurrentIndex] = value;
		_iCurrentIndex = (_iCurrentIndex+1)%_iCurrentItems;
		return tmp;
	}

	template <class T> Fifo<T>& Fifo<T>::operator= (const Fifo<T>& other)
	{
		List<T>::operator= (other);
		_iCurrentIndex = other._iCurrentIndex;
		return *this;
	}

	template <class T> Fifo<T>& Fifo<T>::operator= (const List<T>& other)
	{
		List<T>::operator= (other);
		_iCurrentIndex = 0;
		return *this;
	}
}

#endif
