#ifndef __COUNTABLE_H__
#define __COUNTABLE_H__

#include <wx/thread.h>
#if wxCHECK_VERSION(2, 9, 0)
#include <wx/atomic.h>
#endif
#include "VrutApi.h"

namespace VRUT
{

	// Type of counting structure
	typedef volatile unsigned int CounterType;

	/// ReferenceCounter class should have the following interface
	/// ReferenceCounter::ReferenceCounter() - Creates new object with initial number of references set to 0
	/// explicit ReferenceCounter::ReferenceCounter(initial_count) - Creates new object with specified initial number of references
	/// CounterType ReferenceCounter::GetCount() - Returns number of references
	/// void ReferenceCounter::Inc() - Increments number of references by one
	/// CounterType ReferenceCounter::Dec() - Decrements number of references by one and returns new number of references

	class VRUT_CORE_API IntReferenceCounter
	{
	protected:
		CounterType count;
	public:
		IntReferenceCounter(): count(0) {}
		explicit IntReferenceCounter(CounterType initial_count): count(initial_count) {}

		inline CounterType GetCount()	{	return count;		}
		inline void Inc()				{	++count;			}
		inline CounterType Dec()		{	return --count;		}

	private:
		// Copy constructor and assign operator are not allowed
		IntReferenceCounter(const IntReferenceCounter &);
		IntReferenceCounter &operator=(const IntReferenceCounter &);
	};

#if wxCHECK_VERSION(2, 9, 0)
	class VRUT_CORE_API wxAtomicReferenceCounter
	{
	protected:
		CounterType count;
	public:
		wxAtomicReferenceCounter(): count(0) {}
		explicit wxAtomicReferenceCounter(CounterType initial_count): count(initial_count) {}

		inline CounterType GetCount()	{	return count;							}
		inline void Inc()				{	wxAtomicInc((wxUint32 &)count);			}
		inline CounterType Dec()		{	return wxAtomicDec((wxUint32 &)count);	}

	private:
		// Copy constructor and assign operator are not allowed
		wxAtomicReferenceCounter(const wxAtomicReferenceCounter &);
		wxAtomicReferenceCounter &operator=(const wxAtomicReferenceCounter &);
	};
#endif

	class VRUT_CORE_API wxMutexReferenceCounter
	{
	protected:
		wxCriticalSection mutex;
		CounterType count;
	public:
		wxMutexReferenceCounter(): count(0) {}
		explicit wxMutexReferenceCounter(CounterType initial_count): count(initial_count) {}

		inline CounterType GetCount()		{	return count;	}
		inline void Inc()
		{
			wxCriticalSectionLocker lock(mutex);
			count++;
		}

		inline CounterType Dec()
		{
			CounterType val;
			wxCriticalSectionLocker lock(mutex);
			count--;
			val = count;
			return val;
		}
	private:
		// Copy constructor and assign operator are not allowed
		wxMutexReferenceCounter(const wxMutexReferenceCounter &);
		wxMutexReferenceCounter &operator =(const wxMutexReferenceCounter &);
	};

	template <class Counter>
	class CountableCounterTemplate;

	template<class Counter>
	class smart_pointer_base;

	template <class Counter>
	class CountableCounterTemplate
	{
	public:
		// Number of references is zero when the object is created
		CountableCounterTemplate(): counter(0) {}
		// Number of references is zero even if the object is copied
		CountableCounterTemplate(const CountableCounterTemplate<Counter>& /*countable*/): counter(0) {}

		virtual ~CountableCounterTemplate() {}

		// When assigned, number of references does not change
		CountableCounterTemplate<Counter>& operator=(const CountableCounterTemplate<Counter> &c) {return *this;}

	protected:
		Counter counter;

		// Increments number of references and returns new number of references
		inline void inc()
		{
			this->counter.Inc();
		}

		// Decrements number of references and returns new number of references
		inline CounterType dec()
		{
			return this->counter.Dec();
		}

		template <class T>
		friend class smart_pointer_base;
	};

#if wxCHECK_VERSION(2, 9, 0)
	typedef VRUT_CORE_API CountableCounterTemplate<wxAtomicReferenceCounter>	CountableThreadSafe;
#else
	typedef VRUT_CORE_API CountableCounterTemplate<wxMutexReferenceCounter>		CountableThreadSafe;
#endif
	typedef VRUT_CORE_API CountableCounterTemplate<IntReferenceCounter>		CountableThreadUnsafe;
	// Don't forget you may use the template it self, or you can define you own Countable class

	typedef CountableThreadSafe Countable;

}

class VRUTCondition
{
public:
	VRUTCondition(wxCriticalSection& mutex) : m_mutex(mutex)
	{
		// another thread can't access it until we return from ctor, so no need to
		// protect access to m_numWaiters here
		m_numWaiters = 0;
	}

	bool IsOk() const { return m_semaphore.IsOk(); }

	wxCondError Wait()
	{
		// increment the number of waiters
		{
			wxCriticalSectionLocker lock(m_csWaiters);
			m_numWaiters++;
		}

		m_mutex.Leave();

		// after unlocking the mutex other threads may Signal() us, but it is ok
		// now as we had already incremented m_numWaiters so Signal() will post the
		// semaphore and decrement m_numWaiters back even if it is called before we
		// start to Wait()
		const wxSemaError err = m_semaphore.Wait();

		m_mutex.Enter();

		if ( err == wxSEMA_NO_ERROR )
		{
			// m_numWaiters was decremented by Signal()
			return wxCOND_NO_ERROR;
		}

		// but in case of an error we need to do it manually
		{
			wxCriticalSectionLocker lock(m_csWaiters);
			m_numWaiters--;
		}

		return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR;
	}

	wxCondError WaitTimeout(unsigned long milliseconds)
	{
		{
			wxCriticalSectionLocker lock(m_csWaiters);
			m_numWaiters++;
		}

		m_mutex.Leave();

		wxSemaError err = m_semaphore.WaitTimeout(milliseconds);

		m_mutex.Enter();

		if ( err == wxSEMA_NO_ERROR )
			return wxCOND_NO_ERROR;

		if ( err == wxSEMA_TIMEOUT )
		{
			// a potential race condition exists here: it happens when a waiting
			// thread times out but doesn't have time to decrement m_numWaiters yet
			// before Signal() is called in another thread
			//
			// to handle this particular case, check the semaphore again after
			// acquiring m_csWaiters lock -- this will catch the signals missed
			// during this window
			wxCriticalSectionLocker lock(m_csWaiters);

			err = m_semaphore.WaitTimeout(0);
			if ( err == wxSEMA_NO_ERROR )
				return wxCOND_NO_ERROR;

			// we need to decrement m_numWaiters ourselves as it wasn't done by
			// Signal()
			m_numWaiters--;

			return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR;
		}

		// undo m_numWaiters++ above in case of an error
		{
			wxCriticalSectionLocker lock(m_csWaiters);
			m_numWaiters--;
		}

		return wxCOND_MISC_ERROR;
	}

	wxCondError Signal()
	{
		wxCriticalSectionLocker lock(m_csWaiters);

		if ( m_numWaiters > 0 )
		{
			// increment the semaphore by 1
			if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
				return wxCOND_MISC_ERROR;

			m_numWaiters--;
		}

		return wxCOND_NO_ERROR;
	}

	wxCondError Broadcast()
	{
		wxCriticalSectionLocker lock(m_csWaiters);

		while ( m_numWaiters > 0 )
		{
			if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
				return wxCOND_MISC_ERROR;

			m_numWaiters--;
		}

		return wxCOND_NO_ERROR;
	}

private:
	// the number of threads currently waiting for this condition
	long m_numWaiters;

	// the critical section protecting m_numWaiters
	wxCriticalSection m_csWaiters;

	wxCriticalSection& m_mutex;
	wxSemaphore m_semaphore;
#if wxCHECK_VERSION(2, 9, 0)
	wxDECLARE_NO_COPY_CLASS(VRUTCondition);
#endif
};

#endif	// __COUNTABLE_H__
