#ifndef JES_REF_H
#define JES_REF_H

#include <stdlib.h>

namespace jes
{
    template <typename T>
    class Ref
    {
        public:
            Ref<T>();
            Ref<T>(T * ptr);
            Ref<T>(const Ref<T> & orig);
            Ref<T> & operator=(const Ref<T> & orig);
            Ref<T> & operator=(T * ptr);
            ~Ref<T>();
            T & operator*() const { return *m_ptr; }
            T * operator->() const { return m_ptr; }
            bool is_null() const { return m_ptr == NULL; }
            bool operator==(const T * ptr) { return m_ptr == ptr; }
            bool operator!=(const T * ptr) { return m_ptr != ptr; }

        private:
            void clone_from(const Ref<T> & orig);
            void destroy();

            T * m_ptr;
            unsigned int * m_ref_count;
    };

    template <typename T>
    Ref<T>::Ref()
    {
        m_ptr = NULL;
        m_ref_count = NULL;
    }

    template <typename T>
    Ref<T>::Ref(T * ptr)
    {
        m_ptr = ptr;
        m_ref_count = new unsigned int;
        *m_ref_count = 1u;
    }

    template <typename T>
    Ref<T>::Ref(const Ref<T> & orig)
    {
        clone_from(orig);
    }

    template <typename T>
    Ref<T> & Ref<T>::operator=(const Ref<T> & orig)
    {
        destroy();
        clone_from(orig);
        return *this;
    }

    template <typename T>
    Ref<T> & Ref<T>::operator=(T * ptr)
    {
        destroy();
        m_ptr = ptr;
        m_ref_count = new unsigned int;
        *m_ref_count = 1u;
        return *this;
    }

    template <typename T>
    void Ref<T>::clone_from(const Ref<T> & orig)
    {
        this->m_ptr = orig.m_ptr;
        this->m_ref_count = orig.m_ref_count;
        if (m_ref_count != NULL)
            (*m_ref_count)++;
    }

    template <typename T>
    Ref<T>::~Ref()
    {
        destroy();
    }

    template <typename T>
    void Ref<T>::destroy()
    {
        if (m_ref_count != NULL)
        {
            if (*m_ref_count <= 1u)
            {
                delete m_ptr;
                delete m_ref_count;
            }
            else
            {
                (*m_ref_count)--;
            }
        }
    }
}

#endif
