///////////////////////////////////////////////////////////////////////////////
// Name:        wx/thread.h
// Purpose:     Message queues for inter-thread communication
// Author:      Evgeniy Tarassov (Modified by Vaclav Kyba)
// Created:     2007-10-31
// RCS-ID:      $Id: msgqueue.h 4622 2017-07-31 15:39:55Z antonin.misek-skoda-auto.cz $
// Copyright:   (C) 2007 TT-Solutions SARL
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef _WX_MSGQUEUE_VRUT_H_
#define _WX_MSGQUEUE_VRUT_H_

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

#include <list>
#include <wx/event.h>
#include <wx/thread.h>
#include "countable.h"


//#include <wx/stopwatch.h>

//#include <wx/beforestd.h>
//#include <deque>
//#include <wx/afterstd.h>

namespace VRUT
{
	// ---------------------------------------------------------------------------
	// Message queue allows passing message between threads.
	//
	// This class is typically used for communicating between the main and worker
	// threads. The main thread calls Post() and the worker thread calls Receive().
	//
	// For this class a message is an object of arbitrary type T. Notice that
	// typically there must be some special message indicating that the thread
	// should terminate as there is no other way to gracefully shutdown a thread
	// waiting on the message queue.
	// ---------------------------------------------------------------------------
	template <typename T>
	class MessageQueue
	{
	public:
		// Default ctor creates an initially empty queue
		MessageQueue() : m_conditionNotEmpty(m_mutex)
		{
		}

		///Add a message to this queue and signal the threads waiting for messages.
		///This method is safe to call from multiple threads in parallel.
		///\param[in] msg Message to post
		void Post(const T& msg)
		{
			wxCriticalSectionLocker locker(m_mutex);
			m_messages.push_back(msg);
			m_conditionNotEmpty.Signal();
		}

		// Wait no more than timeout milliseconds until a message becomes available.
		//
		// Setting timeout to 0 is equivalent to an infinite timeout.
		T ReceiveTimeout(long timeout, bool & err)
		{
			wxCriticalSectionLocker locker(m_mutex);

			//WaitTimeout is not accurate, but we will not try to fix the inaccuracy
			//observed inaccuracy was up to 4 ms in debug build
			if (m_messages.empty())
			{
				if (m_conditionNotEmpty.WaitTimeout(timeout)!=wxCOND_NO_ERROR)
				{
					err = true;
					return T();
				}
				if (m_messages.empty()) //I not understand why m_messages can be empty on this place, but I observed such case...
				{
					err = true;
					return T();
				}
			}

			T ret = m_messages.front();
			m_messages.pop_front();
			err = false;
			return ret;
		}

		bool IsEmpty() const
		{
			return m_messages.empty();
		}

	private:
		// Disable copy ctor and assignment operator
		MessageQueue(const MessageQueue<T>& rhs);
		MessageQueue<T>& operator=(const MessageQueue<T>& rhs);

	protected:
		mutable wxCriticalSection m_mutex;
		VRUTCondition m_conditionNotEmpty;
		std::list<T> m_messages;
	};


	//the definition of one node for the MPMC_Queue
	class MPMC_QueueNode
	{
	public:
		MPMC_QueueNode *next;
		MPMC_QueueNode() : next(nullptr) {}
	};

	//The intrusive implementation of message queue, it supports multiple producers and multiple consumers
	//The T must contain "T *next", the Post requires that the next is nullptr, suggested is to inherit T from MPMC_QueueNode
	template<typename T>
	class MPMC_Queue
	{
	public:
		MPMC_Queue() : m_conditionNotEmpty(m_mutex), head(nullptr), tail(nullptr), hPrioMesCount(0)
		{
		}

		void Post(T* msg, bool highPrio = false)
		{
			wxCriticalSectionLocker locker(m_mutex);
			if (highPrio)
			{
				if (hPrioMesCount==0)
				{
					msg->next=tail;
					tail=msg;
					if (!head)
						head=msg;
				}
				else
				{
					T* it = tail;
					for (size_t i=1; i<hPrioMesCount; ++i)
						it=(T*)it->next;
					msg->next=it->next;
					it->next=msg;
					if (head==it)
						head=msg;
				}
				++hPrioMesCount;
			}
			else
			{
				if (head)
					head->next=msg;
				else
					tail=msg;
				head=msg;
			}
			m_conditionNotEmpty.Signal();
		}

		T* ReceiveTimeout(unsigned long timeout)
		{
			wxCriticalSectionLocker locker(m_mutex);
			if (!head)
			{
				if (m_conditionNotEmpty.WaitTimeout(timeout)!=wxCOND_NO_ERROR)
					return nullptr;
				if (!head)
					return nullptr;
			}

			T *ret = tail;
			//if (tail)//unnecessary, we know that head in not nullptr so tail must be also not nullptr
			{
				tail=(T*)tail->next;
				if (!tail)
					head=nullptr;
			}
			if (hPrioMesCount>0)
				--hPrioMesCount;
			return ret;
		}

		bool WaitForAnyTimeout(unsigned long timeout)
		{
			wxCriticalSectionLocker locker(m_mutex);
			if (!head)
			{
				if (m_conditionNotEmpty.WaitTimeout(timeout)!=wxCOND_NO_ERROR)
					return false;
				if (!head)
					return false;
			}
			return true;
		}

		// Cancel timeout for all current calls of ReceiveTimeout() and WaitForAnyTimeout() and the functions returns.
		void CancelTimeout()
		{
			wxCriticalSectionLocker locker(m_mutex);
			m_conditionNotEmpty.Broadcast();
		}

		bool IsEmpty() const
		{
			return (!head);
		}

		size_t GetSizeSlow()//use only for debugging
		{
			wxCriticalSectionLocker locker(m_mutex);
			if (IsEmpty())
				return 0;
			T *iter = tail;
			size_t messageIndex = 0;
			while (iter)
			{
				iter = (T*)iter->next;
				++messageIndex;
			}
			return messageIndex;
		}

		bool CheckQueue()
		{
			T *iter = tail;
			T *prev = nullptr;
			while (iter)
			{
				prev = iter;
				iter = (T*)iter->next;
			}
			if (prev != head)
				return false;
			return true;
		}

	private:
		// Disable copy ctor and assignment operator
		MPMC_Queue(const MPMC_Queue& rhs);
		MPMC_Queue& operator=(const MPMC_Queue& rhs);

	protected:
		mutable wxCriticalSection m_mutex;
		VRUTCondition     m_conditionNotEmpty;

		T *head;
		T *tail;
		unsigned int hPrioMesCount;
	};
}

#endif // _WX_MSGQUEUE_VRUT_H_
