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

#pragma once

#include <glmSDKAPI.h>
#include <glmMotionDefinitions.h>
#include <glmMutex.h>
#include <glmHashCode.h>
#include <glmFileDir.h>
#include <glmAnimationInterface.h>

#include <glmGolaemMotionDeclarations.h> // GolaemMotion declarations

namespace glm
{
	class AbstractSpecificAnimation;

	/**
	 * \brief Cache for generalized animation files
	 *
	 * \author dev@golaem.com
	 *
	 */
	class GLM_SDK_API AnimationCache
	{
	public:	//static methods
		GLM_CLASS_STAT_ID(glm::MemoryStatIds::GolaemShared);
		static bool registerExtension(const char* extension, AnimationInterface* (*loadCallback) (const char*, int, const char*));
		static bool unregisterExtension(const char* extension);
		static Array<GlmString> getKnownExtensions();
		static AnimationInterface* loadAnimation(const char* animationPath, int animationIndexinFile, const char* supportPropertyName);

	public:	//methods

		AnimationCache() ;
		~AnimationCache();

		size_t getAnimationsCount() const ;

		/**
		* Get the hashkey that would be produced by a given animationPath
		*/
		unsigned int getAnimationHashKey(const char* animationPath) const	{ return glm::getHashCode(glm::FileDir::correctPath(animationPath).c_str()); }

		bool isAnimationInCache( const char* animationPath ) const ;

		/** 
		 * \brief Request animation in cache (load it and insert it in cache if not already in cache)
		 * \param animationPath Path to the animation to get/load
		 * \return The animation if it exists in cache or was created, NULL if it doesn't exist in cache and could not be created
		 * \note When loading a new animation, any concurrent "getAnimation" from other thread call will loop until animation is loaded
		 */
		AnimationInterface* getAnimation( const char* animationPath) ;
 		/** 
		 * \brief Request average posture computed from animation in cache
		 * \param animation The animation from which to obtain average posture
		 * \return The average posture if it exists in cache or was created, NULL if it doesn't exist in cache and could not be created
		 * \note When computing a new average posture from an animation, any concurrent "getAnimation" from other thread call will loop until animation is loaded
		 */
		//GenericPosture* getAnimationAveragePosture( const GenericAnimation& animation ) ;

		/// used for serialization
		AnimationInterface* getAnimation( unsigned int hashKey ) ;
		
		/** 
		 * Explicitly add an animation already loaded explicitly
		 */
		void addAnimationExplicitly( const char* animationPath, AnimationInterface& animation ) ;
		//@}

		bool isAnimationInCache( const AnimationInterface* animation, unsigned int& foundHashCode ) const ;

		void clear() ;

	private:	//methods
		static GlmMap<GlmString, AnimationInterface* (*) (const char*, int, const char*)>& getRegisteredExtensions();

		const glm::GlmMap<unsigned int, glm::GlmString >& getCachedAnimationPath() const;
		void setCachedAnimationPath(const glm::GlmMap<unsigned int, glm::GlmString >& );

	private:	//data
		glm::GlmMap<unsigned int, std::pair< AnimationInterface*, glm::SpinLock > > _cachedAnimations;
		mutable glm::SpinLock _animationCacheMutex ;

		glm::GlmMap<unsigned int, glm::GlmString > _cachedAnimationsPath ; // name array
		glm::GlmMap<unsigned int, AnimationInterface* > _explicitAnimations ; // explicit animations for serialization
	};

	//------------------------------------------------------------
	inline size_t
		AnimationCache::getAnimationsCount() const
	{
		ScopedLock<glm::SpinLock> lock( _animationCacheMutex ) ;

		return _cachedAnimations.size() ;
	}
}

