/***************************************************************************
 *   Copyright (C) 2004 by ian reinhart geiser                             *
 *   ian@geiseri.com                                                       *
 *                                                                         *
 *   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.             *
 ***************************************************************************/
#ifndef REFERENCE_H
#define REFERENCE_H
#include <qdebug.h>

/**
 * A simple object to track the source pointer and how
 * many Reference objects are watching it.
 */
template<class T>
class ReferenceTracker
{
public:
	ReferenceTracker( T *pointer ): m_ptr(pointer), m_count(1)
	{}
	T *m_ptr;
	unsigned int m_count;
};

template<class T>
class Reference{
	mutable class ReferenceTracker<T> *m_tracker;
public:

	Reference() : m_tracker(0)
	{
	}
	explicit Reference( T *ptr )
	{
		if( ptr != 0)
		{
			m_tracker = new ReferenceTracker<T>(ptr);
		}
	}

	T *ptr( ) const
	{
		return m_tracker->m_ptr;
	}

	Reference(  const Reference &copy ) : m_tracker(0)
	{
		if( this != &copy)
		{
			releaseReference();
			aquireReference(copy.m_tracker);
		}
	}

	~Reference( )
	{
		releaseReference();
	}

	bool operator==( const Reference &right )
	{
		if( m_tracker == 0 && right.m_tracker == 0)
			return true;
		else if(m_tracker == 0 || right.m_tracker == 0 )
			return false;
		else
			return m_tracker->m_ptr == right.m_tracker->m_ptr;
	}

	Reference &operator=( const Reference &copy )
	{
		if( this != &copy)
		{
			releaseReference();
			aquireReference(copy.m_tracker);
		}
		return *this;
	}

	Reference &operator=(  T *copy )
	{
		if( isValid() )
		{
			if( m_tracker->m_ptr != copy)
			{
				releaseReference();
				if( copy != 0)
				{
					m_tracker = new ReferenceTracker<T>(copy);
				}
			}
		}
		else
			m_tracker = new ReferenceTracker<T>(copy);
		return *this;
	}

	T *operator->() const
	{
		if( m_tracker == 0 )
			return 0;
		return m_tracker->m_ptr;
	}

	bool isValid() const
	{
		if( m_tracker == 0 )
			return false;
		if( m_tracker->m_ptr == 0)
			return false;
		return true;
	}

	template<class C >
	Reference<C> reference_cast( )
	{
		aquireReference( m_tracker );
		return Reference<C>( ptr() );
	}


private:
	T &operator*() const;

	void aquireReference( ReferenceTracker<T> *tracker )
	{
		m_tracker = tracker;
		if( m_tracker )
		{
			++(m_tracker->m_count);
		}
	}

	void releaseReference( )
	{
		if( isValid() )
		{
			if( --(m_tracker->m_count) == 0 )
			{
				delete m_tracker->m_ptr;
				delete m_tracker;
			}
			m_tracker = 0;
		}
	}


};

#endif

