#ifndef __SMARTPTR__H__
#define __SMARTPTR__H__

// TODO Consider smart_pointer_*::operator T*()
// TODO Consider P * smart_pointer_*::get_with_dynamic_cast<P>() { return dynamic_cast<P> ptr; }

#include "common.h"

namespace VRUT
{

	/// smart_pointer_base class encapsulates basic functionality of smart pointers
	template<class T>
	class smart_pointer_base
	{
	public:
		/// Destructor is not virtual (to be fast), but derived classes usually don't need to write their own destructor
		~smart_pointer_base(void) {
			///release the current pointer
			reset();
		}

		/// An assign functions to assign new pointer to this instance
		void assign(T* p) {
			assign_base(p);
		}
		template<class P> void assign(P* p) {
			assign_base(p);			// Allow only child -> parent assignments
		}
		template<class P> void assign_with_dynamic_cast(P* p) {
			assign_base(dynamic_cast<T *>(p));		// Allow only assignments which has sence
		}
		template<class P> void assign_with_static_cast(P* p) {
			assign_base(static_cast<T *>(p));
		}
		template<class P> void assign_with_safe_static_cast(P* p) {
			assign_base(SafeStaticCast<T *>(p));
		}
		// Note that there is no assign(smart_pointer_base<T> ), since it would allow for example
		// smart_pointer_v2.assign(const_smart_pointer), which is not desirable

		void reset(void) {
#ifdef NDEBUG
			T* tmpptr= this->ptr;
			this->ptr = NULL;
			if (tmpptr && (tmpptr->dec() == 0))
				delete tmpptr;
#else
			if ((this->ptr) && (this->ptr->dec() == 0))
			{
				delete this->ptr;
				//wxThread::Sleep(1);//uncomment this line if you want to try to find wrong usage of smart_pointer
			}
			this->ptr = NULL;
#endif
		}

#ifdef VRUT_COMPILER_SUPPORTS_EXPLICIT_CONVERSION_OPERATORS
		// C++11 supports "explicit operator bool() ...", however, some compilers (e.g. VS2010)
		// still don't support it. For those who support it, the following code is enough

		/// Conversion to bool allows the use this class in conditional statements
		explicit operator bool() const {
			return (ptr!=NULL);
		}
#else
		// Implement safe-bool idiom http://www.artima.com/cppsource/safebool.html
		// Without it and without explicit operator bool (), it is possible to write
		//
		//	Object *obj;					int i;		i = obj;		// Illogical, won't compile
		//	smart_pointer<Object> obj;		int i;		i = obj;		// Illogicat, but compilable, operator bool is called
		//
		// or
		//
		//	scene->SetTransformation(node, Matrix());	// SetTransformation requires NODE_ID as first parameter.
		//												// The programmer forgot to use node->GetID(), however,
		//												// the compiler compiled the code without any problems, it used
		//												// operator bool.
		//
	private:
        typedef T* smart_pointer_base::*bool_type;
    public:
		/// Conversion to bool allows the use this class in conditional statements
        operator bool_type() const		{	return (ptr != nullptr) ? &smart_pointer_base::ptr : nullptr;	}
#endif

		inline bool operator == (const smart_pointer_base<T> &p) const		{	return ptr == p.ptr;	}
		inline bool operator == (const T *p) const							{	return ptr == p;		}
		friend inline bool operator == (const T *p, const smart_pointer_base<T> &s)		{	return s.ptr == p;	}
		inline bool operator != (const smart_pointer_base<T> &p) const		{	return ptr != p.ptr;	}
		inline bool operator != (const T *p) const							{	return ptr != p;		}
		friend inline bool operator != (const T *p, const smart_pointer_base<T> &s)		{	return s.ptr != p;	}

		inline bool operator < (const smart_pointer_base<T> &p) const		{	return ptr < p.ptr;		}

	protected:
		T* ptr;

		/// Basic constructor - Constructor is not public because this is just a base class
		smart_pointer_base(T * p = NULL): ptr(NULL) {
			acquire(p);
		}

		// UNRESOLVED Does (p != this->ptr) work in EVERY case (including child to parent assignments etc.)?
		// Is it working even in the following construct?
		// class A: public Countable;
		// class B: public A;
		// B* b = new B;			// Zero references
		// smart_pointer_*<A> a;
		// a = b;					// One reference
		// a = b;					// Is (b == (A*)b)? If no then 'b' object is lost.
		void assign_base(T* p)
		{
			if (p != this->ptr) {	
				reset();
				acquire(p);
			}
		}

		void acquire(T* p) {
			if (p) {
				p->inc();
			}
			this->ptr=p;
		}

	private:
		/// Copy constructor - Constructor is private, basic constructor should be enough
		smart_pointer_base(const smart_pointer_base<T> & other): ptr(NULL) {
			acquire(other.ptr);
		}
		/// Copy constructor - Constructor is private, basic constructor should be enough
		template <class P>
		smart_pointer_base(const smart_pointer_base<P> & other): ptr(NULL) {
			acquire(other.getPtr());			// Allow only child -> parent assignments
		}
	};

	/// smart_pointer_v2 - standard smart pointer
	template <class T>
	class smart_pointer_v2: public smart_pointer_base<T>
	{
	public:
		smart_pointer_v2(T *p = NULL): smart_pointer_base<T>::smart_pointer_base(p) {}
		smart_pointer_v2(const smart_pointer_v2<T> &other): smart_pointer_base<T>::smart_pointer_base(other.ptr) {}
		template <class P> smart_pointer_v2(const smart_pointer_v2<P> &other): smart_pointer_base<T>::smart_pointer_base(other.getPtr()) {}

		smart_pointer_v2(smart_pointer_v2<T> && other): smart_pointer_base<T>::smart_pointer_base(nullptr)
		{
			this->ptr = other.ptr;
			other.ptr = nullptr;
		}
		template <class P> smart_pointer_v2(smart_pointer_v2<P> && other): smart_pointer_base<T>::smart_pointer_base(nullptr)
		{
			this->ptr = other.ptr;			// Allow only child -> parent assignments
			other.ptr = nullptr;
		}

		///assignment operators
		smart_pointer_v2<T> & operator=(T *p) {
			this->assign(p);
			return *this;
		}
		template <class P> smart_pointer_v2<T> & operator=(P *p) {
			this->assign(p);					// Allow only child -> parent assignments
			return *this;
		}
		smart_pointer_v2<T> & operator=(const smart_pointer_v2<T> & other) {
			this->assign(other.ptr);
			return *this;
		}
		template <class P> smart_pointer_v2<T> & operator=(const smart_pointer_v2<P> & other) {
			this->assign(other.getPtr());		// Allow only child -> parent assignments
			return *this;
		}
		smart_pointer_v2<T> & operator=(smart_pointer_v2<T> && other) {
			this->reset();
			this->ptr = other.ptr;
			other.ptr = nullptr;
			return *this;
		}
		template <class P> smart_pointer_v2<T> & operator=(smart_pointer_v2<P> && other) {
			this->reset();
			this->ptr = other.ptr;				// Allow only child -> parent assignments
			other.ptr = nullptr;
			return *this;
		}

		///overload of a generic pointer conversion
		// UNRESOLVED That is the purpose? There are assignment operators and constructors
		//template <class P> operator smart_pointer_v2<P>() const {
		//	return smart_pointer_v2<P>(ptr);			// Allow only child -> parent assignments
		//}

		///overloading the pointer access operator to provide encapsulated access to the carried pointer's members
		T* operator->() const		{	return smart_pointer_base<T>::ptr;		}
		T &operator *() const		{	return *smart_pointer_base<T>::ptr;	}
		T* getPtr() const			{	return smart_pointer_base<T>::ptr;		}

		// Surprisingly, these methods must be defined since smart_pointer_base::assign_with_dynamic_cast
		// is not visible if smart_pointer_v2::assign_with_dynamic_cast is defined
		template<class P> void assign_with_dynamic_cast(P* p) {
			smart_pointer_base<T>::assign_with_dynamic_cast(p);
		}
		template<class P> void assign_with_static_cast(P* p) {
			smart_pointer_base<T>::assign_with_static_cast(p);
		}
		template<class P> void assign_with_safe_static_cast(P* p) {
			smart_pointer_base<T>::assign_with_safe_static_cast(p);
		}

		template<class P> void assign_with_dynamic_cast(const smart_pointer_v2<P> & other) {
			smart_pointer_base<T>::assign_with_dynamic_cast(other.getPtr());
		}
		template<class P> void assign_with_dynamic_cast(smart_pointer_v2<P> && other) {
			this->reset();
			this->ptr = dynamic_cast<T *>(other.ptr);
			other.ptr = nullptr;
		}

		template<class P> void assign_with_static_cast(const smart_pointer_v2<P> & other) {
			smart_pointer_base<T>::assign_with_static_cast(other.getPtr());
		}
		template<class P> void assign_with_static_cast(smart_pointer_v2<P> && other) {
			this->reset();
			this->ptr = static_cast<T *>(other.ptr);
			other.ptr = nullptr;
		}

		template<class P> void assign_with_safe_static_cast(const smart_pointer_v2<P> & other) {
			smart_pointer_base<T>::assign_with_safe_static_cast(other.getPtr());
		}
		template<class P> void assign_with_safe_static_cast(smart_pointer_v2<P> && other) {
			this->reset();
			this->ptr = SafeStaticCast<T *>(other.ptr);
			other.ptr = nullptr;
		}


		template <class P> friend class smart_pointer_v2;		// Because of move constructor and move operator
	};

	/// const_smart_pointer - smart pointer which returns pointers to constant objects only
	/// note that const_smart_pointer = smart_pointer_v2 is allowed, but not the reverse direction
	template <class T>
	class const_smart_pointer: public smart_pointer_base<T>
	{
	public:
		// TODO Add move constructors and move assign operator

		const_smart_pointer(T *p = NULL): smart_pointer_base<T>::smart_pointer_base(p) {}
		const_smart_pointer(const const_smart_pointer<T> &other): smart_pointer_base<T>::smart_pointer_base(other.ptr) {}
		template <class P> const_smart_pointer(const const_smart_pointer<P> &other): smart_pointer_base<T>::smart_pointer_base(other.ptr) {}
		const_smart_pointer(const smart_pointer_v2<T> &other): smart_pointer_base<T>::smart_pointer_base(other.getPtr()) {}
		template <class P> const_smart_pointer(const smart_pointer_v2<P> &other): smart_pointer_base<T>::smart_pointer_base(other.getPtr()) {}

		///assignment operators
		const_smart_pointer<T> & operator=(T *p) {
			assign(p);
			return *this;
		}
		template <class P> const_smart_pointer<T> & operator=(P *p) {
			assign(p);					// Allow only child -> parent assignments
			return *this;
		}
		const_smart_pointer<T> & operator=(const const_smart_pointer<T> & other) {
			this->assign(other.ptr);
			return *this;
		}
		template <class P> const_smart_pointer<T> & operator=(const const_smart_pointer<P> & other) {
			this->assign(other.ptr);			// Allow only child -> parent assignments
			return *this;
		}
		const_smart_pointer<T> & operator=(const smart_pointer_v2<T> & other) {
			this->assign(other.getPtr());
			return *this;
		}
		template <class P> const_smart_pointer<T> & operator=(const smart_pointer_v2<P> & other) {
			this->assign(other.getPtr());		// Allow only child -> parent assignments
			return *this;
		}

		///overload of a generic pointer conversion
		// UNRESOLVED That is the purpose? There are assignment operators and constructors
		//template <class P> operator const_smart_pointer<P>() const {
		//	return const_smart_pointer<P>(ptr);			// Allow only child -> parent assignments
		//}

		///overloading the pointer access operator to provide encapsulated access to the carried pointer's members
		/// const object is returned
		const T* operator->() const		{	return smart_pointer_base<T>::ptr;		}
		const T &operator *() const		{	return *smart_pointer_base<T>::ptr;	}
		const T* getPtr() const			{	return smart_pointer_base<T>::ptr;		}

		// Surprisingly, these methods must be defined since smart_pointer_base::assign_with_dynamic_cast
		// is not visible if const_smart_pointer::assign_with_dynamic_cast is defined
		template<class P> void assign_with_dynamic_cast(P* p) {
			smart_pointer_base<T>::assign_with_dynamic_cast(p);
		}
		template<class P> void assign_with_static_cast(P* p) {
			smart_pointer_base<T>::assign_with_static_cast(p);
		}
		template<class P> void assign_with_safe_static_cast(P* p) {
			smart_pointer_base<T>::assign_with_safe_static_cast(p);
		}

		template<class P> void assign_with_dynamic_cast(const const_smart_pointer<P> & other) {
			smart_pointer_base<T>::assign_with_dynamic_cast(other.ptr);
		}
		template<class P> void assign_with_dynamic_cast(const smart_pointer_v2<P> & other) {
			smart_pointer_base<T>::assign_with_dynamic_cast(other.getPtr());
		}

		template<class P> void assign_with_static_cast(const const_smart_pointer<P> & other) {
			smart_pointer_base<T>::assign_with_static_cast(other.ptr);
		}
		template<class P> void assign_with_static_cast(const smart_pointer_v2<P> & other) {
			smart_pointer_base<T>::assign_with_static_cast(other.getPtr());
		}

		template<class P> void assign_with_safe_static_cast(const const_smart_pointer<P> & other) {
			smart_pointer_base<T>::assign_with_safe_static_cast(other.ptr);
		}
		template<class P> void assign_with_safe_static_cast(const smart_pointer_v2<P> & other) {
			smart_pointer_base<T>::assign_with_safe_static_cast(other.getPtr());
		}

		template <class P> friend class const_smart_pointer;
		//template <class T> friend class smart_pointer;			// Remove this once old smart_pointer is not used in VRUT
		template <class P> friend class smart_pointer;			// Remove this once old smart_pointer is not used in VRUT
	};

	/// smart_pointer is a smart pointer which simulates behaviour of the first version of VRUT::smart_pointer
	template <class T>
	class smart_pointer: public smart_pointer_base<T>
	{
	public:
		smart_pointer(T *p = NULL): smart_pointer_base<T>::smart_pointer_base(p) {}
		smart_pointer(const smart_pointer<T> &other): smart_pointer_base<T>::smart_pointer_base(other.ptr) {}
		template <class P> smart_pointer(const smart_pointer<P> &other): smart_pointer_base<T>::smart_pointer_base((T*)other.ptr) {}
		smart_pointer(const smart_pointer_v2<T> &other): smart_pointer_base<T>::smart_pointer_base(other.getPtr()) {}
		template <class P> smart_pointer(const smart_pointer_v2<P> &other): smart_pointer_base<T>::smart_pointer_base((T*)other.getPtr()) {}
		smart_pointer(const const_smart_pointer<T> &other): smart_pointer_base<T>::smart_pointer_base(other.ptr) {}
		template <class P> smart_pointer(const const_smart_pointer<P> &other): smart_pointer_base<T>::smart_pointer_base((T*)other.ptr) {}

		smart_pointer(smart_pointer<T> && other): smart_pointer_base<T>::smart_pointer_base(nullptr)
		{
			this->ptr = other.ptr;
			other.ptr = nullptr;
		}
		template <class P> smart_pointer(smart_pointer<P> && other): smart_pointer_base<T>::smart_pointer_base(nullptr)
		{
			this->ptr = (T*)other.ptr;
			other.ptr = nullptr;
		}

		///assignment operators
		smart_pointer<T> & operator=(T *p) {
			smart_pointer_base<T>::assign(p);
			return *this;
		}
		template <class P> smart_pointer<T> & operator=(P *p) {
			smart_pointer_base<T>::assign((T*)p);
			return *this;
		}
		smart_pointer<T> & operator=(const smart_pointer<T> & other) {
			smart_pointer_base<T>::assign(other.ptr);
			return *this;
		}
		template <class P> smart_pointer<T> & operator=(const smart_pointer<P> & other) {
			smart_pointer_base<T>::assign((T*)other.ptr);
			return *this;
		}
		smart_pointer<T> & operator=(smart_pointer<T> && other) {
			this->reset();
			this->ptr = other.ptr;
			other.ptr = nullptr;
			return *this;
		}
		template <class P> smart_pointer<T> & operator=(smart_pointer<P> && other) {
			this->reset();
			this->ptr = (T*)other.ptr;
			other.ptr = nullptr;
			return *this;
		}
		smart_pointer<T> & operator=(const smart_pointer_v2<T> & other) {
			smart_pointer_base<T>::assign(other.getPtr());
			return *this;
		}
		template <class P> smart_pointer<T> & operator=(const smart_pointer_v2<P> & other) {
			smart_pointer_base<T>::assign((T*)other.getPtr());
			return *this;
		}
		smart_pointer<T> & operator=(const const_smart_pointer<T> & other) {
			smart_pointer_base<T>::assign(other.ptr);
			return *this;
		}
		template <class P> smart_pointer<T> & operator=(const const_smart_pointer<P> & other) {
			smart_pointer_base<T>::assign((T*)other.ptr);
			return *this;
		}

		///overload of a generic pointer conversion
		// UNRESOLVED That is the purpose? There are assignment operators and constructors
		//template <class P> operator smart_pointer<P>() const {
		//	return smart_pointer<P>((P*)ptr);
		//}

		///overloading the pointer access operator to provide encapsulated access to the carried pointer's members
		/// const object is returned
		const T* operator->() const		{	return smart_pointer_base<T>::ptr;		}
		const T &operator *() const		{	return *smart_pointer_base<T>::ptr;	}
		const T* getPtr() const			{	return smart_pointer_base<T>::ptr;		}

		///An assign function, to give a new pointer to this instance
		void assign(T* p) {
			smart_pointer_base<T>::assign_base(p);
		}
		///Assign from another instance
		template<class P>
		void assign(const smart_pointer<P> & other) {
			smart_pointer_base<T>::assign_base((T*)other.ptr);
		}


		// Surprisingly, these methods must be defined since smart_pointer_base::assign_with_dynamic_cast
		// is not visible if smart_pointer::assign_with_dynamic_cast is defined
		template<class P> void assign_with_dynamic_cast(P* p) {
			smart_pointer_base<T>::assign_with_dynamic_cast(p);
		}
		template<class P> void assign_with_static_cast(P* p) {
			smart_pointer_base<T>::assign_with_static_cast(p);
		}
		template<class P> void assign_with_safe_static_cast(P* p) {
			smart_pointer_base<T>::assign_with_safe_static_cast(p);
		}

		template<class P> void assign_with_dynamic_cast(const smart_pointer<P> & other) {
			smart_pointer_base<T>::assign_with_dynamic_cast(other.ptr);
		}
		template<class P> void assign_with_static_cast(const smart_pointer<P> & other) {
			smart_pointer_base<T>::assign_with_static_cast(other.ptr);
		}
		template<class P> void assign_with_safe_static_cast(const smart_pointer<P> & other) {
			smart_pointer_base<T>::assign_with_safe_static_cast(other.ptr);
		}


		template <class P> friend class smart_pointer;		// So that we had access to ptr even in case smart_pointer<P>::ptr
		friend class Scene;
	};

}

#endif
