//===========================================================================//
// File:	AnimIterator.hpp                                                 
// Project: MechWarrior 4
// Contents: 
//			This defines the iterator classes for animation.  The iterators
//	can walk keyframes forward and backward by an absolute or relitive
//  frame, percent, or time.  Got it?  
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 07/01/98  JSE  Initial coding, 
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive, Inc.
// All Rights reserved worldwide
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//===========================================================================//

#pragma once


namespace MW4Animation {
	class AnimIterator;
}

	
#include <MW4\MW4AnimationSystem.hpp>
#include <MW4\AnimInstance.hpp>

namespace MW4Animation {


//#############################################################################
//#######################       AnimIterator          #########################
//#############################################################################


	class AnimIterator :
		public Stuff::Plug
	{
	private:
		Stuff::Scalar currentTime;

		Stuff::Scalar velocityScale;
		AnimData *animationData;
		BYTE	*nextFrame;
		Stuff::Scalar blendValue;

		bool skipChannels;

	public:
		AnimInstance *animationInstance;

		enum{
			MaxKeyFrame=255
		};
	public:

		// note: you must reset the animation to the start
		// or end before animating.
		AnimIterator(AnimInstance *animation_instance);
		~AnimIterator();


		//skips everything but velocity!
		void SkipChannels()
		{
			//skipChannels = true;
		}
	
		void DontSkipChannels()
		{
			//skipChannels = false;
		}

		bool DoISkipChannels()
		{
			return skipChannels;
		}

		void SetVelocityScale(Stuff::Scalar vel_scale)
		{
			velocityScale = vel_scale;
		}

		Stuff::Scalar GetVelocityScale()
		{
			return velocityScale;
		}

		Stuff::Scalar GetBlendValue()
		{
			return blendValue;
		}

		void SetBlendValue(Stuff::Scalar blend_value)
		{
			Verify(blend_value >= 0);
			blendValue = blend_value;
		}

		AnimData* GetAnimData()
		{
			return animationData;
		}

		//----------------------------------------
		// Convert to and from absolute channels
		//----------------------------------------
		
		int
			ToAbsoluteChannel(
				BYTE joint_number, 
				BYTE channel_number
			);
		
		void FromAbsoluteChannel(
				int absolute_Channel, 
				BYTE& joint_number, 
				BYTE& channel_number
			);

		//------------------------------
		// Get counts if you need them
		//------------------------------

		BYTE GetJointCount();
		BYTE GetJointChannelCount(BYTE joint_number);
		int GetAnimChannelCount();
		int GetKeyFrameCount(int absolute_channel_number);

		//------------------------------
		// Get frames and indexes.
		//------------------------------

		//  -1 means that there are no more keyframes in that direction
		int GetForwardFrameIndex(int absolute_channel_number);
		int GetReverseFrameIndex(int absolute_channel_number);

		// NULL means that there are no more keyframes in that direction
		BaseKeyframe * GetForwardKeyFrame(int absolute_channel_number);
		BaseKeyframe * GetReverseKeyFrame(int absolute_channel_number);

		// If there is only one keyframe these will return the same keyframe
		BaseKeyframe *  GetFirstKeyFrame(int absolute_channel_number);
		BaseKeyframe *  GetLastKeyFrame(int absolute_channel_number);


		// A little diffrent from getfirst, this gets the keyframe at the start position
		// if a keyframe is out of range ( less than or greater than the time span of the
		// animation, than this will find the first real keyframe.
		// If there is only one keyframe these will return the same keyframe
		BaseKeyframe *  GetStartKeyFrame(int absolute_channel_number);
		BaseKeyframe *  GetStopKeyFrame(int absolute_channel_number);

		// NULL means that there is no such keyframe
		BaseKeyframe *  GetKeyFrame(int absolute_channel_number, int index);



		// NULL means that there are no more keyframes in that direction
		BaseKeyframeTime * GetForwardKeyFrameTime(int absolute_channel_number);
		BaseKeyframeTime * GetReverseKeyFrameTime(int absolute_channel_number);

		// If there is only one keyframe these will return the same keyframe
		BaseKeyframeTime *  GetFirstKeyFrameTime(int absolute_channel_number);
		BaseKeyframeTime *  GetLastKeyFrameTime(int absolute_channel_number);


		// A little diffrent from getfirst, this gets the keyframe at the start position
		// if a keyframe is out of range ( less than or greater than the time span of the
		// animation, than this will find the first real keyframe.
		// If there is only one keyframe these will return the same keyframe
		BaseKeyframeTime *  GetStartKeyFrameTime(int absolute_channel_number);
		BaseKeyframeTime *  GetStopKeyFrameTime(int absolute_channel_number);

		// NULL means that there is no such keyframe
		BaseKeyframeTime *  GetKeyFrameTime(int absolute_channel_number, int index);


		//-----------------------------------------------------------------------
		// Reset start makes the animation snap to start, end goes to the end
		// use this instead of settime if you want to go the beginning
		// it's cheaper.
		//-----------------------------------------------------------------------

		void ResetStart();
		void ResetEnd();


		// all of the frame methods down here treat frame 0 as the frame
		// at starttime, not keyframe index 0, like above.  a frame down
		// here is more of an intangeble thing.
		
		// time down here is also reletive to startTime
		// 

		// percents are 1.0 > 0


		// Carryover functions

		// these are the "smart" functions, they figure out back or forward.
		Stuff::Scalar GetCarryOverTime();
		int  GetCarryOverFrame(int frames_per_second);
		Stuff::Scalar GetCarryOverPosition();

		// forward carryovers
		Stuff::Scalar GetCarryOverTimeForward();
		int  GetCarryOverFrameForward(int frames_per_second);
		Stuff::Scalar GetCarryOverPositionForward();
	
		// reverse carryovers
		Stuff::Scalar GetCarryOverTimeReverse();
		int  GetCarryOverFrameReverse(int frames_per_second);
		Stuff::Scalar GetCarryOverPositionReverse();
	


		Stuff::Scalar GetRealTime()
		{
			return currentTime;
		}

		//------------------------------
		// Absolute Settings, adjusted though, so time at the start
		// of an anim is 0
		//------------------------------
		void SetTime(Stuff::Scalar time);
		Stuff::Scalar GetTime();

		void SetFrame(int frame, int frames_per_second);
		int  GetFrame(int frames_per_second);

		void SetPosition(Stuff::Scalar percentage_of_animation);
		Stuff::Scalar GetPosition();

		//------------------------------
		// Relative Settings
		//------------------------------

		// The Move commands check to see if the number is going forward or backward
		// the increment/decrement commands assume the command is in the correct direction

		bool MoveTime(Stuff::Scalar time_slice);
		bool MoveFrame(int frame_count, int frames_per_second);
		bool MovePosition(Stuff::Scalar percent); 


		// Don't pass zeros to increment/decrement functions or I'll kick your ass!!!
		bool IncrementTime(Stuff::Scalar time_slice);
		bool DecrementTime(Stuff::Scalar time_slice);

		bool IncrementFrame(int frame_count, int frames_per_second);
		bool DecrementFrame(int frame_count, int frames_per_second);

		bool IncrementPosition(Stuff::Scalar percent);
		bool DecrementPosition(Stuff::Scalar percent);

		BaseKeyframe * GetKeyFrameAtTime(int absolute_channel_number, Stuff::Scalar time);
		BaseKeyframe * GetKeyFrameAtFrame(int absolute_channel_number, int frame, int frames_per_second);
		BaseKeyframe * GetKeyFrameAtPosition(int absolute_channel_number, Stuff::Scalar percentage_of_animation);


		BaseKeyframeTime * GetKeyFrameTimeAtTime(int absolute_channel_number, Stuff::Scalar time);
		BaseKeyframeTime * GetKeyFrameTimeAtFrame(int absolute_channel_number, int frame, int frames_per_second);
		BaseKeyframeTime * GetKeyFrameTimeAtPosition(int absolute_channel_number, Stuff::Scalar percentage_of_animation);


	public:
		void
			TestInstance() const
				{}
		static bool
			TestClass()
				{return true;}
	};



	// Inlines

	//
	//#############################################################################
	//#############################################################################
	//

	inline BYTE 
		AnimIterator::GetJointCount()
	{
		Check_Pointer(animationData);
		return animationData->GetJointCount();
	}

	//
	//#############################################################################
	//#############################################################################
	//

	inline BYTE 
		AnimIterator::GetJointChannelCount(BYTE joint_number)
	{
		Verify(joint_number < animationData->GetJointCount());
		return animationData->animJointBlock[joint_number].channelsUsed;
	}

	//
	//#############################################################################
	//#############################################################################
	//
	inline int 
		AnimIterator::GetAnimChannelCount()
	{
		Check_Pointer(animationData);
		return animationData->GetChannelCount();
	}

	//
	//#############################################################################
	//#############################################################################
	//

	inline int	
		AnimIterator::GetKeyFrameCount(int absolute_channel_number)
	{
		Verify(absolute_channel_number < animationData->GetChannelCount());

		return animationData->animChannelBlock[absolute_channel_number].keyframeCount;

	}


	//
	//#############################################################################
	//#############################################################################
	//

	inline Stuff::Scalar 
		AnimIterator::GetTime()
	{
		return currentTime -= animationData->animHeaderBlock->startTime;
	}

	//
	//#############################################################################
	//#############################################################################
	//

	inline void 
		AnimIterator::SetFrame(int frame, int frames_per_second)
	{
		Verify(frames_per_second > 0);
		SetTime((Stuff::Scalar)frame / frames_per_second);
	}

	//
	//#############################################################################
	//#############################################################################
	//

	inline int  
		AnimIterator::GetFrame(int frames_per_second)
	{
		Verify(frames_per_second > 0);
		return (int)(GetTime()*frames_per_second);
	}

	//
	//#############################################################################
	//#############################################################################
	//

	inline void 
		AnimIterator::SetPosition(Stuff::Scalar percentage_of_animation)
	{

		if (percentage_of_animation == 0.0f)
		{
			ResetStart();
			return;
		}

		Verify(percentage_of_animation > 0.0f);
		Verify(percentage_of_animation <= 1.0f);

		SetTime(
			Stuff::Lerp(
				0.0f,
				animationData->animHeaderBlock->endTime-animationData->animHeaderBlock->startTime,
				percentage_of_animation
			)
		);
			
	}

	//
	//#############################################################################
	//#############################################################################
	//

	inline Stuff::Scalar 
		AnimIterator::GetPosition()
	{
		Verify((animationData->animHeaderBlock->endTime -
			 animationData->animHeaderBlock->startTime) != 0.0f);


		Stuff::Scalar percent = 
			(currentTime-animationData->animHeaderBlock->startTime) / 
				(animationData->animHeaderBlock->endTime -
				 animationData->animHeaderBlock->startTime);


		Verify(percent >= 0.0f);
		Verify(percent <= 1.0f);

		return percent;

	}

	//
	//#############################################################################
	//#############################################################################
	//


	inline bool 
		AnimIterator::MoveFrame(int frame_count, int frames_per_second)
	{

		Verify(frame_count != 0);
		Verify(frames_per_second > 0);
		return MoveTime((Stuff::Scalar)frame_count / frames_per_second);

	}

	//
	//#############################################################################
	//#############################################################################
	//


	inline bool 
		AnimIterator::MovePosition(Stuff::Scalar percent)
	{

		Verify((animationData->animHeaderBlock->endTime -
			 animationData->animHeaderBlock->startTime) != 0.0f);


		Stuff::Scalar time = 
				(animationData->animHeaderBlock->endTime -
				 animationData->animHeaderBlock->startTime) * percent;


		Verify(time > 0.0f);
		Verify(time <= 1.0f);

		return MoveTime(time);
	}

	
	//
	//#############################################################################
	//#############################################################################
	//

	inline bool 
		AnimIterator::IncrementFrame(int frame_count, int frames_per_second)
	{
		Verify(frame_count != 0);
		Verify(frames_per_second > 0);
		return IncrementTime((Stuff::Scalar)frame_count / frames_per_second);
	}


	//
	//#############################################################################
	//#############################################################################
	//

	inline bool 
		AnimIterator::DecrementFrame(int frame_count, int frames_per_second)
	{
		Verify(frame_count != 0);
		Verify(frames_per_second > 0);
		return DecrementTime((Stuff::Scalar)frame_count / frames_per_second);
	}


	//
	//#############################################################################
	//#############################################################################
	//

	inline bool 
		AnimIterator::IncrementPosition(Stuff::Scalar percent)
	{
		Verify((animationData->animHeaderBlock->endTime -
			 animationData->animHeaderBlock->startTime) != 0.0f);


		Stuff::Scalar time = 
				(animationData->animHeaderBlock->endTime -
				 animationData->animHeaderBlock->startTime) * percent;


		//Verify(time > 0.0f);
		//Verify(time <= 1.0f);

		return IncrementTime(time);
	}


	//
	//#############################################################################
	//#############################################################################
	//

	inline bool 
		AnimIterator::DecrementPosition(Stuff::Scalar percent)
	{
		Verify((animationData->animHeaderBlock->endTime -
			animationData->animHeaderBlock->startTime) != 0.0f);


		Stuff::Scalar time = 
				(animationData->animHeaderBlock->endTime -
				 animationData->animHeaderBlock->startTime) * percent;


		//Verify(time > 0.0f);
		//Verify(time <= 1.0f);

		return DecrementTime(time);
	}

	//
	//#############################################################################
	//#############################################################################
	//

	inline BaseKeyframe * 
		AnimIterator::GetKeyFrameAtFrame(int absolute_channel_number, int frame, int frames_per_second)
	{
		Verify(frames_per_second > 0);
		return GetKeyFrameAtTime(absolute_channel_number, (Stuff::Scalar)frame / frames_per_second);
	}


	//
	//#############################################################################
	//#############################################################################
	//

	inline BaseKeyframe * 
		AnimIterator::GetKeyFrameAtPosition(int absolute_channel_number, Stuff::Scalar percentage_of_animation)
	{
		Verify((animationData->animHeaderBlock->endTime -
			animationData->animHeaderBlock->startTime) != 0.0f);

		Stuff::Scalar time = 
				(animationData->animHeaderBlock->endTime -
				 animationData->animHeaderBlock->startTime) * percentage_of_animation;


		Verify(time > 0.0f);
		Verify(time <= 1.0f);

		return GetKeyFrameAtTime(absolute_channel_number, time);
	}


	//
	//#############################################################################
	//#############################################################################
	//

	inline BaseKeyframeTime * 
		AnimIterator::GetKeyFrameTimeAtFrame(int absolute_channel_number, int frame, int frames_per_second)
	{
		Verify(frames_per_second > 0);
		return GetKeyFrameTimeAtTime(absolute_channel_number, (Stuff::Scalar)frame / frames_per_second);
	}


	//
	//#############################################################################
	//#############################################################################
	//

	inline BaseKeyframeTime * 
		AnimIterator::GetKeyFrameTimeAtPosition(int absolute_channel_number, Stuff::Scalar percentage_of_animation)
	{
		Verify((animationData->animHeaderBlock->endTime -
			animationData->animHeaderBlock->startTime) != 0.0f);

		Stuff::Scalar time = 
				(animationData->animHeaderBlock->endTime -
				 animationData->animHeaderBlock->startTime) * percentage_of_animation;


		Verify(time > 0.0f);
		Verify(time <= 1.0f);

		return GetKeyFrameTimeAtTime(absolute_channel_number, time);
	}


}

