/** \file AnimationCooker.h
 * \brief The header file for the 'AnimationCooker' class.
 */

#pragma once
// OSkA Includes
#include "OSkA.h"

namespace OSkA
{
	/** \brief A AnimationCooker performs some preprocessing and is then used with the Shader.
	 *
	 * The AnimationCooker class is used to pass the Animation transform matrices
	 * at a given point of time to the Shader. If we would use the Skeleton itself
	 * it would cost us a lot of processing, what would be done each frame again.
	 * So we use the AnimationCooker to do all the preprocessing what can be done.
	 *
	 * The AnimationCooker also offers some methods for setting some Animation parameters
	 * after the cooking is done. This parameters can be use e.g. for setting the
	 * time the Animation will start, or how long the Animation will last.
	 *
	 * To do the preprocessing it is necessary to create an new AnimationCooker
	 * and to 'cook' a Skeleton. Then the AnimationCooker can be used with the Shader.
	 * An un-cooked AnimationCooker is almost useless.
	 */
	class AnimationCooker
	{
	public:
		/** \brief Default Constructor
		 */
		AnimationCooker();
		/** \brief Destructor
		 */
		~AnimationCooker();

		/** \brief Here the preprocessing happens.
		 *
		 * We give the Cooker a skeleton then
		 * it performs the preprocessing and can be used with the
		 * Shader.
		 * With the timeSamples parameter you can tell how many time
		 * samples the animation will be calculated for. The more samples
		 * you select the better the quality of the animation might be, but
		 * also more memory will be consumed.
		 * \param skeleton	A pointer to the Skeleton containing the Animation.
		 * \param timeSamples	The number of time samples.
		 * \param storeInTexture	Shall the animation data be stored in a texture? (if false it will be stored in an array)
		 */
		void cook(Skeleton* skeleton, unsigned int timeSamples, bool storeInTexture);

		/** \brief Returns the bone transform array at a given point of time.
		 *
		 * The array of bone transform matrices is interpolated for the
		 * given point of time and returned.
		 * You self have to take care to free the memory of the returning array,
		 * the AnimationCooker won't do it.
		 * \param time	The point of time the interpolation will happen for.
		 * \param animationIndex The index of the animation we want the bone transforms of
		 * \return The bone transform array. Has a length of bone count * 16
		 */
		float* getBoneTransform(float time, int animationIndex);

		/** \brief Returns the numer of Bones.
		 *
		 * \return Number of Bones.
		 */
		int getBonesCount();

		/** \brief Sets the time scale animation parameter.
		 *
		 * \param timeScale	The new time scale value;
		 */
		void setTimeScale(float timeScale);
		/** \brief Sets the time offset animation parameter.
		 *
		 * \param timeOffset	The new time offset value;
		 */
		void setTimeOffset(float timeOffset);
		/** \brief Sets the 'repeat before begin' animation parameter.
		 *
		 * \param repeatBeforeBegin	The new 'repeat before begin' value;
		 */
		void setRepeatBeforeBegin(bool repeatBeforeBegin);
		/** \brief Sets the 'repeat after end' animation parameter.
		 *
		 * \param repeatAfterEnd	The new 'repeat after end' value;
		 */
		void setRepeatAfterEnd(bool repeatAfterEnd);
		/** \brief Returns the time scale parameter.
		 *
		 * \return The time scale parameter.
		 */
		float getTimeScale();
		/** \brief Returns the time offset parameter.
		 *
		 * \return The time offset parameter.
		 */
		float getTimeOffset();
		/** \brief Returns the 'repeat before begin' parameter.
		 *
		 * \return The 'repeat before begin' parameter.
		 */
		bool getRepeatBeforeBegin();
		/** \brief Returns the 'repeat after end' parameter.
		 *
		 * \return The 'repeat after end' parameter.
		 */
		bool getRepeatAfterEnd();

		/** \brief Returns the length of the animation
		 *
		 * \param animationIndex The index of the animation we want the length of
		 * \return The length of the animation.
		 */
		float getLength(int animationIndex);

		/** \brief Returns the number of animations stored in this AnimationCooker
		 *
		 * \return Number of animations
		 */
		int getAnimationsCount();

		/** \brief Translates and Scales the time into the animation range [0, length]
		 *
		 * Based on the animation parameters, the given time(called 'world' time)is
		 * translated and scaled so it directly fits into the animation range(0 to
		 * length), called 'animation' time.
		 * \param time	The 'world' time
		 * \param animationIndex The index of the animation we want the time of
		 * \return The 'animation' time
		 */
		float getTimeInAnimation(float time, int animationIndex);

		/** \brief Returns the height of the animation texture
		 * \return Animation texture height
		 */
		unsigned int getTimeSamples();

		/** \brief Returns the id of the animation texture
		 * \return Animation texture id
		 */
		unsigned int getAnimationTextureId();
	private:
		/** \brief The time scale animation parameter.
		 *
		 * The animation usually starts at 0 and ends at length. But sometimes
		 * it is necessary to make an animaton slower of faster. This happens
		 * by this parameter. A time scale of 2 would mean the animation now
		 * lasts twice as long as before, a value of 0.25 would mean it is now
		 * four times faster then before.
		 */
		float timeScale;
		/** \brief The time offset animation parameter.
		 *
		 * With the offset we can say when the animation will start. Usually
		 * it begins at 0 and ends at length, but with the offset it starts at
		 * timeOffset and ends at length+timeOffset
		 *
		 * This parameter is not influenced by the timeScale. So a offset of e.g.
		 * 2 will always sets the Animation begin to 2, even if the timeScale would
		 * be for example 10(in this case, if the animation had a length of 3, the
		 * animation would start at 2 and end at 32)
		 */
		float timeOffset;
		/** \brief The 'repeat before begin' animation paramater
		 *
		 * With the timeOffset parameter it can happen that we get a time which is
		 * before the begin of the Animation. repeatBeforeBegin describes what happens
		 * then. If repeatBeforeBegin is true, we take the animation as repeated endless
		 * before the begin, so the animation would also run before the set begin.
		 * If repeatBeforeBegin is false, we take the animation as frozen, and just take
		 * the transformation of the first Keyframe, so before the begin the Animation would
		 * stand still and start when the time reaches the begin.
		 */
		bool repeatBeforeBegin;
		/** \brief The 'repeat after end' animation parameter.
		 *
		 * As happened before the begin of an animation, it can also happen that we get
		 * at a time after the animation. This variable handles what happens then.
		 * For details see repeatBeforeBegin.
		 */
		bool repeatAfterEnd;

		/** \brief The number of bones in the Animation.
		 */
		int bonesCount;

		/** \brief List of the lenghts of the animations
		 */
		float* lengths;
		/** \brief Number of animations
		 */
		int numAnimations;

		/** \brief The number of time samples the animation is precalculated for
		 */
		unsigned int timeSamples;

		/** \brief Id of the animation texture
		 */
		unsigned int animationTexture;
		/** \brief Array with all transformation matrices
		 *
		 * When not using the data stored in a texture, this
		 * array will be used to pass the actual transformation
		 * matrices to the shader.
		 */
		float* boneTransforms;
	};
}
