/*
	Trig Wars.
	Copyright (c) 2008, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef MOVEABLE_OBJECT_H
#define MOVEABLE_OBJECT_H

//=====================================================================================================================
//=====================================================================================================================

#include "GameObjects\PositionalObject.h"

//=====================================================================================================================
//=====================================================================================================================

namespace Game
{
	//=================================================================================================================
	//=================================================================================================================
	
	class ObstacleManager;

	//=================================================================================================================
	/// @brief		Base class for all game entities that can have both linear and angular velocity
	//=================================================================================================================

	class MoveableObject : public PositionalObject
	{
		public:

			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				//=====================================================================================================
				/// @brief		Constructor: sets the position and orientation of the object
				/// @param		lin_friction - linear friction per second
				/// @param		ang_friction - angular friction per second
				/// @param		lin_vel - linear velocity of the object
				/// @param		ang_vel - angular velocity of the object
				/// @param		position - position of the object
				/// @param		angle - angle in which the object is facing
				//=====================================================================================================
				
				inline MoveableObject
				( 
					float lin_friction = 0.90f,
					float ang_friction = 0.95f,
					const Vec2f & lin_vel = Vec2f::Zero(), 
					float ang_vel = 0, 
					const Vec2f & position = Vec2f::Zero(), 
					float angle = 0
				)
				: PositionalObject(position,angle) , m_lin_vel(lin_vel)
				{
					// Save angular velocity:

					m_ang_vel = ang_vel;

					// Need to precalc friction multipliers:

					SetLinearFriction(lin_friction);
					SetAngularFriction(ang_friction);
				}
				
				//=====================================================================================================
				/// @brief		Gets the linear velocity of the object 
				/// @returns	Linear velocity
				//=====================================================================================================

				inline Vec2f GetLinearVelocity(){ return m_lin_vel; }

				//=====================================================================================================
				/// @brief		Gets the angular velocity of the object 
				/// @returns	Angular velocity
				//=====================================================================================================

				inline float GetAngularVelocity(){ return m_ang_vel; }

				//=====================================================================================================
				/// @brief		Sets the linear velocity of the object 
				/// @param		v - new velocity
				/// @pre		Given floating point velocity must be valid
				//=====================================================================================================

				inline void SetLinearVelocity( const Vec2f & v )
				{ 
					ENSURE( Float::IsValid(v.x) , "Given floating point velocity must be valid" );
					ENSURE( Float::IsValid(v.y) , "Given floating point velocity must be valid" );
				
					m_lin_vel = v; 
				}

				//=====================================================================================================
				/// @brief		Sets the angular velocity of the object 
				/// @param		a - new angular velocity for the object
				//=====================================================================================================

				inline void SetAngularVelocity( float a ){ m_ang_vel = a; }

				//=====================================================================================================
				/// @brief		Gets the linear friction multiplier for this object
				/// @remarks	Friction is a multiplier that reduces speed at a variable rate.
				//=====================================================================================================

				inline float GetLinearFriction(){ return m_lin_friction; }

				//=====================================================================================================
				/// @brief		Gets the angular friction per second for this object
				/// @returns	angular friction per second
				/// @remarks	Friction is a multiplier that reduces speed at a variable rate.
				//=====================================================================================================

				inline float GetAngularFriction(){ return m_ang_friction; }
				
				//=====================================================================================================
				/// @brief		Accelerates the objects linear velocity by the given amount and within given timeframe
				/// @param		a - amount of acceleration
				/// @param		t - time delta between this frame and the last
				/// @remarks	Acceleration is adjusted according to time delta
				/// @pre		Given floating point acceleration must be valid
				/// @pre		Given floating point time delta must be valid
				/// @pre		Given time delta must be greater than zero
				//=====================================================================================================
				
				inline void Acclerate( const Vec2f & a , float t )
				{ 
					ENSURE( Float::IsValid(a.x) , "Given floating point acceleration must be valid"		);
					ENSURE( Float::IsValid(a.y) , "Given floating point acceleration must be valid"		);
					ENSURE( Float::IsValid(t)	, "Given floating point time delta must be valid"		);
					ENSURE( t > 0				, "Given time delta must be greater than zero"			);
				
					m_lin_vel += a * t; 
				}
				
				//=====================================================================================================
				/// @brief		Accelerates the objects angular velocity by the given amount and within given timeframe
				/// @param		a - amount of acceleration
				/// @param		t - time delta between this frame and the last
				/// @remarks	Acceleration is adjusted according to time delta
				/// @pre		Given floating point acceleration must be valid
				/// @pre		Given floating point time delta must be valid
				/// @pre		Given time delta must be greater than zero				
				//=====================================================================================================
				
				inline void AngularAcclerate( float a , float t )
				{ 
					ENSURE( Float::IsValid(a)	, "Given floating point acceleration must be valid" );
					ENSURE( Float::IsValid(t)	, "Given floating point time delta must be valid"	);
					ENSURE( t > 0				, "Given time delta must be greater than zero"		);				
				
					m_ang_vel += a * t; 
				}

				/// Sets the linear friction multiplier

				void SetLinearFriction( float f );

				/// Sets the angular friction multiplier

				void SetAngularFriction( float f );

				/// Updates the moveable object and moves it along according to it's speed:

				virtual void OnUpdate( float time_delta );
				
				/// Does bounding sphere collision with the obstacles present in the given obstacle manager: returns true if there is a collision
				
				bool ObstacleCollide( ObstacleManager * m , float circle_radius );

		private:
		
			//=========================================================================================================
			// Variables
			//=========================================================================================================
			
				/// Linear velocity of the object
				
				Vec2f m_lin_vel;
				
				/// Angular velocity of the object
				
				float m_ang_vel;

				/// Linear friction multiplier: linear speed is multiplied by this for every frame

				float m_lin_friction;

				/// Angular friction multiplier: angular speed is multiplied by this for every frame

				float m_ang_friction;
	};
};

#endif
