/** @file autoref.h
*	Custom automatic refcounting for EABaseTypes, based on SafeCom but more appropriate to our needs
*	@author Chris Burns (cburns@ea.com)
*
*	EABaseTypes
*	(c) 2012 Electronic Arts Inc.
*/

#ifndef EABASETYPES_AUTOREF_H_
#define EABASETYPES_AUTOREF_H_

#include "EABase/eabase.h"

namespace EA
{
	namespace Types
	{
		template<typename T>
		class AutoRefBase
		{
		public:

			inline T* GetUnsafePointer() const { return mUnsafePointer; }
			inline T** GetUnsafeHandle() { return &mUnsafePointer; }

			inline T* operator->() const { return mUnsafePointer; }
			inline T& operator*() { return *mUnsafePointer; }
			inline T& operator*() const { return *mUnsafePointer; }

			inline bool operator==(intptr_t unsafePointer) const { return mUnsafePointer == reinterpret_cast<T*>(unsafePointer); }
			inline bool operator!=(intptr_t unsafePointer) const { return mUnsafePointer != reinterpret_cast<T*>(unsafePointer); }

			template<typename RelatedT>
			inline bool operator==(const RelatedT* unsafePointer) const { return mUnsafePointer == unsafePointer; }

			template<typename RelatedT>
			inline bool operator!=(const RelatedT* unsafePointer) const { return mUnsafePointer != unsafePointer; }

			template<typename RelatedT>
			inline bool operator==(const AutoRefBase<RelatedT>& other) const { return mUnsafePointer == other.mUnsafePointer; }

			template<typename RelatedT>
			inline bool operator!=(const AutoRefBase<RelatedT>& other) const { return mUnsafePointer != other.mUnsafePointer; }

			T* mUnsafePointer;

		protected:

			inline AutoRefBase():mUnsafePointer(0) {}
			inline AutoRefBase(T* unsafePointer):mUnsafePointer(unsafePointer) {}
		};

		template<typename T>
		class AutoRefIn;

		template<typename T>
		class AutoRef : public AutoRefBase<T>
		{
		public:

			inline AutoRef():AutoRefBase<T>() {}

			inline AutoRef(T* unsafePointer):AutoRefBase<T>(unsafePointer) {}

			template<typename RelatedT>
			inline AutoRef(RelatedT* unsafePointer):AutoRefBase<T>(unsafePointer) {}

			inline ~AutoRef()
			{
				if(this->mUnsafePointer != 0) { this->mUnsafePointer->Release(); }
			}

			inline AutoRef(const AutoRef<T>& other):AutoRefBase<T>(other.mUnsafePointer)
			{
				if(other.mUnsafePointer != 0) { other.mUnsafePointer->AddRef(); }
			}

			template<typename RelatedT>
			inline AutoRef(const AutoRef<RelatedT>& other):AutoRefBase<T>(other.mUnsafePointer)
			{
				if(other.mUnsafePointer != 0) { other.mUnsafePointer->AddRef(); }
			}

			template<typename RelatedT>
			inline AutoRef(AutoRefIn<RelatedT> other);

			inline AutoRef<T>& operator=(T* unsafePointer)
			{
				if(this->mUnsafePointer != 0) { this->mUnsafePointer->Release(); }
				this->mUnsafePointer = unsafePointer;
				return *this;
			}

			template<typename RelatedT>
			inline AutoRef<T>& operator=(RelatedT* unsafePointer)
			{
				if(this->mUnsafePointer != 0) { this->mUnsafePointer->Release(); }
				this->mUnsafePointer = unsafePointer;
				return *this;
			}

			inline AutoRef<T>& operator=(const AutoRef<T>& other)
			{
				if(other.mUnsafePointer != 0) { other.mUnsafePointer->AddRef(); }
				if(this->mUnsafePointer != 0) { this->mUnsafePointer->Release(); }
				this->mUnsafePointer = other.mUnsafePointer;
				return *this;
			}

			template<typename RelatedT>
			inline AutoRef<T>& operator=(const AutoRef<RelatedT>& other)
			{
				if(other.mUnsafePointer != 0) { other.mUnsafePointer->AddRef(); }
				if(this->mUnsafePointer != 0) { this->mUnsafePointer->Release(); }
				this->mUnsafePointer = other.mUnsafePointer;
				return *this;
			}

			template<typename RelatedT>
			inline AutoRef<T>& operator=(AutoRefIn<RelatedT> other);

			template<typename RelatedT>
			inline operator AutoRefIn<RelatedT>() const;
		};

		template<typename T>
		class AutoRefIn : public AutoRefBase<T>
		{
		public:

			explicit inline AutoRefIn(T* unsafePointer):AutoRefBase<T>(unsafePointer) {}

			template<typename RelatedT>
			explicit inline AutoRefIn(RelatedT* unsafePointer):AutoRefBase<T>(unsafePointer) {}

			//inline AutoRefIn(const AutoRef<T>& other):AutoRefBase<T>(other.mUnsafePointer) {}

			//template<typename RelatedT>
			//inline AutoRefIn(const AutoRef<RelatedT>& other):AutoRefBase<T>(other.mUnsafePointer) {}

			template<typename RelatedT>
			inline operator AutoRefIn<RelatedT>() const { return AutoRefIn<RelatedT>(this->mUnsafePointer); }
			//inline AutoRefIn(AutoRefIn<RelatedT> other):AutoRefBase<T>(other.mUnsafePointer) {}
		};

		template<typename T>
		template<typename RelatedT>
		/* inline */ AutoRef<T>::AutoRef(AutoRefIn<RelatedT> other):AutoRefBase<T>(other.mUnsafePointer)
		{
			if(other.mUnsafePointer != 0) { other.mUnsafePointer->AddRef(); }
		}

		template<typename T>
		template<typename RelatedT>
		/* inline */ AutoRef<T>& AutoRef<T>::operator=(AutoRefIn<RelatedT> other)
		{
			if(other.mUnsafePointer != 0) { other.mUnsafePointer->AddRef(); }
			if(this->mUnsafePointer != 0) { this->mUnsafePointer->Release(); }
			this->mUnsafePointer = other.mUnsafePointer;
			return *this;
		}

		template<typename T>
		template<typename RelatedT>
		/* inline */ AutoRef<T>::operator AutoRefIn<RelatedT>() const { return AutoRefIn<RelatedT>(this->mUnsafePointer); }

		template<typename T>
		inline bool operator==(int lhs, const AutoRefBase<T>& rhs)
		{
			return reinterpret_cast<T*>(lhs) == rhs.mUnsafePointer;
		}

		template<typename T>
		inline bool operator!=(int lhs, const AutoRefBase<T>& rhs)
		{
			return reinterpret_cast<T*>(lhs) != rhs.mUnsafePointer;
		}

		template<typename RelatedT, typename T>
		inline bool operator==(RelatedT* lhs, const AutoRefBase<T>& rhs)
		{
			return lhs == rhs.mUnsafePointer;
		}

		template<typename RelatedT, typename T>
		inline bool operator!=(RelatedT* lhs, const AutoRefBase<T>& rhs)
		{
			return lhs != rhs.mUnsafePointer;
		}
	}
}

#define EABASETYPES_REFERENCE_DECLARATION(__type)\
	typedef       ::EA::Types::AutoRef      <      __type>           __type ## Ref;\
	typedef       ::EA::Types::AutoRef      <const __type>  Const ## __type ## Ref;\
	typedef       ::EA::Types::AutoRefIn    <      __type>           __type ## In;\
	typedef       ::EA::Types::AutoRefIn    <const __type>  Const ## __type ## In;\
	typedef       ::EA::Types::AutoRef      <      __type>&          __type ## Out;\
	typedef       ::EA::Types::AutoRef      <const __type>& Const ## __type ## Out;\
	typedef                __type ## Ref                             __type ## Return;\
	typedef       Const ## __type ## Ref                    Const ## __type ## Return;

#define EABASETYPES_FORWARD_DECLARATION(__kind, __type)\
	__kind __type;\
	EABASETYPES_REFERENCE_DECLARATION(__type)

#endif // EABASETYPES_AUTOREF_H_
