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

#pragma once

#if _WIN32 // also defined for x64 / ia64
#include "glmWindows.h"
#else // not win32
#include <pthread.h>
#include <errno.h> // some systems need it for ETIMEDOUT
#include <ctime> // for linux, for unix use #include <sys/time.h>
#endif 

#include "glmAssert.h"

namespace glm
{
	/**
	* \brief Synchronization barrier for Engine Worker Threads (not exported)
	*/
	class GLM_CORE_API SynchroBarrier 
	{

	public:

		/**
		* \brief constructor
		*/
		SynchroBarrier() ;

		/**
		* \brief destructor
		*/
		~SynchroBarrier() ;

		/**
		* \brief put thread in wait state, if threadCount is not 0 : wait for threadCount threads, then trigger launch signal automatically, else wait for go signal.
		*/	
		bool wait(  ) ;

		/**
		* \brief threadCount setter
		*/	
		void setThreadCount( unsigned int threadCount ) ;

		/**
		* \brief get number of threads currently waiting
		*/
		unsigned int getCurrentThreadsWaiting();

		/**
		* \brief launch processing (in mode where threadCount is not used to trigger launch) 
		*/
		bool launch();

	private:

		/**
		* \brief lock mutex
		*/
		inline bool lock();

		/**
		* \brief unlock mutex
		*/
		inline void unlock();

		/**
		* \brief launch without enclosing locks
		*/
		bool internal_launch();

		/// threads to wait for
		unsigned int _threadCount ;

		/// threads to wait for
		unsigned int _currentThreadsWaiting ;			

		/// condition variable index
		unsigned int _condIndex ;

		/// time out for the barrier
		static const unsigned int _timeOut ;

#if _WIN32 // also defined for x64 / ia64

		/// barrier mutex
		HANDLE _mutex ;

		/// wake up condition variables
		HANDLE _event[2] ;

#else
		/// barrier mutex
		pthread_mutex_t _mutex ;

		/// wake up condition variables
		pthread_cond_t _condition[2] ;
#endif

	} ;

	//-------------------------------------------------------------------------
	// lock()
	//-------------------------------------------------------------------------
	bool
		SynchroBarrier::lock()
	{
#if _WIN32 // also defined for x64 / ia64
		int returnCode = WaitForSingleObject( _mutex, _timeOut ) ;
		return ( returnCode == WAIT_OBJECT_0 ) ;
#else
		return ( pthread_mutex_lock( &_mutex ) == 0);
#endif
	}

	//-------------------------------------------------------------------------
	// unlock()
	//-------------------------------------------------------------------------
	void
		SynchroBarrier::unlock()
	{
#ifdef _MSC_VER // also defined for x64 / ia64
    #ifdef NDEBUG
		ReleaseMutex( _mutex ) ;
    #else
		int returnCode = ReleaseMutex( _mutex ) ;
		GLM_DEBUG_ASSERT( returnCode != 0 ) ;
		GLM_UNREFERENCED( returnCode );
    #endif
#else
		pthread_mutex_unlock( &_mutex ) ;
#endif
	}
}
