/*
	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 class manages a set of bullets. It draws each of the bullets, and allows bullets to fire- 
///				as well as managing their movement.
/// @remarks	Unlike other managers (AI managers and such) this class is not a game object. The reason for this
///				is because it is designed to be used by either a game object (such as player ship) or by an
///				object manager that is placed in the game (such as EnemyWaspManager)
//=====================================================================================================================

#include "Precompiled.h"
#include "Game\BulletManager.h"
#include "GameObjects\BulletBase.h"
#include "Graphics\BufferedLineRenderer.h"
#include "Graphics\Camera2D.h"

using namespace Game;

//=====================================================================================================================
/// @brief		Creates the bullet manager
/// @param		bullet_count - number of bullets to make
/// @param		stack - Stack that holds a pointer for each bullet. This function expects the list of bullet pointers
///				to be at the very top of the stack.
/// @param		bullet_color - color of each bullet when rendering
/// @param		bullet_length - how long the bullets should be when drawing
/// @pre		Bullet count must be 1 or more
/// @remarks	The manager takes ownership of bullet memory after this function is called, and is reponsible for 
///				deleting the created bullets.
//=====================================================================================================================

BulletManager::BulletManager( unsigned bullet_count , Stack & stack , Color bullet_color , float bullet_length )
{
	// Debug checks:
	
	ENSURE( bullet_count > 0 , "Bullet count must be 1 or more"	);
	
	// Save the number of bullets
	
	m_bullet_count = bullet_count;	
	
	// Make an array to hold all of our bullet pointers:
	
	m_bullets = new BulletBase * [ bullet_count ];
	
	// Fill the array:
	
	for ( unsigned i = 0 ; i < bullet_count ; ++i )
	{
		// Get this bullet off the stack
		
		BulletBase * bullet = stack.Pop<BulletBase*>();
		
		// Add into the array:
		
		m_bullets[i] = bullet;
		
		// Set the bullets manager to this
		
		bullet->SetManager(this);
	}
	
	// Go back to the previous stack location:
	
	stack.Offset( sizeof(void*) * bullet_count );
	
	// Save bullet color and length
	
	m_bullet_color	= bullet_color;
	m_bullet_length	= bullet_length;
	
	// Create the bullet pool:
	
	m_bullet_pool = new Pool<BulletBase>( stack , bullet_count );
}

//=====================================================================================================================
/// @brief		Destructor. Cleans up the object.
//=====================================================================================================================

BulletManager::~BulletManager()
{
	// Get rid of the bullet pool:
	
	m_bullet_pool->RemoveReference();
	
	// Free each bullet:
	
	for ( unsigned i = 0 ; i < m_bullet_count ; ++i )
	{
		m_bullets[i]->SetManager(0);
		m_bullets[i]->RemoveReference();
	}
	
	// Get rid of the array of bullet pointers
	
	delete[] m_bullets;
}

//=====================================================================================================================
/// @brief		Updates all bullets in the manager
/// @param		time_delta - amount of time between this frame and the last one
/// @pre		Time delta must be at least 0
/// @pre		All bullets in the used list must not be marked as dead
/// @pre		Game must exist
/// @pre		Game must have a camera
//=====================================================================================================================

void BulletManager::UpdateAll( float time_delta )
{
	// Debug check:

	ENSURE( time_delta >= 0								, "Time delta must be at least 0"	);
	ENSURE( GameCore::GetSingleton() != 0				, "Game must exist"					);
	ENSURE( GameCore::GetSingleton()->GetCamera() != 0	, "Game must have a camera"			);
	
	// Get the camera used by the game
	
	Camera2D * camera = GameCore::GetSingleton()->GetCamera();
	
	// Get the camera's position:
	
	Vec2f cam_pos = camera->GetPosition();
	
	// Get the view area of the camera and get half extents:
	
	Vec2f view_area = camera->GetViewArea() * 0.5f;
	
	// Calculate the bottom left and top right points of it and expand a little so bullets can go somewhat offscreen
	
	Vec2f view_bl = camera->GetPosition() - view_area;
	Vec2f view_tr = camera->GetPosition() + view_area;
	
	view_bl -= m_bullet_length * 2.0f;
	view_tr += m_bullet_length * 2.0f;
	
	// Run through each used bullet:
	
	Pool<BulletBase>::Enumerator i = m_bullet_pool->UsedObjects();
	Pool<BulletBase>::Enumerator e = m_bullet_pool->ListEnd();
	
	// Do it:
	
	while ( i != e )
	{
		// Get this bullet and move on:
		
		BulletBase * b = *i; ++i;
		
		// This should never happen
		
		ENSURE( b->IsDead() == false , "All bullets in the used list must not be marked as dead" );
		
		// Update this bullet 
		
		b->BulletUpdate(time_delta,cam_pos,view_bl,view_tr);
		
		// See if dead now: if so then free
		
		if ( b->IsDead() ) m_bullet_pool->Dealloc(b);
	}
}

//=====================================================================================================================
/// @brief		Draws all bullets in the manager
/// @param		camera - camera to draw from
/// @pre		Given camera cannot be null
/// @pre		The game must exist
/// @pre		All bullets in the used list must not be marked as dead
/// @pre		Game must have a buffered line renderer
//=====================================================================================================================

void BulletManager::DrawAll( Camera2D * camera )
{
	// Debug check:
	
	ENSURE( camera != 0													, "Given camera cannot be null"					);
	ENSURE( GameCore::GetSingleton() != 0								, "The game must exist"							);
	ENSURE( GameCore::GetSingleton()->GetBufferedLineRenderer() != 0	, "Game must have a buffered line renderer"		);
	
	// If there are no bullets active then do nothing:
	
	if ( m_bullet_pool->GetUsedCount() <= 0 ) return;
	
	// Get the game:
	
	GameCore * game = GameCore::GetSingleton();
	
	// Get the stack from the game:
	
	Stack & stack = game->GetStack();
	
	// Run through each used bullet and put a line for it onto the stack:
	
	Pool<BulletBase>::Enumerator i = m_bullet_pool->UsedObjects();
	Pool<BulletBase>::Enumerator e = m_bullet_pool->ListEnd();
	
	// Convert the bullet color to a directx unsigned:
	
	unsigned color = m_bullet_color.ToDxUnsigned();
	
	// Calculate the depth that we will draw bullets at
	
	float bullet_depth = -( camera->GetFarDist() + camera->GetNearDist() ) * 0.5f;
	
	// Get the position of the camera:
	
	Vec2f cam_pos = camera->GetPosition();
	
	// Run through the list:
	
	while ( i != e )
	{
		// Get this bullet and move on:
		
		BulletBase * b = *i; ++i;
		
		// This should never happen
		
		ENSURE( b->IsDead() == false , "All bullets in the used list must not be marked as dead" );
		
		// Get it's position and unwrap:
		
		Vec3f pos = Vec3f( PositionalObject::UnwrapCoordinates( cam_pos , b->GetPosition() ) , bullet_depth );
		
		// Put first line point onto the stack
		
		stack.Push( pos		);
		stack.Push( color	);
		
		// Move onto second line point
		
		pos -= Vec3f( Float::Cos(b->GetAngle()) , Float::Sin(b->GetAngle()) , 0 ) * m_bullet_length;
		
		// Put second line point on the stack
		
		stack.Push( pos		);
		stack.Push( color	);
	}
	
	// Get the buffered line renderer
	
	BufferedLineRenderer * r = game->GetBufferedLineRenderer();
	
	// Now draw all of the bullets:
	
	r->Draw( camera->GetViewProjectionMatrix() , m_bullet_pool->GetUsedCount() , stack );
}

//=====================================================================================================================
/// @brief		Fires one bullet: if there are no free bullets then the oldest allocated bullet is scrapped and reused
/// @param		pos - position to fire the bullet at
/// @param		angle - angle the bullet should travel at
/// @param		base_speed - base speed of the firer. this is added onto the bullet's speed
//=====================================================================================================================

void BulletManager::FireBullet( const Vec2f & pos , float angle , float base_speed )
{
	// Allocate a bullet:
	
	BulletBase * bullet = m_bullet_pool->Alloc();
	
	// Fire the bullet:
	
	bullet->Fire(pos,angle,base_speed);
}
