/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once
#ifndef GLMLOGBUFFER_H
#define GLMLOGBUFFER_H

#include "glmConcurrentDataStructures.h"
#include "glmMutex.h"
#include "glmThreading.h"

namespace glm
{
	template< typename T >
	class GLM_CORE_API LogBuffer
	{
		typedef MPMC_RingBuffer<T> InternalContainer;
	public:

		LogBuffer() : _conditionToNotify(NULL) {}
		~LogBuffer() {}

		bool initialize(void* buffer, size_t bufferSize, size_t countMax);
		void uninitialize();

		bool registerConditionToNotifyOnLog(ConditionVar* conditionToNotify);

		void log(T& element);
		bool readNextLog(T& element);

		bool wasEmpty() const;

		static size_t getBufferSizeToAllocate(size_t countMax);

	private:
		 
		glm::ConditionVar* _conditionToNotify;

		typename LogBuffer::InternalContainer _logs;
	};

	template< typename T >
	bool LogBuffer<T>::initialize(void* buffer, size_t bufferSize, size_t countMax)
	{
		GLM_ASSERT( buffer != NULL );
		GLM_ASSERT( bufferSize > 0 && bufferSize == getBufferSizeToAllocate(countMax) );

		const bool initialized = _logs.initialize(buffer, bufferSize, countMax);

		return initialized;
	}

	template< typename T >
	void LogBuffer<T>::uninitialize()
	{
		_conditionToNotify = NULL;

		_logs.uninitialize();
	}

	template< typename T >
	bool LogBuffer<T>::registerConditionToNotifyOnLog(ConditionVar* conditionToNotify)
	{
		GLM_ASSERT(_conditionToNotify == NULL);
		GLM_ASSERT(conditionToNotify != NULL);

		_conditionToNotify = conditionToNotify;

		return true;
	}

	template< typename T >
	void LogBuffer<T>::log(T& element)
	{
		bool wasPushed = _logs.try_push(element);
		while (wasPushed == false)
		{
			glm::yieldCurrentThread();
			wasPushed = _logs.try_push(element);
		}

		if ( _conditionToNotify != NULL )
		{
			//NOTE: should have only one thread responsible for flushing log data
			_conditionToNotify->notifyOne();
		}
	}

	template< typename T >
	bool LogBuffer<T>::readNextLog(T& element)
	{
		return _logs.try_pop(element);
	}

	template< typename T >
	bool LogBuffer<T>::wasEmpty() const
	{
		return _logs.wasEmpty();
	}

	template< typename T >
	size_t LogBuffer<T>::getBufferSizeToAllocate(size_t countMax)
	{
		return LogBuffer::InternalContainer::getBufferSizeToAllocate( countMax );
	}
}

#endif // GLMLOGBUFFER_H

