/*
	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 AI_CONTROLLED_H
#define AI_CONTROLLED_H

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

#include "GameObjects\MoveableObject.h"
#include "GameObjects\DamageableObject.h"
#include "GameObjects\AIManager.h"

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

namespace Game
{
	//=================================================================================================================
	//=================================================================================================================

	class Camera;
	class PlayerShip;
	class AIManager;
	class QuadTreeEntry;

	//=================================================================================================================
	/// @brief		Base class for all AI controlled objects.
	//=================================================================================================================

	class AIControlled : public MoveableObject ,  public DamageableObject , public Poolable
	{
		public:
		
			//=========================================================================================================
			// Structures
			//=========================================================================================================
			
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// This structure represents the action an ai should take to flock. It includes both the steering 
				/// that should be applied as well as the speed that the ai should travel at.
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				
				struct FlockAction
				{
					/// The amount of steering that should be applied
					
					float m_steering;
					
					/// The amount of acceleration that should be applied from 0-1. This is a percentage.
					
					float m_acceleration;
				};
				
			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				/// Creates the AIControlled object
				
				AIControlled( AIManager * manager , float max_health );			
				
				/// Destroys the object
				
				~AIControlled();
				
				/// Gets the score given for killing this object
				
				virtual float GetKillScore() = 0;
				
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// Used for buffered rendering of the object: puts its lines onto the given stack for batch rendering
				/// This is an optimised alternative to OnDraw()
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

				virtual unsigned BufferedDraw( Stack & s , const Vec2f & cam_pos , float z ) = 0;
				
				//=====================================================================================================
				/// @brief		Gets the manager for this AIControlled object
				/// @remarks	This is always guaranteed not to be null
				//=====================================================================================================
				
				inline AIManager & GetAIManager(){ return * m_manager; }

				//=====================================================================================================
				/// @brief		Sets the quad tree entry for this object. This is the quad tree that is in the AIManager.
				/// @param		entry - entry in the quad tree for this object
				//=====================================================================================================

				inline void SetQuadTreeEntry( QuadTreeEntry * entry ){ m_quad_entry = entry; }

				//=====================================================================================================
				/// @brief		Gets the quad tree node this object belongs to in the AI manager
				/// @returns	The quad tree entry for this object
				//=====================================================================================================

				inline QuadTreeEntry * GetQuadTreeEntry(){ return m_quad_entry; }
				
				//=====================================================================================================
				/// @brief		Evaluates the potential of a given lenard-jones potential function.
				/// @param		p	- point we are evaluating potential to
				/// @param		as	- strength of attraction force
				/// @param		rs	- strength of repulsion force
				/// @param		aa	- atenuation factor of attraction or how much distance affects it. 
				///				Higher values mean distance has a greater effect.
				/// @param		ra	- atenuation factor of repuslion or how much distance affects it.
				///				Higher values mean distance has a greater effect.
				/// @returns	Positive values if we are atracted to the point. Higher values mean more 
				///				attraction. Negative values if we are repulsed by the point. Lower values mean
				///				more repulsion.
				/// @remarks	This function does not unwrap coordinates. It is up to the caller to ensure the 
				///				position has been corrected for wrapping.
				//=====================================================================================================
				
				inline float EvaluatePotential( const Vec2f & p , float as , float rs , float aa , float ra )
				{				
					// Get the distance from us to the given point
					
					float dist = ( p - GetPosition() ).Length();
					
					// Calculate the divisors for the pow(dist/aa) and pow(dist,ra)
					
					float d1 = Float::Pow(dist,aa);
					float d2 = Float::Pow(dist,ra);
					
					// Ensure not zero
					
					d1 += 0.00001f;
					d2 += 0.00001f;
					
					// Evaluate the potential function and return:
					
					return ( as / d1 ) - ( rs / d2 );
				}
				
				//=====================================================================================================
				/// @brief		Evaluates the potential of a given lenard-jones potential function, but only for
				///				attraction. This is used where only attraction forces are needed.
				/// @param		p	- point we are evaluating potential to
				/// @param		as	- strength of attraction force
				/// @param		aa	- atenuation factor of attraction or how much distance affects it. 
				///				Higher values mean distance has a greater effect.
				/// @returns	Positive values if we are atracted to the point. Higher values mean more 
				///				attraction. Negative values if we are repulsed by the point. Lower values mean
				///				more repulsion.
				/// @remarks	This function does not unwrap coordinates. It is up to the caller to ensure the 
				///				position has been corrected for wrapping.
				//=====================================================================================================
				
				inline float EvaluatePotentialAttraction( const Vec2f & p , float as , float aa )
				{				
					// Get the distance from us to the given point
					
					float dist = ( p - GetPosition() ).Length();
					
					// Calculate the divisors for the pow(dist/aa) and pow(dist,ra)
					
					float d = Float::Pow(dist,aa);
					
					// Ensure not zero:
					
					d += 0.00001f;
					
					// Evaluate the potential function and return:
					
					return as / d;
				}				
				
				//=====================================================================================================
				/// @brief		Evaluates the potential of a given lenard-jones potential function. This overload
				///				allows the user to specify the distance manually
				/// @param		dist - distance to the point that we are evaluating forces for
				/// @param		as	- strength of attraction force
				/// @param		rs	- strength of repulsion force
				/// @param		aa	- atenuation factor of attraction or how much distance affects it. 
				///				Higher values mean distance has a greater effect.
				/// @param		ra	- atenuation factor of repuslion or how much distance affects it.
				///				Higher values mean distance has a greater effect.
				/// @returns	Positive values if we are atracted to the point. Higher values mean more 
				///				attraction. Negative values if we are repulsed by the point. Lower values mean
				///				more repulsion.
				/// @pre		Given distance cannot be < 0
				//=====================================================================================================
				
				inline float EvaluatePotential( float dist , float as , float rs , float aa , float ra )
				{				
					// Make sure ok:
					
					ENSURE( dist >= 0 , "Given distance cannot be < 0" )				
					
					// Calculate the divisors for the pow(dist/aa) and pow(dist,ra)
					
					float d1 = Float::Pow(dist,aa);
					float d2 = Float::Pow(dist,ra);
					
					// Ensure not zero
					
					d1 += 0.00001f;
					d2 += 0.00001f;
					
					// Evaluate the potential function and return:
					
					return ( as / d1 ) - ( rs / d2 );
				}
				
				//=====================================================================================================
				/// @brief		Evaluates the potential of a given lenard-jones potential function, but only for the 
				///				repulsion part of the equation. This is used where only repulsion is required, as
				///				an optimisation.
				/// @param		dist - distance to the point that we are evaluating forces for
				/// @param		rs	- strength of repulsion force
				/// @param		ra	- atenuation factor of repuslion or how much distance affects it.
				///				Higher values mean distance has a greater effect.
				/// @returns	Positive values if we are atracted to the point. Higher values mean more 
				///				attraction. Negative values if we are repulsed by the point. Lower values mean
				///				more repulsion.
				/// @pre		Given distance cannot be < 0
				//=====================================================================================================
				
				inline float EvaluatePotentialRepulsion( float dist , float rs , float ra )
				{				
					// Make sure ok:
					
					ENSURE( dist >= 0 , "Given distance cannot be < 0" )
					
					// Calculate the divisors for the pow(dist/aa) and pow(dist,ra)

					float d = Float::Pow(dist,ra);
					
					// Make sure this does not get too small:
					
					d += 0.00001f;
					
					// Evaluate the potential function and return:
					
					return -( rs / d );
				}								
				
		protected:
		
			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				/// Calculates the amount of turning we should do reach a certain point
				
				float CalculateTurningToPoint( const Vec2f & p , float turn_strength , float distance_attenuation );
				
				/// Flocks this enemy to avoid the given point: returns turning amount to do this
				
				FlockAction FlockToAvoid
				( 
					Vec2f avoid_pt					, 
					float view_angle				,
					float separation_view_angle		,
					float flock_distance			,
					float turn_force				, 
					float cohesion_weight			,
					float separation_weight			,
					float alignment_weight			,
					float separation_distance		,
					float separation_attenuation	,
					float maximum_acceleration		,
					float leader_percent_accel		,
					float minimum_percent_accel
				);
				
				/// Swarms this enemy to move towards the given point: returns turning amount to do this
				
				float SwarmToChase
				(
					Vec2f chase_pt					,					
					float turn_force				,
					float swarm_sq_area				, 
					float attraction_force			,
					float repulsion_force			,
					float attraction_attenuation	,
					float repulsion_attenuation		,
					float swarm_influence
				);
				
				/// Makes this enemy avoid obstacles: returns turning amount to do this
				
				void AvoidObstacles
				(
					float scan_radius				,
					float repulsion_strength		,
					float repulsion_attenuation		,
					float max_turning				,
					float time_delta	
				);
				
		private:

			//=========================================================================================================
			// Variables
			//=========================================================================================================

				/// The manager that looks after this AI object
				
				AIManager * m_manager;

				/// The quad tree entry this object belongs to in the AIManager: this is updated by the AIManager

				QuadTreeEntry * m_quad_entry;
	};
};

#endif
