/*
	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.
*/

//=====================================================================================================================
/// @brief		This type of manager creates and spawns all of the wasps. Wasps are usually spawned by factories
///				but every so often this class will spawn a few just to keep their numbers up.
/// @remarks	This class also provides the functionality used by factories to spawn enemies
//=====================================================================================================================

#include "Precompiled.h"
#include "GameObjects\EnemyWaspManager.h"
#include "GameObjects\EnemyWasp.h"
#include "GameObjects\EnemyBullet.h"
#include "Game\GameCore.h"
#include "Game\QuadTree.h"
#include "Game\QuadTreeAlgo.h"
#include "Game\EnemyBulletManager.h"
#include "Graphics\Camera2D.h"
#include "Graphics\PrimitivesRenderer.h"

using namespace Game;

//=====================================================================================================================
// Statics
//=====================================================================================================================

#ifdef USE_DEBUG_CONSOLE

	/// Debug flag: if true we draw the quad tree

	bool EnemyWaspManager::s_quad_tree_render = false;

	/// Debug flag: if true we draw the quad tree search test

	bool EnemyWaspManager::s_quad_tree_search_render = false;
	
	/// Debug flag: if true we draw wander goal points for each enemy
	
	bool EnemyWaspManager::s_show_wander_points = false;	

#endif

//=====================================================================================================================
/// @brief		Creates this manager
/// @param		max_enemies	- maximum number of enemies to have
/// @param		initial_enemies - initial number of enemies to spawn
/// @param		spawn_interval_min - minimum time in between spawns
/// @param		spawn_interval_max - maximum time in between spawns
/// @pre		Maximum enemies must be at least 1
/// @pre		Initial enemies cannot be greater than max enemies
/// @pre		Min spawn interval must be greater than zero
/// @pre		Max spawn interval must be greater than zero
/// @pre		The game must exist
//=====================================================================================================================

EnemyWaspManager::EnemyWaspManager
( 
	unsigned max_enemies		, 
	unsigned initial_enemies	, 
	float spawn_interval_min	, 
	float spawn_interval_max
)
{
	// Assertions:
	
	ENSURE( max_enemies >= 1				, "Maximum enemies must be at least 1"					);
	ENSURE( initial_enemies <= max_enemies	, "Initial enemies cannot be greater than max enemies"	);
	ENSURE( spawn_interval_min > 0			, "Min spawn interval must be greater than zero"		);
	ENSURE( spawn_interval_max > 0			, "Max spawn interval must be greater than zero"		);
	ENSURE( GameCore::GetSingleton()		, "The game must exist"									);

	// Init settings

	m_max_enemies			= max_enemies;
	m_initial_enemies		= initial_enemies;
	m_spawn_interval_min	= spawn_interval_min;
	m_spawn_interval_max	= spawn_interval_max;
	m_do_initial_spawning	= false;
	
	// Pick a time till the next spawn:
	
	m_time_till_spawn = GameCore::GetSingleton()->GetRandom().NextFloat(spawn_interval_min,spawn_interval_max);
	
	// Add console commands

	#ifdef USE_DEBUG_CONSOLE

		DebugConsole::AddCommand("G_WaspQuadTreeRender"			, ConCmd_G_WaspQuadTreeRender		);
		DebugConsole::AddCommand("G_WaspQuadTreeSearchRender"	, ConCmd_G_WaspQuadTreeSearchRender	);
		DebugConsole::AddCommand("G_WaspShowWanderPoints"		, ConCmd_G_WaspShowWanderPoints		);

	#endif
}

//=====================================================================================================================
/// @brief		Destructor. Destroys the object.
//=====================================================================================================================

EnemyWaspManager::~EnemyWaspManager()
{
	// Remove console commands

	#ifdef USE_DEBUG_CONSOLE

		DebugConsole::RemoveCommand("G_WaspQuadTreeRender"			);
		DebugConsole::RemoveCommand("G_WaspQuadTreeSearchRender"	);
		DebugConsole::RemoveCommand("G_WaspShowWanderPoints"		);

	#endif
}

//=====================================================================================================================
/// @brief		Called when the manager is added the game. Creates the bullet manager so enemies of this type can fire
/// @pre		The game must exist
//=====================================================================================================================

void EnemyWaspManager::OnGameAdded()
{
	// Make sure ok:
	
	ENSURE( GameCore::GetSingleton() , "The game must exist" )

	// Do the base class function first:
	
	AIManager::OnGameAdded();
	
	// Get the stack from the game:

	Stack & stack = GameCore::GetSingleton()->GetStack();

	// Populate it with bullets for this type of object:

	for ( unsigned i = MAX_BULLETS ; i != 0 ; --i )
	{
		stack.Push( new EnemyBullet(10,300) );
	}

	// Now make the bullet manager:

	m_bullet_manager = new EnemyBulletManager(this,MAX_BULLETS,stack,Color(1.0f,0.3f,0.0f),10);
}

//=====================================================================================================================
/// @brief		Gets the bounding circle radius for the types of enemy ships this manager looks after
/// @returns	The bounding circle radius for these types of ships
//=====================================================================================================================

float EnemyWaspManager::GetShipBoundingCircleRadius()
{
	return Float::Max( EnemyWasp::SIZE.x , EnemyWasp::SIZE.y );
}

//=====================================================================================================================
/// @brief		This virtual function creates the objects used by this manager
/// @param		pooled_objects - vector to save all the pooled objects to
//=====================================================================================================================

void EnemyWaspManager::CreatePooledObjects( std::vector<AIControlled*> & pooled_objects )
{
	// Allocate enough room for all our objects:
	
	pooled_objects.resize( m_max_enemies );
	
	// Make each enemy:
	
	for ( unsigned i = 0 ; i < m_max_enemies ; ++i )
	{
		pooled_objects[i] = new EnemyWasp(this);	
	}
	
	// Do initial spawning:
	
	m_do_initial_spawning = true;	
}

//=====================================================================================================================
/// @brief		This virtual function destroys the object pool for this type of manager:
/// @param		pooled_objects - vector to save all the pooled objects to
//=====================================================================================================================

void EnemyWaspManager::DestroyPooledObjects( std::vector<AIControlled*> & pooled_objects )
{
	// Remove reference to them all
	
	for ( unsigned i = 0 ; i < pooled_objects.size() ; ++i )
	{
		pooled_objects[i]->RemoveReference(); 
	}
	
	// Clear the vector:
	
	pooled_objects.resize(0);
}

//=====================================================================================================================
/// @brief		This virtual function is called by the base class once per frame before objects are updated.
///				Allows for object spawning.
/// @param		time_delta - amount of time in between frames
/// @pre		Game must be initialized
//=====================================================================================================================

void EnemyWaspManager::OnAIManagerUpdate( float time_delta )
{
	// Assertions
	
	ENSURE( GameCore::GetSingleton() , "Game must be initialized" );

	// Decrement time to spawn:
	
	m_time_till_spawn -= time_delta;
	
	// Get the game player: only spawn if the player is there:
	
	PlayerShip * player = GetPlayerShip();
	
	// Do initial spawn if specified:
	
	if ( m_do_initial_spawning && player )
	{
		// Do spawning
		
		for ( unsigned i = 0 ; i < m_initial_enemies ; ++i )
		{
			// If the pool is full then don't spawn:
			
			if ( GetAIObjectPool()->GetUsedCount() >= GetAIObjectPool()->GetSize() ) break;
		
			// Alloc an enemy:
			
			EnemyWasp * o = (EnemyWasp*) AllocatePooledObject();			
			
			// Set enemy position		
			
			o->SetPosition
			( 
				PickSpawnLocation
				(
					200.0f , 
					Float::Max(EnemyWasp::SIZE.x,EnemyWasp::SIZE.y) * 2.0f
				)
			);
			
			// Reset all other states
			
			o->ResetState();
			
			// Need to put into the quad tree			
			
			InsertToQuadTree(o);
		}
	
		// Done initial spawning:
		
		m_do_initial_spawning = false;
	}
	
	// See if time to spawn:
	
	if ( m_time_till_spawn <= 0 && player )
	{
		// Get the random number generator used by the game
	
		Random & random = GameCore::GetSingleton()->GetRandom();
	
		// Pick new time till spawn:
		
		m_time_till_spawn = random.NextFloat( m_spawn_interval_min , m_spawn_interval_max );
		
		// Only do if we can spawn:
		
		if ( GetUsedObjectCount() < GetMaxObjectCount() )
		{
			// Alloc an enemy:
			
			EnemyWasp * o = (EnemyWasp*) AllocatePooledObject();
			
			// Make sure ok:
			
			ENSURE( o->GetQuadTreeEntry() == 0 , "Each wasp should not be in the quad tree before spawning" );			
		
			// Set enemy position		
			
			o->SetPosition
			( 
				PickSpawnLocation
				(
					200.0f , 
					Float::Max(EnemyWasp::SIZE.x,EnemyWasp::SIZE.y) * 2.0f
				)
			);
			
			// Reset all other states
			
			o->ResetState();
			
			// Need to put into the quad tree
			
			InsertToQuadTree(o);
		}		
	}	
}

//=====================================================================================================================
/// @brief	This virtual function is called by the base class before all objects are drawn in the manager: allows for 
///			setting of rendering parameters.
/// @param	c - camera the scene is being drawn from
/// @pre	Game should exist
/// @pre	Game should have a primitives renderer
/// @pre	Given camera cannot be null
//=====================================================================================================================

// Use this to get rid of unused param warnings when running without the console

#ifndef USE_DEBUG_CONSOLE

	void EnemyWaspManager::OnAIManagerDraw( Camera2D * )

#else

	void EnemyWaspManager::OnAIManagerDraw( Camera2D * c )

#endif

{
	// Make sure ok:
	
	ENSURE( GameCore::GetSingleton()							, "Game should exist"						);
	ENSURE( GameCore::GetSingleton()->GetPrimitivesRenderer()	, "Game should have a primitives renderer"	);
	ENSURE( c													, "Given camera cannot be null"				);

	// If using the console draw the quad tree if specified:
	
	#ifdef USE_DEBUG_CONSOLE
	
		// See if we are to draw it:
		
		if ( s_quad_tree_render )
		{
			GetQuadTree()->Render( c , GameCore::GetSingleton()->GetPrimitivesRenderer() );
		}
		
		// Draw the search if specified:
		
		if ( s_quad_tree_search_render )
		{
			// Get the position of the camera:
			
			Vec2f p = GameCore::GetSingleton()->GetCamera()->GetPosition();
			
			// Draw the search results:
		
			QuadTreeAlgo::DrawFindInRectangleResults
			(
				GetQuadTree()										,
				GameCore::GetSingleton()->GetStack()				,
				p - Vec2f(150,150)									,
				Vec2f(300,300)										,
				c													,
				GameCore::GetSingleton()->GetPrimitivesRenderer()
			);	
		}
		
		// Show wander points if enabled:
		
		if ( s_show_wander_points )
		{
			// Get the pool of AI objects:
			
			Pool<AIControlled> * pool = GetAIObjectPool();
			
			// Get primitives renderer:
			
			PrimitivesRenderer * r = GameCore::GetSingleton()->GetPrimitivesRenderer();
			
			// Run through all the objects in use:
			
			Pool<AIControlled>::Enumerator i = pool->UsedObjects();
			Pool<AIControlled>::Enumerator e = pool->ListEnd();
			
			// Run through them:
			
			while ( i != e )
			{
				// Get this object:
				
				EnemyWasp * object = dynamic_cast<EnemyWasp*>(*i); ++i;
				
				// Draw its wander goal point if wandering:
				
				if ( object->GetGoal() == EnemyWasp::GOAL_WANDER )
				{				
					r->DrawLine
					( 
						c , 
						PositionalObject::UnwrapCoordinates
						( 
							PositionalObject::WrapCoordinates(c->GetPosition()), 
							PositionalObject::WrapCoordinates(object->GetPosition())		
						), 
						PositionalObject::UnwrapCoordinates
						( 
							PositionalObject::WrapCoordinates(c->GetPosition()), 
							PositionalObject::WrapCoordinates(object->GetWanderPoint())		
						), 
						0xffffffff 
					);
				}
			}
		}
	
	#endif	// #ifdef USE_DEBUG_CONSOLE
}

//=====================================================================================================================
/// @brief		Allows derived managers to update one AI object:
/// @param		object - object to update
/// @param		time_delta - amount of time in between frames
//=====================================================================================================================

void EnemyWaspManager::UpdateAIObject( AIControlled * object , float time_delta )
{
	object->OnUpdate(time_delta);
}

//=====================================================================================================================
/// @brief		Allows derived managers to draw one AI object:
/// @param		object - object to draw
/// @param		c - camera to view from
//=====================================================================================================================

void EnemyWaspManager::DrawAIObject( AIControlled * object , Camera2D * c )
{
	object->OnDraw(c);
}

//=====================================================================================================================
/// @brief		Annouces the type name for this object.
/// @returns	Type name of this object
//=====================================================================================================================

const char * EnemyWaspManager::GetTypeName() const
{
	return "EnemyWaspManager";
}

#ifdef USE_DEBUG_CONSOLE

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables drawing of the quad tree
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool EnemyWaspManager::ConCmd_G_WaspQuadTreeRender( bool arg , bool arg_given )
{
	if ( arg_given ) s_quad_tree_render = arg; return s_quad_tree_render;
}

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables drawing of the quad tree search test
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool EnemyWaspManager::ConCmd_G_WaspQuadTreeSearchRender( bool arg , bool arg_given )
{
	if ( arg_given ) s_quad_tree_search_render = arg; return s_quad_tree_search_render;
}

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables drawing of wander point goals
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool EnemyWaspManager::ConCmd_G_WaspShowWanderPoints( bool arg , bool arg_given )
{
	if ( arg_given ) s_show_wander_points = arg; return s_show_wander_points;
}

#endif	// #ifdef USE_DEBUG_CONSOLE
