/*
 * The UDS Collection
 * Copyright (C) 2000, 2001  Julian v. Bock
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *	(See file COPYING for more information)
 */

#ifndef _uds_refcounter_hh
#define _uds_refcounter_hh


namespace uds
{

/** RefCounter base class
	\author Julian c. Bock */
template < class Size_t = unsigned >
class RefCounter_b
{
public:
	/** default constructor */
	RefCounter_b() : refc( 0 ) {}
	
	/** copy constructor */
	RefCounter_b( const RefCounter_b& rhs ) : refc( 0 ) {}
	
	/** destructor */
	virtual
	~RefCounter_b() = 0;
	
	/** increases the reference counter */
	virtual void
	AddReference() { ++refc; }
	
	/** decreases the reference counter */
	virtual void
	RemoveReference() { if ( --refc == 0 ) delete this; }
	
	/** \return true when the object is shared */
	bool
	Shared() const { return refc > 1; }
	
	/** \return true when the object is shareable */
	virtual bool
	Shareable() const { return true; }
	
private:
	/// reference count
	volatile Size_t refc;	
};

template < class Size_t >
RefCounter_b< Size_t >::~RefCounter_b() {}


/** base class for objects that use reference counting
	\author Julian v. Bock */
template < class Size_t = unsigned, bool = false >
class RefCounter : public RefCounter_b< Size_t > {};

/** base class for objects that use reference counting
	\author Julian v. Bock */
template < class Size_t >
class RefCounter< Size_t, true >
{
public:
	/** default constructor */
	RefCounter() : shareable( true ) {}
	
	/** copy constructor */
	RefCounter( const RefCounter& rhs ) : shareable( true ) {}
	
	/** \return true when the object is shareable */
	virtual bool
	Shareable() const { return shareable; }
	
	/** marks the object as unshareable */
	void
	MarkUnshareable() { shareable = false; }
	
private:
	/// 'is shareable' flag
	volatile bool shareable;
};


/** smart garbage-collection pointer for classes that use reference counting
	\author Julian v. Bock */
template < class C, bool clonable = false >
class GC_Ptr
{
public:
	typedef C RefC_t;
	
	/** \param p wrapped object */
	GC_Ptr( C* p = 0 ) : ptr( p ) { init(); }
	
	/** copy constructor */
	GC_Ptr( const GC_Ptr& rhs ) : ptr( rhs.ptr ) { init(); }
	
	/** destructor */
	~GC_Ptr() { if ( ptr ) ptr->RemoveReference(); }
	
	bool
	operator == ( const GC_Ptr& rhs ) const { return ptr == rhs.ptr; }
	
	GC_Ptr&
	operator = ( const GC_Ptr& rhs )
	{
		if ( ptr != rhs.ptr )
		{
			if ( ptr ) ptr->RemoveReference();
			ptr = rhs.ptr;
			init();
		}
		return *this;
	}
	
	C&
	operator * () const { return *ptr; }
	
	C*
	operator -> () const { return ptr; }
	
	operator void* () const { return ptr; }
	
private:
	/// wrapped pointer
	C* ptr;

	/** inits the object */
	void
	init()
	{
		if ( !ptr ) return;
		if ( !ptr->Shareable() ) ptr = new C( *ptr );
		ptr->AddReference();
	}
};

/** smart garbage-collection pointer for classes that use reference counting
	and define Clone()
	\author Julian v. Bock */
template < class C >
class GC_Ptr< C, true >
{
public:
	typedef C RefC_t;
	
	/** \param p wrapped object */
	GC_Ptr( C* p = 0 ) : ptr( p ) { init(); }
	
	/** copy constructor */
	GC_Ptr( const GC_Ptr& rhs ) : ptr( rhs.ptr ) { init(); }
	
	/** destructor */
	~GC_Ptr() { if ( ptr ) ptr->RemoveReference(); }
	
	bool
	operator == ( const GC_Ptr& rhs ) const { return ptr == rhs.ptr; }
	
	GC_Ptr&
	operator = ( const GC_Ptr& rhs )
	{
		if ( ptr != rhs.ptr )
		{
			if ( ptr ) ptr->RemoveReference();
			ptr = rhs.ptr;
			init();
		}
		return *this;
	}
	
	GC_Ptr&
	operator * () const { return *ptr; }
	
	GC_Ptr*
	operator -> () const { return ptr; }
	
	operator void* () const { return ptr; }
	
private:
	/// wrapped pointer
	C* ptr;

	/** inits the object */
	void
	init()
	{
		if ( !ptr ) return;
		if ( !ptr->Shareable() ) ptr = ptr->Clone();
		ptr->AddReference();
	}
};

/** smart pointer for copy-on-write
	\author Julian v. Bock */
template < class C, bool clonable = false >
class CoW_Ptr
{
public:
	/** \param p wrapped object */
	CoW_Ptr( C* p = 0 ) : lc( p ) { init(); }
	
	/** copy constructor */
	CoW_Ptr( const CoW_Ptr& rhs ) : lc( rhs.lc ) { init(); }
	
	/** destructor */
	~CoW_Ptr() { if ( lc ) lc->RemoveReference(); }
	
	CoW_Ptr&
	operator = ( const CoW_Ptr& rhs ) { reset( rhs.lc ); return *this; }
	
	const C&
	operator * () const { return letter(); }
	
	C&
	operator * () { return letter(); }
	
	const C*
	operator -> () const { return &letter(); }
	
	C*
	operator -> () { return &letter(); }
	
protected:
	/** performs initialization */
	void
	init() { if ( lc ) lc->AddReference(); }
	
	/** provides acces to the letter object
		\return letter object */
	const C&
	letter() const { return *lc; }
	
	/** provides acces to the letter object
		\return letter object */
	C&
	letter() { if ( lc->Shared() ) reset( new C( *lc ) ); return *lc; }
	
	/** sets a new letter object
		\param l new letter object */
	void
	reset( C* l ) { if ( lc ) lc->RemoveReference(); lc = l; init(); }
	
private:
	/// letter class
	C* lc;
};

/** smart pointer for copy-on-write for classes that define Clone()
	\author Julian v. Bock */
template < class C >
class CoW_Ptr< C, true >
{
public:
	/** \param p wrapped object */
	CoW_Ptr( C* p = 0 ) : lc( p ) { init(); }
	
	/** copy constructor */
	CoW_Ptr( const CoW_Ptr& rhs ) : lc( rhs.lc ) { init(); }
	
	/** destructor */
	~CoW_Ptr() { if ( lc ) lc->RemoveReference(); }
	
	CoW_Ptr&
	operator = ( const CoW_Ptr& rhs ) { reset( rhs.lc ); return *this; }
	
	const C&
	operator * () const { return letter(); }
	
	C&
	operator * () { return letter(); }
	
	const C*
	operator -> () const { return &letter(); }
	
	C*
	operator -> () { return &letter(); }
	
protected:
	/** performs initialization */
	void
	init() { if ( lc ) lc->AddReference(); }
	
	/** provides acces to the letter object
		\return letter object */
	const C&
	letter() const { return *lc; }
	
	/** provides acces to the letter object
		\return letter object */
	C&
	letter() { if ( lc->Shared() ) reset( lc->Clone() ); return *lc; }
	
	/** sets a new letter object
		\param l new letter object */
	void
	reset( C* l ) { if ( lc ) lc->RemoveReference(); lc = l; init(); }
	
private:
	/// letter class
	C* lc;
};

}

#endif // _uds_refcounter_hh
