#include <iostream>
#include <type_traits>
using namespace std;
 
	template<typename T> class IntrusivePtr;
 
	template<typename TDerived>
	class ReferenceCounting {
    public:
        typedef TDerived Derived ;
 
        ReferenceCounting() : m_ref(0) { }
 
        // Do not change reference count if an assignment has been done
        ReferenceCounting& operator= (ReferenceCounting const&){ return *this; }
 
        unsigned long int getRefCount() const{return m_ref;}
 
    protected:
    	~ReferenceCounting() {std::cout <<"RC:DTOR" <<std::endl; }   
 
    private:
 
    	friend  class IntrusivePtr<TDerived>;
    	friend  class IntrusivePtr<const TDerived>;
 
    	unsigned long int addRef() const{
    		++m_ref;
    		std::cout << "RC::addRef: " << m_ref <<  std::endl;
    		return m_ref;
    	}
 
    	// NoDelete is for IntrusivePtr<T>().release()!
    	template<bool Delete = true>
    	void release() const{
    		--m_ref;
    		std::cout << "RC::release: " <<  m_ref << std::endl;
    		if(!m_ref && Delete){
    			std::cout << "RC::delete" << std::endl;
    			delete static_cast<Derived const *>(this);
    		} 
    	}
 
        mutable unsigned long int m_ref; // Mutable to be changeable also for const objects!
    };
 
 
    template<typename T>
    class IntrusivePtr {
    public:
 
    	using NonConstType = typename std::remove_const<T>::type;
 
        IntrusivePtr() : m_p(nullptr) { }
 
        // Explicit constructor from T* , because we want to avoid that this constructor can be used to convert implicitly to IntrusivePtr
        // somewhere in the code which then deletes the resource unexpectetly!
        // In this constructor/destructors we need a static_cast to really be sure if the type T  inherits somehow from ReferenceCounting<T>
        explicit IntrusivePtr(T* p) : m_p(p) {
            if(p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->addRef();
        }
 
        IntrusivePtr(const IntrusivePtr & rhs) : m_p(rhs.m_p) {
            if(m_p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->addRef();
        }
 
        // Move support (temporaries)
        // Copy construct from temporary
        IntrusivePtr(IntrusivePtr && rhs) : m_p( rhs.m_p ){
	        rhs.m_p = 0; // temporary will not invoke reference count because pointer is zero!
	    }
 
        ~IntrusivePtr() {
            if(m_p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->release();
        }
 
 
        // We want to assign the intrusive ptr to this class
        // m_p points to A, rhs->m_p  points to B
        // This means, decrease ref count of current object A, an set m_p=rhs->m_p 
        // and increase ref count of rhs resource. This can by:
        // Copy and swap idiom, call by value to copy the IntrusivePtr (ref count of B increments)
        // swap this resource pointer into the local temporary rhs (only pointer swap) 
        // deleting the local rhs at end of function decrements ref count of initial resource A 
        IntrusivePtr& operator=(IntrusivePtr rhs) {
            rhs.swap(*this); // swaps the resource pointers
            return *this; // delete rhs-> which decrements correctly our initial resource A!
        }
 
        // Move Assignment (from temporary)
		// Make sure rhs.m_p is zero and as a consequence the destruction of rhs does not invoke release!
	    IntrusivePtr & operator=(IntrusivePtr && rhs){
	        IntrusivePtr( std::move( rhs ) ).swap(*this);
	        return *this;
	    }
 
		// Reset the IntrusivePtr to some other resource B, 
		// meaning decrementing our resource A and setting the new pointer to B
		// and incrementing B
		// Can also take a nullptr!, making it not default argument because avoiding syntax mistakes with release()
		// which does a complete different thing (takes off the IntrusivePtr)
        void reset(T* p) {
            // Make temporary intrusive pointer for *p (incrementing ref count B)
            // swapping pointers with our resource A, and deleting temporary, decrement A
            IntrusivePtr(p).swap(*this);
        }
 
        // Release a IntrusivePtr from managing the shared resource
        // Decrements ref count of this resource A but without deleting it!
        T* release() {
        	static_cast<const ReferenceCounting<NonConstType> *>(m_p)->template release<false>();
            T* p = m_p;
            m_p = nullptr;
            return p;
        }
 
		// Get the underlying pointer	
        T* get() const { return m_p; }
 
        // Implicit cast to T*
        operator T*() const { return m_p; } 
        // Implicit cast to T&
        operator T&() const { return *m_p; }
 
        T* operator->() const { return m_p; }
 
        void swap(IntrusivePtr& rhs) {
            std::swap(m_p, rhs.m_p);
        }
    private:
        T* m_p;
    };
 
 
    // Helper to select the correct Derived type, the one which gets deleted in the ReferencCounting class
    template<typename PossibleDerived,typename T>
    struct Select{ typedef PossibleDerived type;};
 
    template<typename T>
    struct Select<void,T>{ typedef T type; };
 
 
    // The base class!
    template<typename PossibleDerived = void>
    class A : public ReferenceCounting< typename Select<PossibleDerived, A<PossibleDerived> >::type >{
    	public:
 
    	A(){}
    	~A(){
    		std::cout << "A::DTOR: " <<this <<  std::endl;	
    	}
 
    	int foo(){return i[10000];}
 
		int i[10001];
    };
 
 
    // Use this class (because no brackets <> to write, more handy to write code)
    class ANoTemplate final : public A<ANoTemplate>{
    	public:
 
    	ANoTemplate(){}
    	~ANoTemplate(){
    		std::cout << "B::DTOR: " <<this <<  std::endl;	
    	}
    };
 
    // if we want to derive from this class we need to be very carefull, as
    template<typename PossibleDerived = void>
    class B : public A< typename Select<PossibleDerived, B<PossibleDerived> >::type >{
    	public:
    	using Base = A< typename Select<PossibleDerived, B<PossibleDerived> >::type >;
    	~B(){
    		std::cout << "B::DTOR: " <<this <<  std::endl;	
    	}
 
    };
 
    class AddendumToC{
    	public:
    	~AddendumToC(){
    		std::cout << "AddendumToC::DTOR " <<this <<  std::endl;		
    	}
    };
 
    // Do not derive from this class, otherwise the refernce counting base class deletes the wrong class!
    class C : public B<C>, public AddendumToC{
    	public:
    	~C(){
    		std::cout << "C::DTOR: " <<this <<  std::endl;	
    	}
    };
 
    // DOOO NOT DOO INHERIT from C if you want reference counting with an intrusive pointer!
    // In fact: If we have a IntrusivePtr onto a D instance, it will not compile as D cannot be cast into ReferenceCounting<C>!!)
    class D : public C{};
 
    int main(){
 
 
 
		{
		A<> *a = new A<>();
    	IntrusivePtr< A<> > p (a);
    	std::cout << "Created IntrusivePtr< A<> > " <<p<< std::endl;
		}
 
		{
		ANoTemplate *a = new ANoTemplate();
    	IntrusivePtr< ANoTemplate > p (a);
    	std::cout << "Created Local IntrusivePtr< ANoTemplate >  " <<p<< std::endl;
		}
 
		{
    	IntrusivePtr< B<> > p (new B<>());
    	std::cout << "Created Local IntrusivePtr< B<> > " <<p<< std::endl;
		}
 
		{
		C *a = new C();
    	IntrusivePtr< C > p (a);
    	std::cout << "Created Local IntrusivePtr< C > " <<p<< std::endl;
		}
 
		{
		D *a = new D();
    	//IntrusivePtr< D > p (a);
    	//std::cout << "Created IntrusivePtr< D > " <<p<< std::endl;
    	//std::cout << "TAKE CARE: ONLY C gets deleted! " <<p<< std::endl;
		}
 
		{
    	IntrusivePtr< B<>::Base >(new B<>{});
    	//std::cout << "Created Local IntrusivePtr< B::Base > " <<p<< " from wrong pointer to C class " << std::endl;
		}
 
 
 
		{
		using A=ANoTemplate;
		A *a = new A();
    	IntrusivePtr<A> p (a);
    	std::cout << "Create Local " <<p<< std::endl;
    	{
    		std::cout << "Create IntrPtr" << std::endl;			
		    	IntrusivePtr<A> p2(p);
    			// Copy object
    			A c(p2);
    			std::cout << "Ref count of copied obj:" << c.getRefCount() << std::endl;
 
    			A b = *a;
    			std::cout << "Ref count of copied obj:" << b.getRefCount() << std::endl;
	    	{
		    	std::cout << "Create IntrPtr2 from;" << p.get()<< std::endl;			
		    	IntrusivePtr<A const> p3(p);
		    	std::cout << "p3 points to " << p3.get() << " with ref count: " << p3->getRefCount()<< std::endl;
		    	//p1->i[0]=10;
		    	//p1.release(); p2.release();  // does not delete object
		    	const A* r =p3.release();
		    	std::cout << "Ref count of copied obj:" << r->getRefCount() << std::endl;
	    	}
    	}
		}
 
    }