//------------------------------------------------------------------------------
// <copyright file="LqrSMACQueue.h" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
//   Declaration of QueueBase class and implementation of Queue<T> class.
//
//		- QueueBase creates some variables that are implemented in a
//		  translation unit in the shared library that are used by derived
//		  classes
//
//		- Queue<T> is a generic FIFO queue class; this class is not
//		  thread-safe
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#ifndef __LqrSMACQueue_H__
#define __LqrSMACQueue_H__

#include <deque>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include "LqrSMACDeclareException.h"
#include "LqrSMACSharedLib.h"

namespace LiquidRobotics {
namespace SMACFramework {

class SHARED_LIB_PUBLIC QueueBase : boost::noncopyable
{
public:

	typedef std::string string;

	DECLARE_EXCEPTION(ReadTimeout, std::runtime_error);
	DECLARE_EXCEPTION(QueueEmpty, std::runtime_error);
	DECLARE_EXCEPTION(QueueFull, std::runtime_error);
	DECLARE_EXCEPTION(WriteTimeout, std::runtime_error);

protected:

	static void CheckNewCapacity(size_t newCapacity, const std::type_info& typeInfo);

	static const string queueEmptyOnPeekText;
	static const string queueEmptyOnReadText;
	static const string queueFullOnWriteText;
	static const string timeoutOnAwaitReadableText;
	static const string timeoutOnAwaitWriteableText;

};

template<typename T>
class SHARED_LIB_PUBLIC Queue : public QueueBase
{
public:

	typedef std::deque<T> BaseQueueType;
	typedef T Item;
	typedef boost::shared_ptr< Queue<T> > Ptr;
	typedef typename BaseQueueType::size_type size_type;

	Queue(size_type initialCapacity = 0)
	{
		capacity = initialCapacity;
	}

	virtual ~Queue()
	{
	}

	virtual size_type GetCapacity() const
	{
		return capacity;
	}

	virtual void SetCapacity(size_type newCapacity)
	{
		CheckNewCapacity(newCapacity, typeid(*this));
		capacity = newCapacity;
	}

	virtual void Clear()
	{
		q.clear();
	}

	virtual bool IsEmpty() const
	{
		return q.empty();
	}

	virtual bool IsFull() const
	{
		return q.size() >= capacity;
	}

	virtual const Item& Peek() const
	{
		if (IsEmpty())
			throw QueueEmpty(queueEmptyOnPeekText);

		return q.front();
	}

	virtual Item Read()
	{
		if (IsEmpty())
			throw QueueEmpty(queueEmptyOnReadText);

		T result = q.front();
		q.pop_front();
		return result;
	}

	virtual bool ReadTest(Item& result)
	{
		if (IsEmpty())
			return false;

		else
		{
			result = q.front();
			q.pop_front();
			return true;
		}
	}

	virtual size_type Size() const
	{
		return q.size();
	}

	virtual size_type SpaceAvailable() const
	{
		if (capacity > q.size())
			return capacity-q.size();
		else
			return 0;
	}

	virtual bool Write(const Item& newItem, bool throwOnError = true)
	{
		bool result = !IsFull();

		if (!result)
		{
			if (throwOnError)
				throw QueueFull(queueFullOnWriteText);
		}
		else
			q.push_back(newItem);

		return result;
	}

	virtual void WriteExpand(const Item& newItem)
	{
		q.push_back(newItem);
		capacity = q.size();
	}

protected:

	size_type capacity;
	BaseQueueType q;
};

}
}

#endif
