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

#pragma once

#include "glmMap.h"
#include "glmSmartPointer.h"
#include "glmCoreAPI.h"

namespace glm
{
    class GLM_CORE_API CloneFactory
    {
    private:
        glm::GlmMap<const void*, void*> _clonesMap;

    public:
        CloneFactory();
        ~CloneFactory();

        /**
         * @brief Get the clone if it exists 
         */
        template <class Clonable>
        Clonable* getClone(const Clonable* source) const;

        /**
         * @brief Get the clone if it exists, or create it if not
         */
        template <class Clonable>
        Clonable* addClone(const Clonable* source);

        /**
         * @brief Clone the source and return it
         */
        template <class Clonable>
        Clonable* clone(const Clonable* source);

        /**
         * @brief Clone the source and return it encapsulated in a smart pointer
         */
        template <class Clonable>
        SmartPointer<Clonable> cloneSafe(const Clonable* source);
    };

    //-------------------------------------------------------------------------
    template <class Clonable>
    Clonable* CloneFactory::getClone(const Clonable* source) const
    {
        Clonable* result = NULL;
        glm::GlmMap<const void*, void*>::const_iterator itClone = _clonesMap.find(source);
        if (itClone != _clonesMap.end())
        {
            result = (Clonable*)itClone.getValue();
        }
        return result;
    }

    //-------------------------------------------------------------------------
    template <class Clonable>
    Clonable* CloneFactory::addClone(const Clonable* source)
    {
        Clonable* existingClone = getClone(source);
        Clonable* newClone = existingClone;
        if (newClone == NULL)
        {
            newClone = source->create();
            _clonesMap[source] = newClone;
        }
        return newClone;
    }

    //-------------------------------------------------------------------------
    template <class Clonable>
    Clonable* CloneFactory::clone(const Clonable* source)
    {
        if (source == NULL)
        {
            return NULL;
        }
        Clonable* clone = getClone(source);
        if (clone == NULL)
        {
            clone = addClone(source);
            source->cloneImpl(clone, this);
        }
        return clone;
    }

    //-------------------------------------------------------------------------
    template <class Clonable>
    SmartPointer<Clonable> CloneFactory::cloneSafe(const Clonable* source)
    {
        return SmartPointer<Clonable>(clone(source));
    }
} // namespace glm
