/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NSHARED_PTR__
#define __NSHARED_PTR__


		#include "thread/atomic_value.h"


namespace GS {

/// Weak reference to a shared object.
class	WeakRef
{
		friend class SharedObject;

		bool		is_valid;
		Threading::Atomic32 refc;

		WeakRef() : is_valid(true) {}

public:

		bool		IsValid() const { return is_valid; }

		int			GetRefCount() const { return refc.Get(); }

		void		AddRef() { refc.Inc(); }
		void		RemoveRef()
		{
			if	((refc.Dec() == 0) && !is_valid)
				delete this;
		}
};

/*!
	@short	Reference counted object.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
class	SharedObject
{
		Threading::Atomic32 refc;
		WeakRef		*weak_ref;

protected:

virtual	~SharedObject()
		{
			if	(weak_ref)
			{
				if	(weak_ref->refc.Get() == 0)
					delete weak_ref;
				else
					weak_ref->is_valid = false;
			}
		}

public:

		WeakRef		*GetWeakRef()
		{
			if	(!weak_ref)
				weak_ref = new WeakRef;		// TODO have a specialized, fixed pool allocator for these.
			return weak_ref;
		}

		int			GetRefCount() const { return refc.Get(); }

		void		AddRef() { refc.Inc(); }
		void		RemoveRef()
		{
			if	(refc.Dec() == 0)
			{
				if	(weak_ref)
					weak_ref->is_valid = false;
				delete this;
			}
		}

		SharedObject() : weak_ref(0) {}
};

/*!
	@short	Strong smart pointer to a reference counted object.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
template <class T> class SharedPtr
{
		T			*rc_o;

public:

inline	operator T	*() const { return rc_o; }
inline	T			*c_ptr() const { return rc_o; }

inline	bool		IsNull() const { return rc_o ? false : true; }
inline	bool		IsValid() const { return rc_o ? true : false; }

		void		Init()
		{
			if	(rc_o)
				rc_o->AddRef();
		}

inline	bool		operator == (const T *p) const { return rc_o == p; }
inline	bool		operator != (const T *p) const { return rc_o != p; }
inline	bool		operator == (T *p) const { return rc_o == p; }
inline	bool		operator != (T *p) const { return rc_o != p; }

inline	bool		operator == (const SharedPtr &p) const { return rc_o == p.rc_o; }
inline	bool		operator != (const SharedPtr &p) const { return rc_o != p.rc_o; }

		T			*operator = (T *p)
		{
			if	(rc_o != p)
			{
				if	(rc_o)
					rc_o->RemoveRef();
				rc_o = p;
				Init();
			}
			return p;
		}
		SharedPtr <T> &operator = (const SharedPtr &p)
		{
			*this = p.c_ptr();
			return *this;
		}
inline	T			*operator -> () const { return rc_o; }
inline	T			&operator * () const { return *rc_o; }

		explicit SharedPtr(T *p) : rc_o(p) { Init(); }
		SharedPtr(const SharedPtr &p) : rc_o(p.rc_o) { Init(); }
		SharedPtr() : rc_o(0) {}

		~SharedPtr()
		{
			if	(rc_o)
				rc_o->RemoveRef();
		}
};

} // GS


#endif	// __NSHARED_PTR__
