#ifndef _DESHAREDPTR_HPP
#define _DESHAREDPTR_HPP
/*-------------------------------------------------------------------------
 * drawElements C++ Base Library
 * -----------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Shared pointer.
 *//*--------------------------------------------------------------------*/

#include "deDefs.hpp"
#include "deAtomic.h"

#include <exception>
#include <algorithm>

namespace de
{

//! Shared pointer self-test.
void SharedPtr_selfTest (void);

class DeadReferenceException : public std::exception
{
public:
	DeadReferenceException (void) throw() : std::exception() {}
	const char* what (void) const throw() { return "DeadReferenceException"; }
};

template<bool threadSafe>
struct ReferenceCount;

template<> struct ReferenceCount<true>	{ typedef volatile int	Type; };
template<> struct ReferenceCount<false>	{ typedef int			Type; };

template<class Deleter, bool threadSafe>
struct SharedPtrState
{
	SharedPtrState (Deleter deleter_)
		: strongRefCount	(0)
		, weakRefCount		(0)
		, deleter			(deleter_)
	{
	}

	typename ReferenceCount<threadSafe>::Type	strongRefCount;
	typename ReferenceCount<threadSafe>::Type	weakRefCount;		//!< WeakPtr references + StrongPtr references.
	Deleter										deleter;
};

template<typename DstDeleterType, typename SrcDeleterType, bool threadSafe>
SharedPtrState<DstDeleterType, threadSafe>* sharedPtrStateCast (SharedPtrState<SrcDeleterType, threadSafe>* state)
{
	return reinterpret_cast<SharedPtrState<DstDeleterType, threadSafe>*>(state);
}

template<typename T, class Deleter, bool threadSafe>
class SharedPtr;

template<typename T, class Deleter, bool threadSafe>
class WeakPtr;

/*--------------------------------------------------------------------*//*!
 * \brief Shared pointer
 *
 * SharedPtr is smart pointer for managing shared ownership to a pointer.
 * Multiple SharedPtr's can maintain ownership to the pointer and it is
 * destructed when last SharedPtr is destroyed.
 *
 * Shared pointers can be assigned (or initialized using copy constructor)
 * and in such case the previous reference is first freed and then a new
 * reference to the new pointer is acquired.
 *
 * SharedPtr can also be empty.
 *
 * If threadSafe template parameter is set to true, it is safe to share
 * data using SharedPtr across threads. SharedPtr object itself is not
 * thread safe and should not be mutated from multiple threads simultaneously.
 *
 * \todo [2012-10-26 pyry] Add custom deleter.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter = DefaultDeleter<T>, bool threadSafe = true>
class SharedPtr
{
public:
								SharedPtr			(void);
								SharedPtr			(const SharedPtr<T, Deleter, threadSafe>& other);

	template<typename Y>
	explicit					SharedPtr			(Y* ptr, Deleter deleter = Deleter());

	template<typename Y, class DeleterY>
	explicit					SharedPtr			(const SharedPtr<Y, DeleterY, threadSafe>& other);

	template<typename Y, class DeleterY>
	explicit					SharedPtr			(const WeakPtr<Y, DeleterY, threadSafe>& other);

								~SharedPtr			(void);

	template<typename Y, class DeleterY>
	SharedPtr&					operator=			(const SharedPtr<Y, DeleterY, threadSafe>& other);
	SharedPtr&					operator=			(const SharedPtr<T, Deleter, threadSafe>& other);

	template<typename Y, class DeleterY>
	SharedPtr&					operator=			(const WeakPtr<Y, DeleterY, threadSafe>& other);

	T*							get					(void) const throw() { return m_ptr;	}	//!< Get stored pointer.
	T*							operator->			(void) const throw() { return m_ptr;	}	//!< Get stored pointer.
	T&							operator*			(void) const throw() { return *m_ptr;	}	//!< De-reference pointer.

	operator					bool				(void) const throw() { return !!m_ptr;	}

	void						swap				(SharedPtr<T, Deleter, threadSafe>& other);

	void						clear				(void);

	template<typename Y, class DeleterY>
	operator SharedPtr<Y, DeleterY, threadSafe>	(void) const;

private:
	void						acquire				(void);
	void						acquireFromWeak		(const WeakPtr<T, Deleter, threadSafe>& other);
	void						release				(void);

	T*												m_ptr;
	SharedPtrState<Deleter, threadSafe>*			m_state;

	friend class WeakPtr<T, Deleter, threadSafe>;

	template<typename U, class DeleterU, bool threadSafeU>
	friend class SharedPtr;
};

/*--------------------------------------------------------------------*//*!
 * \brief Weak pointer
 *
 * WeakPtr manages weak references to objects owned by SharedPtr. Shared
 * pointer can be converted to weak pointer and vice versa. Weak pointer
 * differs from SharedPtr by not affecting the lifetime of the managed
 * object.
 *
 * WeakPtr can be converted back to SharedPtr but that operation can fail
 * if the object is no longer live. In such case DeadReferenceException
 * will be thrown.
 *
 * \todo [2012-10-26 pyry] Add custom deleter.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter = DefaultDeleter<T>, bool threadSafe = true>
class WeakPtr
{
public:
						WeakPtr				(void);
						WeakPtr				(const WeakPtr<T, Deleter, threadSafe>& other);
	explicit			WeakPtr				(const SharedPtr<T, Deleter, threadSafe>& other);
						~WeakPtr			(void);

	WeakPtr&			operator=			(const WeakPtr<T, Deleter, threadSafe>& other);
	WeakPtr&			operator=			(const SharedPtr<T, Deleter, threadSafe>& other);

	SharedPtr<T, Deleter, threadSafe>	lock	(void);

private:
	void				acquire				(void);
	void				release				(void);

	T*										m_ptr;
	SharedPtrState<Deleter, threadSafe>*	m_state;

	friend class SharedPtr<T, Deleter, threadSafe>;
};

// SharedPtr template implementation.

/*--------------------------------------------------------------------*//*!
 * \brief Construct empty shared pointer.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline SharedPtr<T, Deleter, threadSafe>::SharedPtr (void)
	: m_ptr		(DE_NULL)
	, m_state	(DE_NULL)
{
}

/*--------------------------------------------------------------------*//*!
 * \brief Construct shared pointer from pointer.
 * \param ptr Pointer to be managed.
 *
 * Ownership of the pointer will be transferred to SharedPtr and future
 * SharedPtr's initialized or assigned from this SharedPtr.
 *
 * Y* must be convertible to T*.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
template<typename Y>
inline SharedPtr<T, Deleter, threadSafe>::SharedPtr (Y* ptr, Deleter deleter)
	: m_ptr		(DE_NULL)
	, m_state	(DE_NULL)
{
	try
	{
		m_ptr	= ptr;
		m_state	= new SharedPtrState<Deleter, threadSafe>(deleter);
		m_state->strongRefCount	= 1;
		m_state->weakRefCount	= 1;
	}
	catch (...)
	{
		delete m_ptr;
		delete m_state;
		throw;
	}
}

/*--------------------------------------------------------------------*//*!
 * \brief Initialize shared pointer from another SharedPtr.
 * \param other Pointer to be shared.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline SharedPtr<T, Deleter, threadSafe>::SharedPtr (const SharedPtr<T, Deleter, threadSafe>& other)
	: m_ptr		(other.m_ptr)
	, m_state	(other.m_state)
{
	acquire();
}

/*--------------------------------------------------------------------*//*!
 * \brief Initialize shared pointer from another SharedPtr.
 * \param other Pointer to be shared.
 *
 * Y* must be convertible to T*.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
template<typename Y, class DeleterY>
inline SharedPtr<T, Deleter, threadSafe>::SharedPtr (const SharedPtr<Y, DeleterY, threadSafe>& other)
	: m_ptr		(other.m_ptr)
	, m_state	(sharedPtrStateCast<Deleter>(other.m_state))
{
	acquire();
}

/*--------------------------------------------------------------------*//*!
 * \brief Initialize shared pointer from weak reference.
 * \param other Pointer to be shared.
 *
 * Y* must be convertible to T*.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
template<typename Y, class DeleterY>
inline SharedPtr<T, Deleter, threadSafe>::SharedPtr (const WeakPtr<Y, DeleterY, threadSafe>& other)
	: m_ptr		(DE_NULL)
	, m_state	(DE_NULL)
{
	acquireFromWeak(other);
}

template<typename T, class Deleter, bool threadSafe>
inline SharedPtr<T, Deleter, threadSafe>::~SharedPtr (void)
{
	release();
}

/*--------------------------------------------------------------------*//*!
 * \brief Assign from other shared pointer.
 * \param other Pointer to be shared.
 * \return Reference to this SharedPtr.
 *
 * Reference to current pointer (if any) will be released first. Then a new
 * reference to the pointer managed by other will be acquired.
 *
 * Y* must be convertible to T*.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
template<typename Y, class DeleterY>
inline SharedPtr<T, Deleter, threadSafe>& SharedPtr<T, Deleter, threadSafe>::operator= (const SharedPtr<Y, DeleterY, threadSafe>& other)
{
	if (*this == other)
		return *this;

	// Release current reference.
	release();

	// Copy from other and acquire reference.
	m_ptr	= other.m_ptr;
	m_state	= sharedPtrStateCast<Deleter>(other.m_state);

	acquire();

	return *this;
}

/*--------------------------------------------------------------------*//*!
 * \brief Assign from other shared pointer.
 * \param other Pointer to be shared.
 * \return Reference to this SharedPtr.
 *
 * Reference to current pointer (if any) will be released first. Then a new
 * reference to the pointer managed by other will be acquired.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline SharedPtr<T, Deleter, threadSafe>& SharedPtr<T, Deleter, threadSafe>::operator= (const SharedPtr<T, Deleter, threadSafe>& other)
{
	if (*this == other)
		return *this;

	// Release current reference.
	release();

	// Copy from other and acquire reference.
	m_ptr	= other.m_ptr;
	m_state	= other.m_state;

	acquire();

	return *this;
}

/*--------------------------------------------------------------------*//*!
 * \brief Assign from weak pointer.
 * \param other Weak reference.
 * \return Reference to this SharedPtr.
 *
 * Reference to current pointer (if any) will be released first. Then a
 * reference to pointer managed by WeakPtr is acquired if the pointer
 * is still live (eg. there's at least one strong reference).
 *
 * If pointer is no longer live, DeadReferenceException is thrown.
 *
 * Y* must be convertible to T*.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
template<typename Y, class DeleterY>
inline SharedPtr<T, Deleter, threadSafe>& SharedPtr<T, Deleter, threadSafe>::operator= (const WeakPtr<Y, DeleterY, threadSafe>& other)
{
	// Release current reference.
	release();

	m_ptr	= DE_NULL;
	m_state	= DE_NULL;

	acquireFromWeak(other);

	return *this;
}

/*--------------------------------------------------------------------*//*!
 * \brief Type conversion operator.
 *
 * T* must be convertible to Y*. Since resulting SharedPtr will share the
 * ownership destroying Y* must be equal to destroying T*.
 *//*--------------------------------------------------------------------*/
template<class T, class Deleter, bool threadSafe>
template<typename Y, class DeleterY>
inline SharedPtr<T, Deleter, threadSafe>::operator SharedPtr<Y, DeleterY, threadSafe> (void) const
{
	return SharedPtr<Y, DeleterY, threadSafe>(*this);
}

/*--------------------------------------------------------------------*//*!
 * \brief Compare pointers.
 * \param a A
 * \param b B
 * \return true if A and B point to same object, false otherwise.
 *//*--------------------------------------------------------------------*/
template<class T, class DeleterT, bool threadSafeT, class U, class DeleterU, bool threadSafeU>
inline bool operator== (const SharedPtr<T, DeleterT, threadSafeT>& a, const SharedPtr<U, DeleterU, threadSafeU>& b) throw()
{
	return a.get() == b.get();
}

/*--------------------------------------------------------------------*//*!
 * \brief Compare pointers.
 * \param a A
 * \param b B
 * \return true if A and B point to different objects, false otherwise.
 *//*--------------------------------------------------------------------*/
template<class T, class DeleterT, bool threadSafeT, class U, class DeleterU, bool threadSafeU>
inline bool operator!= (const SharedPtr<T, DeleterT, threadSafeT>& a, const SharedPtr<U, DeleterU, threadSafeU>& b) throw()
{
	return a.get() != b.get();
}

/** Swap pointer contents. */
template<typename T, class Deleter, bool threadSafe>
inline void SharedPtr<T, Deleter, threadSafe>::swap (SharedPtr<T, Deleter, threadSafe>& other)
{
	using std::swap;
	swap(m_ptr,		other.m_ptr);
	swap(m_state,	other.m_state);
}

/** Swap operator for SharedPtr's. */
template<typename T, class Deleter, bool threadSafe>
inline void swap (SharedPtr<T, Deleter, threadSafe>& a, SharedPtr<T, Deleter, threadSafe>& b)
{
	a.swap(b);
}

/*--------------------------------------------------------------------*//*!
 * \brief Set pointer to null.
 *
 * clear() removes current reference and sets pointer to null value.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline void SharedPtr<T, Deleter, threadSafe>::clear (void)
{
	release();
	m_ptr	= DE_NULL;
	m_state	= DE_NULL;
}

template<typename T, class Deleter, bool threadSafe>
inline void SharedPtr<T, Deleter, threadSafe>::acquireFromWeak (const WeakPtr<T, Deleter, threadSafe>& weakRef)
{
	DE_ASSERT(!m_ptr && !m_state);

	SharedPtrState<Deleter, threadSafe>* state = weakRef.m_state;

	if (!state)
		return; // Empty reference.

	if (threadSafe)
	{
		int oldCount, newCount;

		// Do atomic compare and increment.
		do
		{
			oldCount = state->strongRefCount;
			if (oldCount == 0)
				throw DeadReferenceException();
			newCount = oldCount+1;
		} while (deAtomicCompareExchange32((deUint32 volatile*)&state->strongRefCount, (deUint32)oldCount, (deUint32)newCount) != (deUint32)oldCount);

		deAtomicIncrement32(&state->weakRefCount);
	}
	else
	{
		if (state->strongRefCount == 0)
			throw DeadReferenceException();

		state->strongRefCount	+= 1;
		state->weakRefCount		+= 1;
	}

	m_ptr	= weakRef.m_ptr;
	m_state	= state;
}

template<typename T, class Deleter, bool threadSafe>
inline void SharedPtr<T, Deleter, threadSafe>::acquire (void)
{
	if (m_state)
	{
		if (threadSafe)
		{
			deAtomicIncrement32((deInt32 volatile*)&m_state->strongRefCount);
			deAtomicIncrement32((deInt32 volatile*)&m_state->weakRefCount);
		}
		else
		{
			m_state->strongRefCount	+= 1;
			m_state->weakRefCount	+= 1;
		}
	}
}

template<typename T, class Deleter, bool threadSafe>
inline void SharedPtr<T, Deleter, threadSafe>::release (void)
{
	if (m_state)
	{
		if (threadSafe)
		{
			if (deAtomicDecrement32(&m_state->strongRefCount) == 0)
			{
				m_state->deleter(m_ptr);
				m_ptr = DE_NULL;
			}

			if (deAtomicDecrement32(&m_state->weakRefCount) == 0)
			{
				delete m_state;
				m_state = DE_NULL;
			}
		}
		else
		{
			m_state->strongRefCount	-= 1;
			m_state->weakRefCount	-= 1;
			DE_ASSERT(m_state->strongRefCount >= 0 && m_state->weakRefCount >= 0);

			if (m_state->strongRefCount == 0)
			{
				m_state->deleter(m_ptr);
				m_ptr = DE_NULL;
			}

			if (m_state->weakRefCount == 0)
			{
				delete m_state;
				m_state = DE_NULL;
			}
		}
	}
}

// WeakPtr template implementation.

/*--------------------------------------------------------------------*//*!
 * \brief Construct empty weak pointer.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline WeakPtr<T, Deleter, threadSafe>::WeakPtr (void)
	: m_ptr		(DE_NULL)
	, m_state	(DE_NULL)
{
}

/*--------------------------------------------------------------------*//*!
 * \brief Construct weak pointer from other weak reference.
 * \param other Weak reference.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline WeakPtr<T, Deleter, threadSafe>::WeakPtr (const WeakPtr<T, Deleter, threadSafe>& other)
	: m_ptr		(other.m_ptr)
	, m_state	(other.m_state)
{
	acquire();
}

/*--------------------------------------------------------------------*//*!
 * \brief Construct weak pointer from shared pointer.
 * \param other Shared pointer.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline WeakPtr<T, Deleter, threadSafe>::WeakPtr (const SharedPtr<T, Deleter, threadSafe>& other)
	: m_ptr		(other.m_ptr)
	, m_state	(other.m_state)
{
	acquire();
}

template<typename T, class Deleter, bool threadSafe>
inline WeakPtr<T, Deleter, threadSafe>::~WeakPtr (void)
{
	release();
}

/*--------------------------------------------------------------------*//*!
 * \brief Assign from another weak pointer.
 * \param other Weak reference.
 * \return Reference to this WeakPtr.
 *
 * The current weak reference is removed first and then a new weak reference
 * to the object pointed by other is taken.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline WeakPtr<T, Deleter, threadSafe>& WeakPtr<T, Deleter, threadSafe>::operator= (const WeakPtr<T, Deleter, threadSafe>& other)
{
	if (this == &other)
		return *this;

	release();

	m_ptr	= other.m_ptr;
	m_state	= other.m_state;

	acquire();

	return *this;
}

/*--------------------------------------------------------------------*//*!
 * \brief Assign from shared pointer.
 * \param other Shared pointer.
 * \return Reference to this WeakPtr.
 *
 * The current weak reference is removed first and then a new weak reference
 * to the object pointed by other is taken.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter, bool threadSafe>
inline WeakPtr<T, Deleter, threadSafe>& WeakPtr<T, Deleter, threadSafe>::operator= (const SharedPtr<T, Deleter, threadSafe>& other)
{
	release();

	m_ptr	= other.m_ptr;
	m_state	= other.m_state;

	acquire();

	return *this;
}

template<typename T, class Deleter, bool threadSafe>
inline void WeakPtr<T, Deleter, threadSafe>::acquire (void)
{
	if (m_state)
	{
		if (threadSafe)
			deAtomicIncrement32(&m_state->weakRefCount);
		else
			m_state->weakRefCount += 1;
	}
}

template<typename T, class Deleter, bool threadSafe>
inline void WeakPtr<T, Deleter, threadSafe>::release (void)
{
	if (m_state)
	{
		if (threadSafe)
		{
			if (deAtomicDecrement32(&m_state->weakRefCount) == 0)
			{
				delete m_state;
				m_state	= DE_NULL;
				m_ptr	= DE_NULL;
			}
		}
		else
		{
			m_state->weakRefCount -= 1;
			DE_ASSERT(m_state->weakRefCount >= 0);

			if (m_state->weakRefCount == 0)
			{
				delete m_state;
				m_state	= DE_NULL;
				m_ptr	= DE_NULL;
			}
		}
	}
}

} // de

#endif // _DESHAREDPTR_HPP