// This code is in the public domain -- Ignacio Castaņo <castano@gmail.com>

#ifndef NV_CORE_REFCOUNTED_H
#define NV_CORE_REFCOUNTED_H

#include "nvcore.h"
#include "Debug.h"

#define NV_DECLARE_PTR(Class) \
    template <class T> class SmartPtr; \
    typedef SmartPtr<class Class> Class ## Ptr; \
    typedef SmartPtr<const class Class> Class ## ConstPtr


namespace nv
{
    /// Weak proxy.
    class WeakProxy
    {
        NV_FORBID_COPY(WeakProxy);
    public:
	    /// Ctor.
	    WeakProxy(void * ptr) : m_count(0), m_ptr(ptr) { }

        /// Dtor.
        ~WeakProxy()
        {
            nvCheck( m_count == 0 );
        }

        /// Increase reference count.
        uint addRef() const
        {
            m_count++;
            return m_count;
        }

        /// Decrease reference count and remove when 0.
        uint release() const
        {
            nvCheck( m_count > 0 );

            m_count--;
            if( m_count == 0 ) {
                delete this;
                return 0;
            }
            return m_count;
        }

	    /// WeakPtr's call this to determine if their pointer is valid or not.
	    bool isAlive() const {
		    return m_ptr != NULL;
	    }

	    /// Only the actual object should call this.
	    void notifyObjectDied() {
		    m_ptr = NULL;
	    }

        /// Return proxy pointer.
        void * ptr() const {
            return m_ptr;
        }

    private:
        mutable int m_count;
	    void * m_ptr;
    };


    /// Reference counted base class to be used with SmartPtr and WeakPtr.
    class RefCounted
    {
        NV_FORBID_COPY(RefCounted);
    public:

        /// Ctor.
        RefCounted() : m_count(0), m_weak_proxy(NULL)
        {
        }

        /// Virtual dtor.
        virtual ~RefCounted()
        {
            nvCheck( m_count == 0 );
            releaseWeakProxy();
        }


        /// Increase reference count.
        uint addRef() const
        {
            m_count++;
            return m_count;
        }


        /// Decrease reference count and remove when 0.
        uint release() const
        {
            nvCheck( m_count > 0 );

            m_count--;
            if( m_count == 0 ) {
                delete this;
                return 0;
            }
            return m_count;
        }

        /// Get weak proxy.
        WeakProxy * getWeakProxy() const
        {
            if (m_weak_proxy == NULL) {
                m_weak_proxy = new WeakProxy((void *)this);
                m_weak_proxy->addRef();
            }
            return m_weak_proxy;
        }

        /// Release the weak proxy.	
        void releaseWeakProxy() const
        {
            if (m_weak_proxy != NULL) {
                m_weak_proxy->notifyObjectDied();
                m_weak_proxy->release();
                m_weak_proxy = NULL;
            }
        }

        /// Get reference count.
        int refCount() const
        {
            return m_count;
        }


    private:

        mutable int m_count;
        mutable WeakProxy * m_weak_proxy;

    };

} // nv namespace


#endif // NV_CORE_REFCOUNTED_H
