
#pragma once
#ifndef Auto_Ptr_HPP
#define Auto_Ptr_HPP

#include "Stuff.hpp"


namespace Stuff
{

// Auto_Ptr<>: similar to the STL auto_ptr<> class, but better! :)

// An Auto_Ptr<> is a pointer that "owns" the object pointed to.
// When the Auto_Ptr<> is deleted, it will also delete the object
// if it still owns the pointer.  Auto_Ptr<> also handles the transfer
// of ownership between Auto_Ptrs via the == operators.

// This version of Auto_Ptr<> is slightly more memory-efficient as it
// stores the ownership bits in the unused bits of the pointer data member.
// It also allows you to specify whether or not to use array deletion;
// this also is stored in the extra bits of the pointer.


	template <class T>
	class Auto_Ptr
	{
	  public:
		Auto_Ptr();
		explicit Auto_Ptr(T* ptr, bool delete_as_array = DELETE_NORMAL);
		Auto_Ptr(Auto_Ptr const& src);
		~Auto_Ptr();

		enum deletion_type
		{
			DELETE_AS_ARRAY = true,
			DELETE_NORMAL = false
		};

		Auto_Ptr<T> Assimilate(T* ptr, bool delete_as_array = DELETE_NORMAL);
		Auto_Ptr<T> Assimilate(Auto_Ptr<T>& src);

		T* GetPointer() const;

		T* Release() const;
		T* ReleaseAndNull();

		Auto_Ptr<T>& operator=(Auto_Ptr<T> const& src);
		
		operator bool() const;
		T& operator*() const;
		T* operator->() const;

		void Swap(Auto_Ptr<T>& src);

		void Delete();

	  private:
		bool IsOwner() const;
		bool IsArray() const;

		void SetAsOwner(bool fOwn) const;
		void SetAsArray(bool fArray);
		void Set(T* ptr, bool fIsOwner, bool fIsArray);

		mutable union
		{
			T *m_ptr;
			mutable int unsigned m_bits;
		};

		enum
		{
			OWNER_MASK = 1,
			ARRAY_MASK = 2,
			POINTER_MASK = ~3
		};
	};

	template<class T>
	inline bool operator==(const Auto_Ptr<T>& a,
						   const Auto_Ptr<T>& b)
	{
		return (a.GetPointer() == b.GetPointer());
	}

	template<class T>
	inline bool operator==(const T* a,
						   const Auto_Ptr<T>& b)
	{
		return (a == b.GetPointer());
	}

	template<class T>
	inline bool operator==(const Auto_Ptr<T>& a,
						   const T* b)
	{
		return (a.GetPointer() == b);
	}

}; // namespace Stuff


template <class T>
Stuff::Auto_Ptr<T>::Auto_Ptr()
	: m_ptr(0)
{
	Verify(sizeof(T*) == sizeof(unsigned int));
}

template <class T>
Stuff::Auto_Ptr<T>::Auto_Ptr(T* ptr, bool delete_as_array)
{
	Verify(sizeof(T *) == sizeof(unsigned int));
	Set(ptr,true,delete_as_array);
}

template <class T>
Stuff::Auto_Ptr<T>::Auto_Ptr(Auto_Ptr const& src)
{
	Verify(sizeof(T*) == sizeof(unsigned int));
	m_ptr = src.m_ptr;
	src.SetAsOwner(false);
}

template <class T>
Stuff::Auto_Ptr<T>::~Auto_Ptr()
{
	Delete();
}

template <class T>
Stuff::Auto_Ptr<T> Stuff::Auto_Ptr<T>::Assimilate(T* ptr, bool delete_as_array)
{
	Auto_Ptr<T> temp(*this);
	Set(ptr,true,delete_as_array);
	return (temp);
}

template <class T>
Stuff::Auto_Ptr<T> Stuff::Auto_Ptr<T>::Assimilate(Auto_Ptr<T>& src)
{
	Auto_Ptr<T> temp;
	if (&src != this)
	{
		temp = *this;
		m_ptr = src.m_ptr;
		src.SetAsOwner(false);
	}

	return (temp);
}

template <class T>
T* Stuff::Auto_Ptr<T>::GetPointer() const
{
	return ((T*)(m_bits & POINTER_MASK));
}

template <class T>
T* Stuff::Auto_Ptr<T>::Release() const
{
	SetAsOwner(false);
	return (GetPointer());
}

template <class T>
T* Stuff::Auto_Ptr<T>::ReleaseAndNull()
{
	T* rv = GetPointer();
	m_ptr = 0;
	return (rv);
}

template <class T>
Stuff::Auto_Ptr<T>& Stuff::Auto_Ptr<T>::operator=(Auto_Ptr<T> const& src)
{
	if (&src != this)
	{
		Delete();
		m_ptr = src.m_ptr;
		src.SetAsOwner(false);
	}

	return (*this);
}
		
template <class T>
Stuff::Auto_Ptr<T>::operator bool() const
{
	return (GetPointer() != 0);
}

template <class T>
T& Stuff::Auto_Ptr<T>::operator*() const
{
	return (*GetPointer());
}

template <class T>
T* Stuff::Auto_Ptr<T>::operator->() const
{
	return (GetPointer());
}

template <class T>
void Stuff::Auto_Ptr<T>::Swap(Auto_Ptr<T>& src)
{
	if (this != &src)
	{
		T* temp_ptr = src.m_ptr;
		src.m_ptr = m_ptr;
		m_ptr = temp_ptr;
	}
}

template <class T>
void Stuff::Auto_Ptr<T>::Delete()
{
	if (IsOwner())
	{
		if (IsArray())
		{
			m_bits &= POINTER_MASK;
			delete [] m_ptr;
		}
		else
		{
			m_bits &= POINTER_MASK;
			delete m_ptr;
		}
	}

	m_ptr = 0;
}

template <class T>
bool Stuff::Auto_Ptr<T>::IsOwner() const
{
	return ((m_bits & OWNER_MASK) == OWNER_MASK);
}

template <class T>
bool Stuff::Auto_Ptr<T>::IsArray() const
{
	return ((m_bits & ARRAY_MASK) == ARRAY_MASK);
}

template <class T>
void Stuff::Auto_Ptr<T>::SetAsOwner(bool fOwn) const
{
	if (fOwn == true)
	{
		m_bits |= OWNER_MASK;
	}
	else
	{
		m_bits &= ~OWNER_MASK;
	}
}

template <class T>
void Stuff::Auto_Ptr<T>::SetAsArray(bool fArray)
{
	if (fArray == true)
	{
		m_bits |= ARRAY_MASK;
	}
	else
	{
		m_bits &= ~ARRAY_MASK;
	}
}

template <class T>
void Stuff::Auto_Ptr<T>::Set(T* ptr, bool fIsOwner, bool fIsArray)
{
	m_ptr = ptr;
	Verify((m_bits & POINTER_MASK) == m_bits);

	if (fIsOwner == true)
	{
		m_bits |= OWNER_MASK;
	}

	if (fIsArray == true)
	{
		m_bits |= ARRAY_MASK;
	}
}


#endif // Auto_Ptr_HPP