/*****************************************************************************
 * $Id: gfx_shared_ptr_2.hh,v 1.5 2006/08/08 03:45:39 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Template class for sharing objects by pointer.
 * @verbatim
 *
 * Which to use?  SharedVal or SharedPtr?
 * --------------------------------------
 * See gfx_shared_val.hh.
 *
 * Overview:
 * ---------
 * This is a template class for sharing objects by pointer using a reference count.
 * It has members containing a pointer to an object and a reference count.
 * Sharing is done by copying SharedPtr objects.
 * Copying is fast since SharedPtr objects are small.
 * Unsharing a count is done by deleting a SharedPtr object.
 * Only when the last SharedPtr is deleted (the original or the last copy)
 * will the actual object be deleted.
 *
 * Usage:
 * ------
 * Sharing an object is done by copying SharedPtr objects.
 * Copying a SharedPtr increments a reference count of the object being shared.
 *
 *     SharedPtr<Object> shared1( new Object );
 *     SharedPtr<Object> shared2 = shared1;  // copy constructor
 *
 * Passing a SharedPtr to a function is implicit copying,
 * so the caller and callee shares the object.
 * A SharedPtr should be passed by value (not as a pointer/reference):
 *
 *     void Func( SharedPtr<Object> obj );   // right
 *     void Func( SharedPtr<Object>& obj );  // wrong, inhibits necessary copying method
 *
 * Directly assigning a SharedPtr with a pointer unshares one count of the outgoing object,
 * and resets the SharedPtr's reference count to 1 for the incoming object.
 *
 *     SharedPtr<Object> ptr1( new Object );
 *     SharedPtr<Object> ptr2 = ptr1;		// ptr1 and ptr2 share one Object (refCnt=2)
 *     ptr2 = new Object;					// ptr2 points to different Object (refCnt=1, refCnt=1)
 * 
 * Caveats/Warnings:
 * -----------------
 * Clients must never explicitly delete the shared object.
 *
 * @endverbatim
 *//*
 * LEGAL:	COPYRIGHT (C) JIM E. BROOKS 2005
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

// (Not enclosed by #ifndef as it is reincluded by gfx_shared_ptr.hh)

namespace gfx {

////////////////////////////////////////////////////////////////////////////////
/// @brief Template class for sharing objects by pointer.
/// @verbatim
///                 SharedPtr( void );
///                 SharedPtr( T* obj );
///					SharedPtr( const SharedPtr<T>& src );
///					~SharedPtr();
///	SharedPtr<T>&	operator=( const SharedPtr<T>& src );
///	SharedPtr<T>&	operator=( T* obj );
/// T*              operator->( void ) const;
/// T&              operator*( void ) const;
/// bool            operator==( const SharedPtr& other ) const;
/// bool            operator!=( const SharedPtr& other ) const;
/// bool            operator==( const T* obj ) const;
/// bool            operator!=( const T* obj ) const;
/// @endverbatim
///
template<typename T>
class SharedPtr
{
public:
    // Default ctor constructs an invalid SharedPtr.
    SharedPtr( void )
    {
        Share1( NULL );  // reference count = 1
    }

public:
    // Constructor.  Initially share one count of an actual object.
    SharedPtr( T* obj )
    {
        Share1( obj );  // reference count = 1
    }

public:
    // Copy constructor.  Increment reference count of shared object.
    SharedPtr( const SharedPtr<T>& src )
    {
    	// Just copy.  This is a new object so it has nothing to unshare.
    	Copy( src );
    }

public:
    // Destructor.
    ~SharedPtr()
    {
    	Unshare();
        DEBUG_CODE( mObj = NULL; mRefCntPtr = NULL; )
    }

public:
    // Copy a SharedPtr into another.  Copying shares an object.
    SharedPtr<T>& operator=( const SharedPtr<T>& src )
    {
    	// Self-assignment is unexpected but not handling it would be folly.
    	if ( EX( &src != this ) )
    	{
    		// Unshare this destination before reassigning it.
    		Unshare();
    		Copy( src );
    	}
    	return *this;
    }

public:
    // Reassign this SharedPtr with a different pointer.
	// Unshare the outgoing pointer, and reset the reference count to 1
	// for the incoming pointer.
    SharedPtr<T>& operator=( T* obj )
    {
		// Unshare() might delete mRefCntPtr, Share1() will make a new one.
        Unshare();      // unshare outgoing pointer
        Share1( obj );  // assign with incoming pointer, reset reference count = 1
        return *this;
    }

public:
    T*   operator->( void ) const { return mObj; }
    T&   operator*( void ) const { return *mObj; }  // dereference, not multiply
    bool operator==( const SharedPtr& other ) const { return mObj == other.mObj; }
    bool operator!=( const SharedPtr& other ) const { return mObj != other.mObj; }
    bool operator==( const T* obj ) const { return mObj == obj; }
    bool operator!=( const T* obj ) const { return mObj != obj; }

private:
    // Directly copy a SharedPtr object.
    // @remarks Logically, the source should be valid.  However, in practice,
    // vector<SharedPtr>.resize() invokes the copy constructor, though you'd
    // think the STL would use only the default constructor.
    void Copy( const SharedPtr<T>& src )
    {
    	// Increment the source's reference count as this destination is a copy.
    	mObj		= src.mObj;
    	mRefCntPtr	= src.mRefCntPtr;
    	++(*src.mRefCntPtr);
    }

private:
    // Initialize/reset sharing with reference count = 1.
    void Share1( T* obj )
    {
    	mObj		= obj;
    	mRefCntPtr	= new int;			// reference count isn't tied to this object
    	*mRefCntPtr	= 1;				// initially one
    }

private:
    // Decrease reference count.
    void Unshare( void )
    {
    	// Default constructor creates a SharedPtr that points to nothing.
   		// Decrement the reference count.
   		// Don't delete shared resources until it hits zero.
   		if ( --(*mRefCntPtr) == 0 )
   		{
   			SharedPtrDeleteObj( mObj );	 // macro for delete or delete[]
#if ! SPEED
   	        DELETE_NULL( mRefCntPtr );   // for speed, let it leak a measly int
#endif
   		}
    }

private:
	T*			mObj;			///< the actual resource to share
	int*		mRefCntPtr;     ///< reference count isn't tied to this object

template<typename OBJ> friend OBJ* GET_PTR( const SharedPtr<OBJ> ptr );  // not "T"
template<typename OBJ> friend const OBJ* GET_PTRC( const SharedPtr<OBJ> ptr );
};

// Restricted usage.
template<typename T> T* GET_PTR( const SharedPtr<T> ptr ) { return ptr.mObj; }
template<typename T> const T* GET_PTRC( const SharedPtr<T> ptr ) { return ptr.mObj; }

} // namespace gfx
