/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
****************************************************************************/

#pragma once

#include "glmRTTI.h"
#include "glmAssert.h"
#include "glmMemory.h"
#include "glmAtomic.h"

namespace glm
{
    // Reference counter class
    class GLM_CORE_API ReferenceCounter
    {
        //GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation)

    public:
        ReferenceCounter();
        virtual ~ReferenceCounter(); //added destructor so that overloaded delete operator isn't being called when passed a NULL pointer ( Related to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#348 )

        unsigned int getReferenceCount();
        void incrementReferenceCount();
        void decrementReferenceCount();

    private:
        atomic32_t _referenceCount;
    };

    // SmartPointer
    template <class TPointeeType>
    class SmartPointer
    {
        friend class BinaryStream;
        friend class XMLStream;

    public:
        typedef TPointeeType pointee_type;

        SmartPointer();
        SmartPointer(TPointeeType* pointer);
        SmartPointer(const SmartPointer<TPointeeType>& pointer);
        template <class TFrom>
        SmartPointer(const SmartPointer<TFrom>& from);
        virtual ~SmartPointer();

        const SmartPointer<TPointeeType>& operator=(const SmartPointer<TPointeeType>& pointer);
        template <class TFrom>
        const SmartPointer<TPointeeType>& operator=(const SmartPointer<TFrom>& pointer);

        TPointeeType* operator->() const; //allows a smart pointer object to act as though it were the actual encapsulated pointee through indirection.
        TPointeeType& operator*() const;  //allows a smart pointer object to act as though it were the actual encapsulated pointee when dereferenced.
        bool operator!() const;           //enables "if (!sp) ...
        operator void*();                 //added conversion to void*, to prevent delete from compiling and becomes ambiguous

        template <class TTo, class TFrom>
        friend SmartPointer<TTo> staticCast(const SmartPointer<TFrom>& from); //the staticCast function permits to explicitly cast a smart pointer to a pointee to a smart pointer to a related pointee
        template <class TTo, class TFrom>
        friend const SmartPointer<TTo> dynamicCast(const SmartPointer<TFrom>& from); //the dynamicCast function permits to explicitly cast a smart pointer to a pointee to a smart pointer to a related pointee

        // if you provide an automatic conversion to the pointee type, there still is the risk of ambiguities.
        // the templated operators are "greedy" in the sense that they match comparisons with any pointer type whatsoever,
        // thus consuming the ambiguity.
        template <class UPointeeType, class U>
        friend bool operator==(const SmartPointer<UPointeeType>& lhs, const U* rhs);
        template <class U, class UPointeeType>
        friend bool operator==(const U* lhs, const SmartPointer<UPointeeType>& rhs);
        template <class U>
        bool operator==(const SmartPointer<U>& rhs) const;
        template <class U>
        friend bool operator!=(const SmartPointer<TPointeeType>& lhs, const U* rhs);
        template <class U>
        friend bool operator!=(const U* lhs, const SmartPointer<TPointeeType>& rhs);
        template <class U>
        bool operator!=(const SmartPointer<U>& rhs) const;
        template <class U>
        friend bool operator<(const SmartPointer<TPointeeType>& lhs, const U* rhs);
        template <class U>
        friend bool operator<(const U* lhs, const SmartPointer<TPointeeType>& rhs);
        template <class U>
        bool operator<(const SmartPointer<U>& rhs) const;

        // why should we keep the nontemplated operators the ones that take the pointee type?
        // They never get a chance to match, because the template matches any pointer type,
        // including the pointee type itself.
        // The rule that "never" actually means "almost never" applies here, too.
        // In the test if (sp == 0), the compiler tries the following matches.
        // - The templated operators. They don't match because zero is not a pointer type.
        // A literal zero can be implicitly converted to a pointer type, but template matching
        // does not include conversions.
        // - The nontemplated operators. After eliminating the templated operators, the compiler
        // tries the nontemplated ones. One of these operators kicks in through an implicit
        // conversion from the literal zero to the pointee type. Had the nontemplated operators
        // not existed, the test would have been an error.
        // In conclusion, we need both the nontemplated and the templated comparison operators.
        friend bool operator==(const SmartPointer<TPointeeType>& lhs, const TPointeeType* rhs)
        {
            return lhs._pointee == rhs;
        }
        friend bool operator==(const TPointeeType* lhs, const SmartPointer<TPointeeType>& rhs)
        {
            return lhs == rhs._pointee;
        }
        friend bool operator!=(const SmartPointer<TPointeeType>& lhs, const TPointeeType* rhs)
        {
            return lhs._pointee != rhs;
        }
        friend bool operator!=(const TPointeeType* lhs, const SmartPointer<TPointeeType>& rhs)
        {
            return lhs != rhs._pointee;
        }
        friend bool operator<(const SmartPointer<TPointeeType>& lhs, const TPointeeType* rhs)
        {
            return lhs._pointee < rhs;
        }
        friend bool operator<(const TPointeeType* lhs, const SmartPointer<TPointeeType>& rhs)
        {
            return lhs < rhs._pointee;
        }

        unsigned int getReferenceCount() const; //return the number of references to the pointer
        TPointeeType* getImpl() const;          //return the pointer

    private:
        void setReferenceCounter(ReferenceCounter*& referenceCounter, TPointeeType* pointee);
        void setReferenceCounter(ReferenceCounter*& referenceCounter, const TPointeeType* pointee);
        void increaseCounter();
        void decreaseCounter();

        template <class TTo, class TFrom>
        friend SmartPointer<TTo> assignmentOperator(const SmartPointer<TFrom>& from); //permits to implicitly cast a smart pointer to a pointee to a smart pointer to a related pointee

        ReferenceCounter* _referenceCounter; //tracks the number of smart pointers that point to the same object
        TPointeeType* _pointee;
    };

} // namespace glm

#include "glmAttribute.h"

namespace glm
{

    //-----------------------------------------------------------------------------
    template <class TTo, class TFrom>
    inline SmartPointer<TTo> staticCast(const SmartPointer<TFrom>& from)
    {
        SmartPointer<TTo> pointer;
        if (pointer._pointee != static_cast<TTo*>(from._pointee))
        {
            // if the SmartPointer has not been initialised by a copy constructor
            // we must decrease the old reference counter instance
            pointer.decreaseCounter();

            pointer._referenceCounter = NULL;
            if (from._pointee != NULL)
            {
                // initialise the reference counter to the assignment value
                pointer._referenceCounter = from._referenceCounter;
            }

            // copy the pointers to the copies of T
            pointer._pointee = static_cast<TTo*>(from._pointee);
            pointer.increaseCounter();
        }
        return pointer;
    }

    //-----------------------------------------------------------------------------
    template <class TTo, class TFrom>
    inline const SmartPointer<TTo> dynamicCast(const SmartPointer<TFrom>& from)
    {
        SmartPointer<TTo> pointer;
        if (pointer._pointee != dynamic_cast<TTo*>(from._pointee))
        {
            // if the SmartPointer has not been initialised by a copy constructor
            // we must decrease the old reference counter instance
            pointer.decreaseCounter();

            pointer._referenceCounter = NULL;
            if (from._pointee != NULL)
            {
                // initialise the reference counter to the assignment value
                pointer._referenceCounter = from._referenceCounter;
            }

            // copy the pointers to the copies of T
            pointer._pointee = dynamic_cast<TTo*>(from._pointee);
            pointer.increaseCounter();
        }
        return pointer;
    }

    //-----------------------------------------------------------------------------
    inline unsigned int ReferenceCounter::getReferenceCount()
    {
        return static_cast<unsigned int>(atomicLoadAcquire(_referenceCount));
    }

    //-----------------------------------------------------------------------------
    inline void ReferenceCounter::incrementReferenceCount()
    {
        atomicFetchAddSequentiallyConsistent(_referenceCount, 1);
    }

    //-----------------------------------------------------------------------------
    inline void ReferenceCounter::decrementReferenceCount()
    {
        atomicFetchAddSequentiallyConsistent(_referenceCount, -1);
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    SmartPointer<TPointeeType>::SmartPointer()
        : _referenceCounter(NULL)
        , _pointee(NULL)
    {
        increaseCounter();
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    SmartPointer<TPointeeType>::SmartPointer(TPointeeType* pointer)
        : _referenceCounter(NULL)
        , _pointee(pointer)
    {
        increaseCounter();
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    SmartPointer<TPointeeType>::SmartPointer(const SmartPointer<TPointeeType>& pointer)
        : // initialise the reference counter to NULL, it will be set in the operator=
        _referenceCounter(NULL)
        , _pointee(NULL)
    {
        *this = pointer;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    template <class TFrom>
    SmartPointer<TPointeeType>::SmartPointer(const SmartPointer<TFrom>& from)
        : // initialise the reference counter to NULL, it will be set in the operator=
        _referenceCounter(NULL)
        , _pointee(NULL)
    {
        *this = from;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    SmartPointer<TPointeeType>::~SmartPointer()
    {
        decreaseCounter();
        _pointee = NULL;
        _referenceCounter = NULL;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    inline const SmartPointer<TPointeeType>& SmartPointer<TPointeeType>::operator=(const SmartPointer<TPointeeType>& other)
    {
        if (this != &other)
        {
            // if the SmartPointer has not been initialised by a copy constructor
            // we must decrease the old reference counter instance
            decreaseCounter();

            _referenceCounter = NULL;
            if (other._pointee != NULL)
            {
                // initialise the reference counter to the assignment value
                _referenceCounter = other._referenceCounter;
            }

            // copy the pointers to the copies of T
            _pointee = other._pointee;
            increaseCounter();
        }
        return (*this);
    }

    //-----------------------------------------------------------------------------
    template <class TTo, class TFrom>
    SmartPointer<TTo> assignmentOperator(const SmartPointer<TFrom>& from)
    {
        SmartPointer<TTo> pointer;
        if (pointer._pointee != from._pointee)
        {
            // if the SmartPointer has not been initialised by a copy constructor
            // we must decrease the old reference counter instance
            pointer.decreaseCounter();

            pointer._referenceCounter = NULL;
            if (from._pointee != NULL)
            {
                // initialise the reference counter to the assignment value
                pointer._referenceCounter = from._referenceCounter;
            }

            // copy the pointers to the copies of T
            pointer._pointee = from._pointee;
            pointer.increaseCounter();
        }
        return pointer;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    template <class TFrom>
    inline const SmartPointer<TPointeeType>& SmartPointer<TPointeeType>::operator=(const SmartPointer<TFrom>& pointer)
    {
        *this = assignmentOperator<TPointeeType, TFrom>(pointer);
        return (*this);
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    TPointeeType* SmartPointer<TPointeeType>::operator->() const
    {
        return _pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    void SmartPointer<TPointeeType>::setReferenceCounter(ReferenceCounter*& referenceCounter, TPointeeType* pointee)
    {
        referenceCounter = NULL;
        if (pointee != NULL)
        {
            referenceCounter = dynamic_cast<ReferenceCounter*>(pointee);
            GLM_DEBUG_ASSERT_MSG(referenceCounter != NULL, "A class pointed by an ExternalReferenceCountingPolicy SmartPointer must be derived from glm::ReferenceCounter");
        }
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    void SmartPointer<TPointeeType>::setReferenceCounter(ReferenceCounter*& referenceCounter, const TPointeeType* pointee)
    {
        referenceCounter = NULL;
        if (pointee != NULL)
        {
            referenceCounter = const_cast<ReferenceCounter*>(dynamic_cast<const ReferenceCounter*>(pointee));
            GLM_DEBUG_ASSERT(referenceCounter != NULL);
        }
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    void SmartPointer<TPointeeType>::increaseCounter()
    {
        if (_pointee != NULL)
        {
            if (_referenceCounter == NULL)
            {
                setReferenceCounter(_referenceCounter, _pointee);
            }
            _referenceCounter->incrementReferenceCount();
        }
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    inline void SmartPointer<TPointeeType>::decreaseCounter()
    {
        if (_referenceCounter != NULL) // if pointee is null, ref counter is null
        {
            _referenceCounter->decrementReferenceCount();
            // if no more SmartPointer points to the object, we delete it
            if (_referenceCounter->getReferenceCount() == 0)
            {
                _referenceCounter = NULL;
                GLM_SAFE_DELETE(_pointee);
                _pointee = NULL;
            }
        }
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    inline TPointeeType& SmartPointer<TPointeeType>::operator*() const
    {
        return *_pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    inline bool SmartPointer<TPointeeType>::operator!() const
    {
        return !_pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType, class U>
    inline bool operator==(const SmartPointer<TPointeeType>& lhs, const U* rhs)
    {
        return lhs._pointee == rhs;
    }

    //-----------------------------------------------------------------------------
    template <class U, class TPointeeType>
    inline bool operator==(const U* lhs, const SmartPointer<TPointeeType>& rhs)
    {
        return lhs == rhs._pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    template <class U>
    inline bool SmartPointer<TPointeeType>::operator==(const SmartPointer<U>& rhs) const
    {
        return _pointee == rhs._pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType, class U>
    inline bool operator!=(const SmartPointer<TPointeeType>& lhs, const U* rhs)
    {
        return lhs._pointee != rhs;
    }

    //-----------------------------------------------------------------------------
    template <class U, class TPointeeType>
    inline bool operator!=(const U* lhs, const SmartPointer<TPointeeType>& rhs)
    {
        return lhs != rhs._pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    template <class U>
    bool inline SmartPointer<TPointeeType>::operator!=(const SmartPointer<U>& rhs) const
    {
        return _pointee != rhs._pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType, class U>
    inline bool operator<(const SmartPointer<TPointeeType>& lhs, const U* rhs)
    {
        return lhs._pointee < rhs;
    }

    //-----------------------------------------------------------------------------
    template <class U, class TPointeeType>
    inline bool operator<(const U* lhs, const SmartPointer<TPointeeType>& rhs)
    {
        return lhs < rhs._pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    template <class U>
    bool inline SmartPointer<TPointeeType>::operator<(const SmartPointer<U>& rhs) const
    {
        return _pointee < rhs._pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    inline SmartPointer<TPointeeType>::operator void*()
    {
        return _pointee;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    inline unsigned int SmartPointer<TPointeeType>::getReferenceCount() const
    {
        if (_referenceCounter != NULL)
        {
            return _referenceCounter->getReferenceCount();
        }
        return 0;
    }

    //-----------------------------------------------------------------------------
    template <class TPointeeType>
    inline TPointeeType* SmartPointer<TPointeeType>::getImpl() const

    {
        return _pointee;
    }
} // namespace glm
