//===========================================================================//
// File:	SpringOf.hpp                                                 
// Project: MechWarrior 4
// Contents: 
//			
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/20/00  JSE  Initial coding, 
//---------------------------------------------------------------------------//
// Copyright (C) 2000, Microsoft Corp.
// All Rights reserved worldwide
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//===========================================================================//

#pragma once

#include "MW4.hpp"

namespace MechWarrior4 {


	//##########################################################################
	//#########################    SpringOf    ###############################
	//##########################################################################
	
	template <class T> class SpringOf
	{
	public:
		
		T springLimit;
		T springConstant;
		T damageVelocity;
		T currentPosition;
		T currentVelocity;
		T forceLose;
		T notchVelocity;

		bool initialized;

		SpringOf()
		{
			initialized = false;
		}

		SpringOf(const T& spring_limit, const T& spring_constant, const T& force_lose, const T& notch_velocity)
			: springLimit(spring_limit)
			, springConstant(spring_constant)
			, forceLose(force_lose)
			, notchVelocity(notch_velocity)
		{
			initialized = true;

			for (int i = 0; i < T::GetMemberCount(); i++)
			{
				currentPosition[i] = 0.0f;
				currentVelocity[i] = 0.0f;
				damageVelocity[i] = 0.0f;
			}
		};

		~SpringOf(){};

		void Initialize(const T& spring_limit, const T& spring_constant, const T& force_lose, const T& notch_velocity)
		{
			initialized = true;
			springLimit = spring_limit;

			springConstant = spring_constant;
			forceLose = force_lose;
			notchVelocity = notch_velocity;

			for (int i = 0; i < T::GetMemberCount(); i++)
			{
				currentPosition[i] = 0.0f;
				currentVelocity[i] = 0.0f;
				damageVelocity[i] = 0.0f;
			}
			
		};

		
		void HitSpring(const T& how_hard)
		{
			Verify(initialized);

			for (int i = 0; i < T::GetMemberCount(); i++)
			{
				damageVelocity[i] += how_hard[i];
			}
		}

		void Reset(void)
		{
			for (int i = 0; i < T::GetMemberCount(); i++)
			{
				currentPosition[i] = 0.0f;
				currentVelocity[i] = 0.0f;
				damageVelocity[i] = 0.0f;
			}
		}

		T GetCurrentPosition(void)
		{
			return currentPosition;
		};

		void AdvanceTime(Stuff::Scalar time_slice)
		{

			Max_Clamp(time_slice, 0.2f);

			Verify(initialized);
			//---------------------------------------------
			// calculate the distance we are stretched
			//---------------------------------------------

			T return_velocity = T::Identity;
			T return_force = T::Identity;
			T delta_position = T::Identity;

			T last_position = currentPosition; 

			for (int i = 0; i < T::GetMemberCount(); i++)
			{
			
				//---------------------------------------------
				// calculate the return force
				//---------------------------------------------
				return_force[i] = springConstant[i] * -currentPosition[i];
				
				//---------------------------------------------
				// cacluclate the return velocity
				//---------------------------------------------
				return_velocity[i] = return_force[i] * time_slice;
				currentVelocity[i] += return_velocity[i];

				//---------------------------------------------
				// add the damage velocity
				//---------------------------------------------
				currentVelocity[i] += damageVelocity[i];
				



				// if velocity is appraoching zero...

				if (currentPosition[i] > 0.0f)
				{
					if (currentVelocity[i] < 0.0f)
					{
						Max_Clamp(currentVelocity[i], -notchVelocity[i]);
					}
				}
				else if (currentPosition[i] < 0.0f)
				{
					if (currentVelocity[i] > 0.0f)
					{
						Min_Clamp(currentVelocity[i], notchVelocity[i]);
					}
				}



				//---------------------------------------------
				// Add the new positoin to the eye position.
				//---------------------------------------------

				delta_position[i] = currentVelocity[i] * time_slice;
				currentPosition[i] += delta_position[i];

				// if we are approaching zero
				
				if (Abs(currentVelocity[i]) <= notchVelocity[i])
				{
					if (last_position[i] > 0.0f && currentPosition[i] <= 0.0f)
					{
						currentVelocity[i] = 0.0f;
						currentPosition[i] = 0.0f;
					}
					else if (last_position[i] < 0.0f && currentPosition[i] >= 0.0f)
					{
						currentVelocity[i] = 0.0f;
						currentPosition[i] = 0.0f;
					}
				}
				

				//---------------------------------------------
				// Bleed some force off
				//---------------------------------------------

				if (currentPosition[i] > 0.0f && currentVelocity[i] < 0.0f)
				{
					currentVelocity[i] += (forceLose[i] * time_slice);
					Max_Clamp(currentVelocity[i], 0.0f);
				}
				else if (currentPosition[i] < 0.0f && currentVelocity[i] > 0.0f)
				{
					currentVelocity[i] -= (forceLose[i] * time_slice);
					Min_Clamp(currentVelocity[i], 0.0f);
				}

				//-----------------------------------------------------
				// clamp the positions so it never goes out of bounds
				//-----------------------------------------------------

				if (currentPosition[i] > springLimit[i])
				{
					currentVelocity[i] = 0.0f;
					currentPosition[i] = springLimit[i];
				}
				else if (currentPosition[i] < -springLimit[i])
				{
					currentVelocity[i] = 0.0f;
					currentPosition[i] = -springLimit[i];
				}

			}

			damageVelocity = T::Identity;
		};
	};

}
