/* +---------------------------------------------------------------------------+
   |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
   |                                                                           |
   |                   http://mrpt.sourceforge.net/                            |
   |                                                                           |
   |   Copyright (C) 2005-2008  University of Malaga                           |
   |                                                                           |
   |    This software was written by the Perception and Robotics               |
   |      research group, University of Malaga (Spain).                        |
   |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
   |                                                                           |
   |  This file is part of the MRPT project.                                   |
   |                                                                           |
   |     MRPT is free software: you can redistribute it and/or modify          |
   |     it under the terms of the GNU General Public License as published by  |
   |     the Free Software Foundation, either version 3 of the License, or     |
   |     (at your option) any later version.                                   |
   |                                                                           |
   |   MRPT is distributed in the hope that it will be useful,                 |
   |     but WITHOUT ANY WARRANTY; without even the implied warranty of        |
   |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
   |     GNU General Public License for more details.                          |
   |                                                                           |
   |     You should have received a copy of the GNU General Public License     |
   |     along with MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
   |                                                                           |
   +---------------------------------------------------------------------------+ */
#ifndef  SYNCH_H
#define  SYNCH_H

#include <MRPT/UTILS/utils_defs.h>
#include <MRPT/UTILS/CStream.h>
#include <MRPT/UTILS/CReferencedMemBlock.h>

/*---------------------------------------------------------------
	Class
  ---------------------------------------------------------------*/
namespace UTILS
{
	/** This namespace provides multitask, synchronization utilities.
	 */
	namespace SYNCH
	{
		/** This class provides simple critical sections functionality.
		  * \sa CCriticalSectionLocker
		  */
		class	CCriticalSection
		{
		private:
			CReferencedMemBlock		m_data;   //!< The OS-dependent descriptors

			std::string		m_name;
		public:
			/** Constructor
			  */
			CCriticalSection(const char *name = NULL);

			/** Destructor
			  */
			~CCriticalSection();

			/** Enter.
			  * \exception If the calling thread already possesses this critical section (it would be a dead-lock).
			  */
			void  enter() const;

			/** Leave
			  * \exception If the calling thread is not the current owener of the critical section.
			  */
			void  leave() const;

			/** Returns the name used in the constructor. */
			std::string getName() const { return m_name; }

			/** If set to a non-NULL value, debug messages regarding the calling threads IDs will be output.
			  */
			CStream		*m_debugOut;
		};

		/** A class acquiring a CCriticalSection at its constructor, and releasing it at destructor.
		  *   It is a better idea to always use CCriticalSectionLocker, since it is more secure in the case of possible exceptions, many different exit points from a function, etc.. : it will always release the critical section at the destructor.
		  *    Example:
		  *  \code
		  *		{  // Code in this scope is protected by critical section
		  *			CCriticalSectionLocker  myCSLocker( &myCS );
		  *			...
		  *		}  // End of code protected by critical section
		  *  \endcode
		  *  \sa CCriticalSection
		  */
		class CCriticalSectionLocker
		{
		protected:
			const CCriticalSection	*m_cs;

		public:
			/** Constructor: enters the critical section.
			  */
			CCriticalSectionLocker( const CCriticalSection *cs  );

			CCriticalSectionLocker(const CCriticalSectionLocker &o) : m_cs(o.m_cs)
			{
			}

			CCriticalSectionLocker & operator = (const CCriticalSectionLocker&o)
			{
				m_cs = o.m_cs;
				return *this;
			}

			/** Destructor: leaves the critical section.
			  */
			~CCriticalSectionLocker();

		}; // end of CCriticalSectionLocker

		/** This class provides a simple way of waiting for and signaling events.
		  */
		class	CEvent
		{
		private:
			CReferencedMemBlock		m_data;

		public:
			/** Constructor: set the initial signaled state of the event.
			  */
			CEvent( bool initialSignaled );

			/** Destructor
			  */
			~CEvent();

			/** Signal the event: the first waiting thread resumes execution (if no thread is waiting, the object keeps signaled)
			  */
			void  signal();

			/** Waits for the event to be signaled.
			  */
			void  wait();

			/** Manual reset of the event, without waiting to a signaled state (without effect if it is currently not signaled)
			  */
			void  reset();
		};

		/** A semaphore.
		  * The state of a semaphore object is signaled when its count is greater than zero,
		  *  and nonsignaled when its count is equal to zero. The initialCount parameter specifies
		  *  the initial count. Each time a waiting thread is released because of the semaphore's
		  *  signaled state, the count of the semaphore is decreased by one. Use the release function
		  *  to increment a semaphore's count by a specified amount. The count can never be less
		  *   than zero or greater than the value specified in the maxCount parameter.
		  */
		class CSemaphore
		{
		protected:
			CReferencedMemBlock		m_data;

        public:
            /** Creates a semaphore.
              */
            CSemaphore(
                unsigned int    initialCount,
                unsigned int    maxCount,
                const std::string &name=std::string("") );

            /** Destructor
              */
            ~CSemaphore();

            /** Blocks until the count of the semaphore to be non-zero.
              * \param timeout_ms The timeout in milliseconds, or set to zero to wait indefinidely.
              * \return true if the semaphore has been signaled, false on timeout or any other error.
              */
            bool waitForSignal( unsigned int timeout_ms = 0 );

            /** Increments the count of the semaphore by a given amount.
              */
            void release(unsigned int increaseCount = 1);

		};

	} // End of namespace

} // End of namespace

#endif
