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

#pragma once

//golaem motion includes
#include "glmMotionDefinitions.h"
#include "glmSDKAPI.h"
#include <glmTypes.h>
#include <glmVector3.h>

#include <glmGolaemMotionDeclarations.h> // GolaemMotion declarations

namespace glm
{
	/**
	 *
	 * \brief Describe the following activation/desactivation times of the constraint
	 *
	 * \author dev@golaem.com
	 *
	 */
	class GLM_SDK_API GroundSupportSequenceV0
	{
		GLM_RTTI( "356cc1b9-7cb5-413b-8a50-1df0a9daa01c", glm::GroundSupportSequenceV0, addAttributes )

	public:
		//************************************************************
		//! @name Structures and types
		//************************************************************
		//@{
		class GLM_SDK_API KeyFrameV4
		{
			GLM_RTTI( "793cec27-8ac2-46ba-8de2-dd4d4afa696b", glm::GroundSupportSequenceV0::KeyFrameV4, addAttributes )

		public:

			GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

			/**
			 *
			 * \brief default constructor
			 */
			KeyFrameV4(Time time=0, bool value = false, float groundHeight=0.f, const Vector3& groundNormal=Vector3(0,0,1) );

			/**
			 * \brief Comparison operator
			 * \param otherInstance 
			 * \return if identical
			 */
			bool operator==(const KeyFrameV4& otherInstance) const;

			/**
			 * \brief Comparison operator
			 * \param otherInstance 
			 * \return true if different
			 */
			bool operator!=(const KeyFrameV4& otherInstance) const
			{
				return !((*this)==otherInstance);
			}

			/**
			 * \brief strict weak operator
			 * \param otherInstance 
			 * \return true if strictly inferior
			 */
			inline bool operator<(const KeyFrameV4& otherInstance) const;


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

		public: 
			Time _time;
			bool _value;

			//ground info
			float _groundHeight; // is a normalized delta between eob world height and ground world height
			Vector3 _groundNormal;

		private:
			// foot roll in relative time ( +/- _time )
			Time _preRoll;	//DEPRECATED: to remove from next file format
			Time _postRoll;	//DEPRECATED: to remove from next file format

		} ;
		//@}

	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

		/**
		 * \brief default constructor
		 */
		GroundSupportSequenceV0();

		/**
		 * \brief Comparison operator
		 * \param otherInstance 
		 * \return true if identical
		 */
		bool operator==(const GroundSupportSequenceV0& otherInstance) const;

		/**
		 * \brief Comparison operator
		 * \param otherInstance 
		 * \return true if different
		 */
		bool operator!=(const GroundSupportSequenceV0& otherInstance) const
		{
			return !((*this)==otherInstance);
		}

		/**
		 * \brief Get the activation coefficient at the given time of the sequence
		 * \return 
		 */
		bool getValueAt(KeyFrameV4& keyframe, bool loop ) const;

		/**
		 * \brief Get the phase times for the current playback time
		 * \return true if phases times are found
		 */
		bool getPhasesTime(const Time currentTime, int& currentLoopIndex, Time &previousTime, int& previousLoopIndex, Time &nextTime, int& nextLoopIndex, bool loop) const;

		void crop(const Time& newLoopDuration, const Time& timeOffset);
		void retime(const Time& newLoopDuration);

	public:
		Time _loopDuration;
		glm::Array<KeyFrameV4> _sequence;	//!< the sequence of activations. The first value in the pair reprensent the type of the sequence element (see the enum ElementType), while the second value represent the beginning time of the sequence element. If no sequence element at time=0; it's ElementType::DESACTIVATED by default.

	private:
		/**
		 * \brief register the inspectable attributes to the class RTTI
		 */
		static void addAttributes();
	};

	//------------------------------------------------------------
	inline bool GroundSupportSequenceV0::KeyFrameV4::operator<(const KeyFrameV4& otherInstance) const
	{
		return _time < otherInstance._time;
	}

	//------------------------------------------------------------
	inline bool GroundSupportSequenceV0::KeyFrameV4::operator==(const GroundSupportSequenceV0::KeyFrameV4& otherInstance) const
	{
		return glm::approxEqual(_time, otherInstance._time, static_cast<double>(GLM_NUMERICAL_PRECISION)) 
			&& (_value == otherInstance._value);
	}

	//------------------------------------------------------------
	inline bool GroundSupportSequenceV0::operator==(const GroundSupportSequenceV0& otherInstance) const
	{
		return (_loopDuration==otherInstance._loopDuration) && (_sequence==otherInstance._sequence);
	}

	//-------------------------------------------------------------------------
	inline bool GroundSupportSequenceV0::getValueAt(KeyFrameV4& keyframe, bool loop) const
	{
		if (_sequence.empty())
		{
			return false;
		}
		else if (_sequence.size() == 1)
		{
			return _sequence.begin()->_value;
		}
		else 
		{
			if ((_loopDuration > 0.) && (fabs(keyframe._time) >= _loopDuration))
			{
				if ( !loop )
				{
					return _sequence.back()._value;
				}
				keyframe._time = fmod(keyframe._time, _loopDuration);
			}
			if (keyframe._time < 0.)
			{
				keyframe._time += _loopDuration;
			}
			glm::Array<KeyFrameV4>::const_iterator ite = std::upper_bound(_sequence.begin(), _sequence.end(), keyframe);
			if (ite == _sequence.begin())
			{
				return (_sequence.begin()+_sequence.size()-1)->_value;
			}
			return (--ite)->_value;
		}
	}
}
