//	Tweener.h - Base class (& some subclasses) for a generic tweener
//				implementation.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#ifndef TWEENER_H_
#define TWEENER_H_

#include "MiscHelper.h"

//------------------------------------------------------------------------------
///	Base class for generic tweener implementations.
/*!
	Provides a simple linear interpolation by default.
 */
template <class T>
class Tweener
{
  public:
	///	Constructor.
	/*!
		\param val This is the value that we want to tween. It is a reference
		so that every time tick() is called the actual variable (which will
		probably be a member of another class) is updated as a result. The value
		of val when this constructor is called is taken as the start point of
		the tweening.
		\param endVal The value that we want to tween towards.
		\param deltaVal The amount to move along the tween curve everytime
		tick() is called.
	 */
	Tweener(T& val, T endVal, float deltaVal):
	value(val),
	start(val),
	end(endVal),
	index(0.0f),
	delta(deltaVal),
	delay(0.0f)
	{
		
	};
	///	Copy Constructor.
	Tweener(const Tweener<T>& other):
	value(other.value),
	start(other.start),
	end(other.end),
	index(other.index),
	delta(other.delta),
	delay(other.delay)
	{

	};
	///	Destructor.
	virtual ~Tweener() {};

	///	Test.
	Tweener<T>& operator=(const Tweener<T>& other)
	{
		value = other.value;
		start = other.start;
		end = other.end;
		index = other.index;
		delta = other.delta;
		delay = other.delay;

		return *this;
	};

	///	Updates our position within the tween curve, sets value accordingly.
	/*!
		\param globalDelta The delta value between frames. By default this is
		set to 60fps.

		By default this provides a simple linear interpolation.
	 */
	virtual void tick(float globalDelta = 1.0f)
	{
		index += delta * globalDelta;
		if(index > 1.0f)
			index = 1.0f;

		if(index >= 0.0f)
			value = (start * (1.0f-index)) + (end * index);
	};
	///	Returns true if the tweener has reached its endpoint.
	bool getFinished() const {return (index >= 1.0f);};

	///	Resets the tweener so it can move to a new endpoint.
	/*!
		This takes value's current value as the start point.
	 */
	void reset(T newEndVal)
	{
		start = value;
		end = newEndVal;
		index = -delay;
	};
	///	Sets the amount the tweener moves with each tick().
	void setDelta(float deltaVal)
	{
		delta = deltaVal;
	};
	///	Sets the tweener's position within its curve (0->1).
	void setIndex(float val)
	{
		index = val;
	};
	///	Sets the amount of delay before the tweener starts altering value.
	void setDelay(float val)
	{
		delay = val;
	};

	///	Returns the tweener's current destination.
	const T& getEndVal() const {return end;};
  protected:
	///	The variable to be tweened.
	T& value;
	///	The start point of the tween curve.
	T start;
	///	The end point of the tween curve.
	T end;
	///	Our position within the curve.
	float index;
	///	The amount to advance index by in tick().
	float delta;
	///	The amount of delay before tick() starts altering value.
	float delay;
};

//------------------------------------------------------------------------------
///	Simple exp-like tweener implementation.
template <class T>
class ExpTweener : public Tweener<T>
{
  public:
	///	Constructor.
	ExpTweener(T& val, T endVal, float deltaVal):
	Tweener<T>(val, endVal, deltaVal)
	{

	};
	///	Destructor.
	~ExpTweener() {};

	///	Updates our position within the tween curve, sets value accordingly.
	void tick(float globalDelta = 1.0f)
	{
		float tempf;

		Tweener<T>::index += Tweener<T>::delta * globalDelta;
		if(Tweener<T>::index > 1.0f)
			Tweener<T>::index = 1.0f;

		if(Tweener<T>::index >= 0.0f)
		{
			tempf = fakeExp(Tweener<T>::index);

			Tweener<T>::value = (Tweener<T>::start * (1.0f-tempf)) + (Tweener<T>::end * tempf);
		}
	};
};

//------------------------------------------------------------------------------
///	Simple log-like tweener implementation.
template <class T>
class LogTweener : public Tweener<T>
{
  public:
	///	Constructor.
	LogTweener(T& val, T endVal, float deltaVal):
	Tweener<T>(val, endVal, deltaVal)
	{

	};
	///	Destructor.
	~LogTweener() {};

	///	Updates our position within the tween curve, sets value accordingly.
	void tick(float globalDelta = 1.0f)
	{
		float tempf;

		Tweener<T>::index += Tweener<T>::delta * globalDelta;
		if(Tweener<T>::index > 1.0f)
			Tweener<T>::index = 1.0f;

		if(Tweener<T>::index >= 0.0f)
		{
			tempf = fakeLog(Tweener<T>::index);

			Tweener<T>::value = (Tweener<T>::start * (1.0f-tempf)) + (Tweener<T>::end * tempf);
		}
	};
};

//------------------------------------------------------------------------------
///	Simple s-shaped (cos(0->pi)) tweener implementation.
template <class T>
class STweener : public Tweener<T>
{
  public:
	///	Constructor.
	STweener(T& val, T endVal, float deltaVal):
	Tweener<T>(val, endVal, deltaVal)
	{

	};
	///	Destructor.
	~STweener() {};

	///	Updates our position within the tween curve, sets value accordingly.
	void tick(float globalDelta = 1.0f)
	{
		float tempf;

		Tweener<T>::index += Tweener<T>::delta * globalDelta;
		if(Tweener<T>::index > 1.0f)
			Tweener<T>::index = 1.0f;

		if(Tweener<T>::index >= 0.0f)
		{
			tempf = 1.0f - ((cosf(Tweener<T>::index*NIALL_PI) * 0.5f)+0.5f);

			Tweener<T>::value = (Tweener<T>::start * (1.0f-tempf)) + (Tweener<T>::end * tempf);
		}
	};
};

//------------------------------------------------------------------------------
///	A bouncing tweener implementation.
template <class T>
class BounceTweener : public Tweener<T>
{
  public:
	///	Constructor.
	BounceTweener(T& val, T endVal, float deltaVal):
	Tweener<T>(val, endVal, deltaVal)
	{

	};
	///	Destructor.
	~BounceTweener() {};

	///	Updates our position within the tween curve, sets value accordingly.
	void tick(float globalDelta = 1.0f)
	{
		float tempf;

		Tweener<T>::index += Tweener<T>::delta * globalDelta;
		if(Tweener<T>::index > 1.0f)
			Tweener<T>::index = 1.0f;

		if(Tweener<T>::index >= 0.0f)
		{
			tempf = fabsf(cosf(Tweener<T>::index*2.5f*NIALL_PI));
			tempf *= fakeLog(1.0f-Tweener<T>::index);
			tempf = 1.0f - tempf;

			Tweener<T>::value = (Tweener<T>::start * (1.0f-tempf)) + (Tweener<T>::end * tempf);
		}
	};
};

//------------------------------------------------------------------------------
///	A rotated S-shaped tweener (i.e. goes fast-slow-fast rather than slow-fast-slow).
template <class T>
class RotSTweener : public Tweener<T>
{
  public:
	///	Constructor.
	RotSTweener(T& val, T endVal, float deltaVal):
	Tweener<T>(val, endVal, deltaVal)
	{

	};
	///	Destructor.
	~RotSTweener() {};

	///	Updates our position within the tween curve, sets value accordingly.
	void tick(float globalDelta = 1.0f)
	{
		float tempf;

		Tweener<T>::index += Tweener<T>::delta * globalDelta;
		if(Tweener<T>::index > 1.0f)
			Tweener<T>::index = 1.0f;

		if(Tweener<T>::index >= 0.0f)
		{
			tempf = 1.0f - (acosf((Tweener<T>::index*2.0f)-1.0f)/NIALL_PI);

			Tweener<T>::value = (Tweener<T>::start * (1.0f-tempf)) + (Tweener<T>::end * tempf);
		}
	};
};

#endif
