//===========================================================================//
// File:	AnimIteratorManager.hpp                                                 
// Project: MechWarrior 4
// Contents: 
//---------------------------------------------------------------------------//
//   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


#include <MW4\MW4AnimationSystem.hpp>


namespace MW4Animation {
	
	struct ChannelApplication 
	{
		bool hasPosition;
		bool hasRotation;
		bool hasVelocity;
		bool hasAngularVelocity;
		bool hasTrigger;
		
		ChannelApplication()
		{
			hasPosition = false;
			hasRotation = false;
			hasVelocity = false;
			hasTrigger = false;
			hasAngularVelocity = false;

			positionValue = Stuff::Point3D::Identity;
			velocityValue = Stuff::Point3D::Identity;
			angularVelocityValue = Stuff::Vector3D::Identity;
			rotationValue = Stuff::UnitQuaternion::Identity;
			triggerValue = 0;
		};

		// only one of these will be used
		// Union keyword doesn't function
		// for some reason, oh well, there
		// is only one of these ever in the
		// world.
		Stuff::Point3D    positionValue;
		Stuff::Point3D	  velocityValue;
		Stuff::Vector3D	  angularVelocityValue;
		Stuff::UnitQuaternion rotationValue;
		int				  triggerValue;

	};

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

	// this can be optimized to use a static memory set for all the joints

#if defined(MW4_ANIMATION_DEBUG)
static int TabCount = 0;
static int BlendCount = 0;
#endif
	
#define MAX_BUFFER_JOINTS  70

	class BlendBuffer
	{

	//##########################################################################
	// Memory Allocation Support
	//
	private:
		static Stuff::MemoryBlock *AllocatedMemory;

	public:
		void*
			operator new(size_t)
				{return AllocatedMemory->New();}
		void
			operator delete(void *where)
				{AllocatedMemory->Delete(where);}

	//##########################################################################
	// execution Support
	//
	public:

		enum {
			DefaultFlagBit = 0,
			PositionDirtyFlagBit,
			VelocityDirtyFlagBit,
			AngularVelocityDirtyFlagBit,
			RotationDirtyFlagBit,
			TriggerDirtyFlagBit
		};
		
		enum {
			PositionDirtyFlag =  1<<PositionDirtyFlagBit,
			VelocityDirtyFlag = 1<<VelocityDirtyFlagBit,
			AngularVelocityDirtyFlag = 1<<AngularVelocityDirtyFlagBit,
			RotationDirtyFlag = 1<<RotationDirtyFlagBit,
			TriggerDirtyFlag = 1<<TriggerDirtyFlagBit
		};

		BlendBuffer(int number_of_joints)
		{
			#if defined(MW4_ANIMATION_DEBUG)
				++BlendCount;
			#endif

			Verify(number_of_joints > 0);
			numberOfJoints = number_of_joints;

			Verify(number_of_joints < MAX_BUFFER_JOINTS);
			
			for (int i = 0; i < numberOfJoints; ++i)
			{
				dirtyFlags[i] = 0;
			}
		}

		~BlendBuffer()
		{

		}

		static void
			InitializeClass();
		
		static void
			TerminateClass();


		int numberOfJoints;

		BYTE					dirtyFlags[MAX_BUFFER_JOINTS];
		Stuff::Point3D			positionBuffer[MAX_BUFFER_JOINTS];
		Stuff::Point3D			velocityBuffer[MAX_BUFFER_JOINTS];
		Stuff::UnitQuaternion	rotationBuffer[MAX_BUFFER_JOINTS];
		Stuff::Vector3D			angularVelocityBuffer[MAX_BUFFER_JOINTS];
		int						triggerBuffer[MAX_BUFFER_JOINTS];

		void SetDirtyPosition(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			dirtyFlags[joint_number] |= PositionDirtyFlag;
		}

		void SetDirtyVelocity(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			dirtyFlags[joint_number] |= VelocityDirtyFlag;
		}
		
		void SetDirtyAngularVelocity(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			dirtyFlags[joint_number] |= AngularVelocityDirtyFlag;
		}

		void SetDirtyRotation(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			dirtyFlags[joint_number] |= RotationDirtyFlag;
		}

		void SetDirtyTrigger(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			dirtyFlags[joint_number] |= TriggerDirtyFlag;
		}

		bool IsPositionDirty(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			return (dirtyFlags[joint_number]&PositionDirtyFlag) != 0;
		}
		bool IsVelocityDirty(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			return (dirtyFlags[joint_number]&VelocityDirtyFlag) != 0;
		}
		bool IsAngularVelocityDirty(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			return (dirtyFlags[joint_number]&AngularVelocityDirtyFlag) != 0;
		}
		bool IsRotationDirty(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			return (dirtyFlags[joint_number]&RotationDirtyFlag) != 0;
		}
		bool IsTriggerDirty(int joint_number)
		{
			Verify(joint_number >= 0);
			Verify(joint_number < numberOfJoints);
			return (dirtyFlags[joint_number]&TriggerDirtyFlag) != 0;
		}


	};


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

	class AnimHierarchyNode:
		public Stuff::Plug
	{
	//##########################################################################
	// Memory Allocation Support
	//
	private:
		static Stuff::MemoryBlock *AllocatedMemory;

	public:
		void*
			operator new(size_t)
				{return AllocatedMemory->New();}
		void
			operator delete(void *where)
				{AllocatedMemory->Delete(where);}

		static void
			InitializeClass();
		
		static void
			TerminateClass();


	public:

		
		AnimHierarchyNode(Stuff::Scalar blend_value);
		~AnimHierarchyNode();

		void SetBlendValue(Stuff::Scalar blend_value)
			{blendValue = blend_value;}
		
		// all the AnimHierarchyNodes on my level blend with 
		// this value...
		Stuff::Scalar blendValue;

		Stuff::SlotOf<AnimHierarchyNode*> parentNode;
		Stuff::ChainOf<AnimHierarchyNode*> childrenNodes;

		// all my anim iterators blend with their iterator blend value
		Stuff::ChainOf<AnimIterator*> animIterators;
	};

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

	class AnimHierarchyIteratorManager:
		public Stuff::Plug
	{
	public:

		typedef void
			(*UserDefinedApplyChannelFunction)(
				ChannelApplication &channel_data,
				int joint_index,
				void *user_data
			);

		AnimHierarchyIteratorManager(UserDefinedApplyChannelFunction apply_function, void *user_data);
		~AnimHierarchyIteratorManager();

		UserDefinedApplyChannelFunction applyFunction;
		void *userData;


		Stuff::SlotOf<AnimHierarchyNode*> iteratorHierarchy;
		Stuff::SlotOf<AnimHierarchyNode*> currentLayer;

		AnimHierarchyNode *PushLayer(Stuff::Scalar blend_value);
		void PopLayer();
		void UndoPush();
		void AddAnimToCurrentLayer(AnimIterator *interator);
		

		void BlendAndApply(
				int total_joint_count, 
				void *override_user_data = NULL, 
				UserDefinedApplyChannelFunction override_apply_function = NULL
			);

		static bool doTransitionBlend;

		void Blend(AnimHierarchyNode *current_node, BlendBuffer *blend_result);
		void Apply(BlendBuffer *blend_result, void *user_data, UserDefinedApplyChannelFunction apply_function);
		void Combine(BlendBuffer *target_buffer, BlendBuffer *new_buffer, Stuff::Scalar percent);
		void AnimCombine(BlendBuffer *target_buffer, AnimIterator *anim_iterator, Stuff::Scalar percent);

	};
}

