/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.7 $
 *********************************************************************/

#ifndef _SMARTPTR_H
#define _SMARTPTR_H

#include "Object.h"

namespace util
{
	/**
	 * A smart pointer class. SmartPtr provides much the same
	 * functionality as its STL counterpart auto_ptr. The main
	 * difference is that it is derived from Object. It also has some
	 * sophisticated features that are missing from auto_ptr.
	 **/
	template <class T> class SmartPtr : public Object
	{
	public:
		/**
		 * Copy a SmartPtr instance. If <i>other</i> owns its internal
		 * pointer, the ownership of the pointer is transferred to *this.
		 **/
		SmartPtr(const SmartPtr& other) :
			_ptr(other._ptr), _bOwner(other._bOwner), _bArray(other._bArray) { other._bOwner = false; }

		/**
		 * Initialize the internal pointer with <i>ptr</i>. If
		 * <i>owner</i> is true (as it is by default), the new SmartPtr
		 * instance becomes the owner of the pointer, and deletes it
		 * automatically if its ownership is not explicitly released. If
		 * <i>array</i> is true, then delete[] is used instead of delete
		 * when the pointer needs to be destroyed.
		 **/
		explicit SmartPtr(T* ptr = NULL, bool owner = true, bool array = false) :
			_ptr(ptr), _bOwner(owner), _bArray(array) {}

		/**
		 * Destroy a SmartPtr instance and delete the the internal pointer
		 * if it is owned by *this.
		 **/
		~SmartPtr()
		{
			if (_bOwner) deletePtr();
		}

		/**
		 * Typecast to a boolean value. The result is true, if the
		 * internal pointer differs from NULL.
		 **/
		//operator bool() const { return (_ptr != NULL); }
		
		/**
		 * Set the internal pointer to a value. If the current pointer is
		 * owned by *this, it is deleted. *this becomes the owner of the
		 * given pointer. The type (i.e. array/not array) of the internal
		 * pointer is assumed to remain intact.
		 **/
		void reset(T* ptr = NULL)
		{
			if (_bOwner && ptr != _ptr) deletePtr();
			_ptr = ptr;
			_bOwner = true;
		}

		/**
		 * Release the ownership of the internal pointer and return it.
		 * The internal pointer remains usable, but it is no longer owned
		 * by *this.
		 **/
		T* release() const
		{
			_bOwner = false;
			return _ptr;
		}

		/**
		 * Return the internal pointer.
		 **/
		T* get() { return _ptr; }
		
		/**
		 * Return the internal pointer.
		 **/
		const T* get() const { return _ptr; }

		/**
		 * Cast this SmartPtr to a pointer to the type of the internal
		 * pointer. This method simply returns the intenal pointer.
		 **/
		operator T* () { return _ptr; }
		/**
		 * Cast this SmartPtr to a pointer to the type of the internal
		 * pointer. This method simply returns the intenal pointer. (Const
		 * version)
		 **/
		operator const T* () const { return _ptr; }

		/**
		 * Cast this SmartPtr to a reference of the type of the internal
		 * pointer.
		 **/
		operator T& () { return *_ptr; }
		/**
		 * Cast this SmartPtr to a reference of the type of the internal
		 * pointer. (Const version)
		 **/
		operator const T& () const { return *_ptr; }

		/**
		 * Get a reference to the object pointed to by the internal
		 * pointer.
		 **/
		T& operator* () { return *_ptr; }

		/**
		 * Return the internal pointer.
		 **/
		T* operator-> () { return _ptr; }

		/**
		 * Return the internal pointer. Const version.
		 **/
		const T* operator-> () const { return _ptr; }

		/**
		 * Set the internal pointer to a value.
		 *
		 * @see #reset(T*)
		 **/
		SmartPtr& operator= (T* ptr) { reset(ptr); return *this; }

		/**
		 * Copy another SmartPtr instance. If *this is owner of its
		 * internal pointer, it is deleted first. If <i>other</i> is owner
		 * of its internal pointer, the ownership is transferred to *this.
		 * As a result, both SmartPtr instances point to the same memory
		 * location.
		 **/
		SmartPtr& operator= (const SmartPtr& other)
		{
			if (_bOwner && _ptr != other._ptr) deletePtr();
			_bOwner = other._bOwner;
			_ptr = other._ptr;
			_bArray = other._bArray;
			if (&other != this)
				other._bOwner = false;
			return *this;
		}

		/**
		 * Compare two SmartPtrs. Returns true if and only if the internal
		 * pointers are the same.
		 **/
		bool operator== (const SmartPtr<T>& other) const
		{
			return _ptr == other._ptr;
		}

		/**
		 * Set the ownership of the internal pointer.
		 *
		 * @param owner if true, this SmartPtr is allowed to release its
		 * internal pointer
		 **/
		void setOwner(bool owner) { _bOwner = owner; }

		/**
		 * Check whether the internal pointer is an array. If it is,
		 * delete[] is used instead of delete when destroying the pointer.
		 **/
		bool isArray() const { return _bArray; }

		/**
		 * Set the type of the internal pointer.
		 *
		 * @param array if true, delete[] is used instead of delete (the
		 * default) when destroying the internal pointer
		 **/
		void setArray(bool array) { _bArray = array; }

	private:
		T* _ptr;

		mutable bool _bOwner;
		bool _bArray;

		void deletePtr()
		{
			if (_bArray)
				delete[] _ptr;
			else
				delete _ptr;
		}
	};

}

#endif
