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

#pragma once

#include <glmSDKAPI.h>
#include "glmMotionDefinitions.h"
#include <glmGolaemMotionDeclarations.h> // GolaemMotion declarations

namespace glm
{
	class GenericSpecificConverter;
	class AnimationInterface;
	struct ChannelReplayParameters;
	struct ChannelInfo;
	struct AnimationReplayMappingV0;
	struct AnimationReplayMappingV1;
	struct AnimationReplayMappingV2;

	struct GLM_SDK_API EffectorMapping
	{
		GLM_RTTI( "c138a8c5-da3d-4d78-b895-3fbe2bfe0654", glm::EffectorMapping, addRTTIAttributes )
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

		EffectorMapping() : _mappingIdx(getClassMemoryStatId())
		{ }
		EffectorMapping(const EffectorMapping& source) : _mappingIdx(getClassMemoryStatId())
		{
			_mappingIdx = source._mappingIdx;			
		}
		EffectorMapping& operator=(const EffectorMapping& source)
		{
			if ( &source != this )
			{
				_mappingIdx = source._mappingIdx;
			}
			return (*this);
		}
		~EffectorMapping()
		{
			uninitialize();
		}

		void initializeEmpty(const size_t mappingCount)
		{
			GLM_DEBUG_ASSERT( mappingCount > 0 );
			GLM_DEBUG_ASSERT( _mappingIdx.empty() );
			
			_mappingIdx.reserve( mappingCount );
			_mappingIdx.resize( mappingCount );
			for(size_t i = 0; i < mappingCount; ++i)
			{
				_mappingIdx[i] = _mappingInvalidIdx;
			}
		}
		
		inline int getMappingCount() const { return static_cast<int>(_mappingIdx.size()); } 

		static const int _mappingInvalidIdx = -1;

		glm::PODArray<int> _mappingIdx;

	private:
		
		void uninitialize()
		{
			_mappingIdx.clear();
			_mappingIdx.trim();
		}

		static void addRTTIAttributes();
	};

	typedef EffectorMapping BlendShapeMapping ;

	/**
	* \brief Configuration of replayed bones on a different skeleton
	*
	* \author dev@golaem.com
	*
	*/
	class GLM_SDK_API AnimationReplayMapping
	{
		GLM_RTTI( "478c4436-8e24-47bf-905d-4ef936572aa2", glm::AnimationReplayMapping, addRTTIAttributes )

	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation)

		//************************************************************
		//! @name Constructors and destructor
		//************************************************************
		//@{
		AnimationReplayMapping();
		AnimationReplayMapping(const AnimationReplayMapping& source);
		AnimationReplayMapping& operator=(const AnimationReplayMapping& source);
		~AnimationReplayMapping();

		void init();
		void finish();

		void allocateBlindDataMapping(const size_t blindDataCount);
		void deallocateBlindDataMapping();
		void copyBlindDataMapping(const glm::PODArray<int>& sourceMapping);

		void allocateReplayChannels(const size_t replayChannelCount);
		void deallocateReplayChannels();
		void copyReplayChannels(const AnimationReplayMapping& source);

		/**
		* \brief V0 backwards compatibility
		*/
		void createFromV0(AnimationReplayMappingV0& animationReplayMappingV0);
		/**
		* \brief V1 backwards compatibility
		*/
		void createFromV1(AnimationReplayMappingV1& animationReplayMappingV1);
		/**
		* \brief V2 backwards compatibility
		*/
		void createFromV2(AnimationReplayMappingV2& animationReplayMappingV2);
		//@}

		//************************************************************
		//! @name File management
		//************************************************************
		//@{
		LoadMotionMappingErrorCode::Value load(const char* filePath, int* retFileVersionNumber = NULL);
		SaveMotionMappingErrorCode::Value save(const char* filePath) const;
		LoadMotionMappingErrorCode::Value loadXML(const char* filePath, int* retFileVersionNumber = NULL);
		SaveMotionMappingErrorCode::Value saveXML(const char* filePath) const ;
		//@}

		//************************************************************
		//! @name Configuration BEFORE init
		//************************************************************
		//@{
		void setReplaySkeleton(const GenericSkeleton* skeleton);
		//@}

		//************************************************************
		//! @name Configuration AFTER init
		//************************************************************
		//@{
		void setReplayChannel(int channelIdxInReplaySkeleton, int replayChannelInAnimationSkeleton, const ChannelReplayParameters& replayParameters);


		/**
		* Get and set the replay effector index.
		* \warning the index to use is not the generic bone index but the index in order of apparition in the channel bone list
		*/
		void setReplayEffectorIndex(int channelIdxInReplaySkeleton, int effectorIdxInReplaySkeleton, int animationEffectorIdxInReplaySkeleton);
		const EffectorMapping* getReplayEffectorIndex() const { if ( !_effectorMapping.empty() ) return &_effectorMapping[0]; return NULL; }

		inline int getChannelCount() { return static_cast<int>(_channelsInfo.size()); }
		inline int getReplayChannelsCount() const { return static_cast<int>(_animationReplayChannels.size()); }
		const int* getAnimationReplayChannels() const { if ( !_animationReplayChannels.empty() ) return &_animationReplayChannels[0]; return NULL;}

		const ChannelReplayParameters* getAnimationReplayParameters() const { if ( !_channelReplayParameters.empty() ) return &_channelReplayParameters[0]; return NULL; }

		void computeAutomaticReplayMapping(const GenericSpecificConverter* replaySkeletonConverter, const AnimationInterface& animation, const int coronalFactor = 1, const int transverseFactor = 1, const int sagittalFactor = 1, const int skeletonPartTypeFactor = 1);
		void computeAutomaticReplayMapping(const GenericSkeleton& replaySkeleton, const GenericSkeleton& animationSkeleton, const int coronalFactor = 1, const int transverseFactor = 1, const int sagittalFactor = 1, const int skeletonPartTypeFactor = 1);
		void computeIdentityReplayMapping(const GenericSkeleton& replaySkeleton);
		//@}

		inline int getBlindDataMappingCount() const { return static_cast<int>(_blindDataMapping.size()); }

	private:
		/**
		* \brief Register the inspectable attributes to the class RTTI
		*/
		static void addRTTIAttributes();

	public:
		bool _replayRoot;
		bool _replaySpine;
		glm::Array<ChannelInfo> _channelsInfo;

		glm::PODArray<int> _blindDataMapping;

		static const int _animationChannelInvalidIdx = -1;

		static int _currentVersionNumber;

	private:
		//************************************************************
		//! @name Configuration data
		//************************************************************
		//@{

		glm::PODArray<int> _animationReplayChannels;
		glm::Array<EffectorMapping> _effectorMapping;
		glm::Array<ChannelReplayParameters> _channelReplayParameters;
		//@}

		void sortEffectors( const GenericSkeleton& skeleton, const ChannelInfo* channelInfo, int effectorMappingCount, glm::PODArray<int>& results );
	} ;
}
