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

#pragma once

#include "glmCoreAPI.h"
#include "glmString.h"
#include "glmMap.h"
#include "glmArray.h"
#include "glmStringOperators.h"
#include "glmMutex.h"
#include "glmScopedLock.h"
#include <typeinfo>

namespace glm
{
    namespace internal
    {
		inline void stripTypeName(const glm::GlmString& name, GlmString& result)
        {
            // should get something like:
            // msvc: internal::GetTypeNameHelper<class glm::GolaemCharacter>::getTypeName
            // gcc: static const glm::GlmString& glm::internal::GetTypeNameHelper<T>::getTypeName() [with T = glm::GolaemCharacter]

			result = name;
            glm::Array<glm::GlmString> splitExpr;
#ifdef _MSC_VER
            glm::split(result, "GetTypeNameHelper<", splitExpr);
#else
            glm::split(result, "[with T =", splitExpr);
#endif
            if (splitExpr.size() > 1)
            {
                result = splitExpr[1];
                splitExpr.clear();
#ifdef _MSC_VER
                glm::split(result, ">::getTypeName", splitExpr);
#else
                glm::split(result, "]", splitExpr);
#endif
                if (splitExpr.size() > 0)
                {
                    result = splitExpr[0];
#ifdef _MSC_VER
                    result = glm::replaceString(result, "class", "");
                    result = glm::replaceString(result, "struct", "");
#endif
                }
            }
            result.trim();
        }
        template <typename T>
        struct GetTypeNameHelper
        {
            static void getTypeName(glm::GlmString& functionName)
            {
#if GLM_IS_PLATFORM_WINDOWS
				stripTypeName(__FUNCTION__, functionName);
#else
				stripTypeName(__PRETTY_FUNCTION__, functionName);
#endif
            }
        };

    } // namespace internal

    template <typename T>
	void typeName(glm::GlmString& functionName)
    {
		internal::GetTypeNameHelper<T>::getTypeName(functionName);
    }

    struct GLM_CORE_API TypeInfo
    {
        GlmString _typeName;

        TypeInfo();
        TypeInfo(const GlmString& typeName);
    };

    class GLM_CORE_API TypesManager
    {
    private:
        GlmMap<size_t, TypeInfo> _registeredTypes;

        mutable glm::SpinLock _typesLock;

		TypeInfo _defaultTypeInfo;

    public:
        TypesManager();
        ~TypesManager();

        template <class T>
        void registerType();

        template <class T>
        void unregisterType();

        const TypeInfo& getType(size_t typeHash) const;

        template <class T>
        const TypeInfo& getType() const;
    };

    //-------------------------------------------------------------------------
    template <class T>
    void TypesManager::registerType()
    {
        ScopedLock<glm::SpinLock> typesLock(_typesLock);
        size_t typeHash = typeid(T).hash_code();
        GlmMap<size_t, TypeInfo>::const_iterator itType = _registeredTypes.find(typeHash);
        if (itType != _registeredTypes.end())
        {
            // already registered
            return;
        }
        TypeInfo& typeInfo = _registeredTypes[typeHash];
        glm::typeName<T>(typeInfo._typeName);
    }

    //-------------------------------------------------------------------------
    template <class T>
    void TypesManager::unregisterType()
    {
        ScopedLock<glm::SpinLock> typesLock(_typesLock);
        size_t typeHash = typeid(T).hash_code();
        _registeredTypes.erase(typeHash);
    }

    //-------------------------------------------------------------------------
    template <class T>
    const TypeInfo& TypesManager::getType() const
    {
        size_t typeHash = typeid(T).hash_code();
        return getType(typeHash);
    }

    //-----------------------------------------------------------------------------
    GLM_CORE_API TypesManager& getTypesManagerSingleton();

} // namespace glm
