//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Synchronization primitives.

#pragma once

#include <mutex>
#include <thread>
#include <condition_variable>

namespace MTL {
	namespace Synchronization {

		//----------------------------------------------------------------------//
        /// \brief Mutex.
		typedef std::mutex CMutex;

		//----------------------------------------------------------------------//
        /// \brief Recursive Mutex.
		typedef std::recursive_mutex CRecursiveMutex;

		//----------------------------------------------------------------------//
        /// \brief Counting semaphore.
		class CSemaphore
		{
		private:
			std::mutex m_mutex;
			std::condition_variable m_condition;
			unsigned long m_count = 0; // Initialized as locked.

		public:
        	/// \brief Raise a semaphore.
			void notify() {
				std::unique_lock<decltype(m_mutex)> lock(m_mutex);
				++m_count;
				m_condition.notify_one();
			}

            /// \brief Wait for a semaphore.
            void wait() {
                std::unique_lock<decltype(m_mutex)> lock(m_mutex);
                while (!m_count) // Handle spurious wake-ups.
                    m_condition.wait(lock);
                --m_count;
            }

            /// \brief Wait for a semaphore for a given number of milliseconds.
            bool wait(unsigned long Timeout) {
                std::unique_lock<decltype(m_mutex)> lock(m_mutex);
                while (!m_count) { // Handle spurious wake-ups.
                    if (m_condition.wait_for(lock, std::chrono::milliseconds(Timeout)) == std::cv_status::timeout)
                        return false;
                }
                --m_count;
                return true;
            }

            /// \brief Check whether a semaphore has been raised.
			bool try_wait() {
				std::unique_lock<decltype(m_mutex)> lock(m_mutex);
				if (m_count) {
					--m_count;
					return true;
				}
				return false;
			}
		};

		//----------------------------------------------------------------------//
		/// \brief Lock.
		/// \tparam LockType	Lockable object type (e.g. mutex).
		template <typename LockType>
		class CLockGuard
		{
		private:
			LockType &	m_rLock;
		public:
			/// \brief Constructor.
			/// \param rLock	Lockable object to pass to lock.
			CLockGuard(LockType & rLock)
				: m_rLock(rLock)
			{
				m_rLock.lock();
			}

			/// \brief Destructor.
			virtual ~CLockGuard()
			{
				m_rLock.unlock();
			}
		};
		
		//----------------------------------------------------------------------//
		/// \brief Thread.
		typedef std::thread CThread;

	} // namespace Synchronization
} // namespace MTL
