/*
	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		Class representing an obstacle. An obstacle is basically a sphere that has it's points jutted 
///				inwards by random amounts to give the appearance of being a deformed rock or something.. 
///				Again like enemies, this object is not designed to be placed directly in the game. Rather it should
///				be encapsulated in an Obstacle manager which creates and maintains this obstacle.
//=====================================================================================================================

#include "Precompiled.h"
#include "GameObjects\Obstacle.h"

using namespace Game;

//=====================================================================================================================
// Constants
//=====================================================================================================================

/// The color of each obstacle in unsigned format

const unsigned Obstacle::OBSTACLE_COLOR = 0xffffffff;

//=====================================================================================================================
/// @brief		Creates the obstacle with the given parameters
/// @param		num_points - number of points in the obstacle
/// @param		position - position of the obstacle
/// @param		min_radius - minimum radius from the obstacle center that an obstacle point can be at
/// @param		max_radius - maximum radius from the obstacle center that an obstacle point can be at
/// @pre		Number of points must be at least 3
/// @pre		Position given must be valid
/// @pre		Min radius must be > 0
/// @pre		Max radius must be > 0
/// @pre		Max radius must be >= min radius
/// @pre		Game must exist
//=====================================================================================================================

Obstacle::Obstacle( unsigned num_points , const Vec2f & position , float min_radius , float max_radius ) : PositionalObject(position)
{
	// Debug checks

	ENSURE( num_points >= 3				, "Number of points must be at least 3"		);
	ENSURE( min_radius > 0				, "Min radius must be > 0"					);
	ENSURE( max_radius > 0				, "Max radius must be > 0"					);
	ENSURE( max_radius >= min_radius	, "Max radius must be >= min radius"		);
	ENSURE( GameCore::GetSingleton()	, "Game must exist"							);
	
	// This should be null initially
				
	m_quad_tree_entry = 0;
	
	// Get the game
	
	GameCore * game = GameCore::GetSingleton();
	
	// Get the random number generator from the game
	
	Random & random = game->GetRandom();
	
	// Create our array of points:
	
	m_points = new Vec2f[num_points];
	
	// Save the number of points:
	
	m_num_points = num_points;
	
	// Now pick each point: 
	
	for ( unsigned i = 0 ; i < num_points ; i++ )
	{
		// Random float between 0 and 1 
		
		float r = random.NextFloat();
		
		// Calculate the angle of the point along the circle that bounds the obstacle
		
		float a = Float::TwoPI() * ( (float)(i) / (float)(num_points) );
		
		// Calculate the direction of this point from the center of the obstacle
		
		Vec2f d = Vec2f( Float::Cos(a) , Float::Sin(a) );
	
		// Pick a random position between the minimum and maximum radius:
		
		m_points[i] = d * ( ( r * min_radius ) + ( ( 1.0f - r ) * max_radius ) );
	}
	
	// Calculate the maximum radius of this particular obstacle:
	
	m_sq_radius = m_points[0].SqLength();
	
	// Go through each point:
	
	for ( unsigned i = 1 ; i < num_points ; ++i )
	{
		// Get the distance of this point from obstacle center
		
		float r = m_points[i].SqLength();
		
		// See if bigger than current biggest radius:
		
		if ( r > m_sq_radius ) m_sq_radius = r;
	}
	
	// Now get the proper distance of the radius
	
	m_radius = Float::Sqrt(m_sq_radius);
}

//=====================================================================================================================
/// @brief		Destroys this obstacle and frees up ram
/// @pre		The object should not be in a quad tree
//=====================================================================================================================

Obstacle::~Obstacle()
{
	// Make sure ok:
	
	ENSURE( m_quad_tree_entry == 0 , "The object should not be in a quad tree" );

	// Delete our points:
	
	delete[] m_points;
}

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

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

//=====================================================================================================================
/// @brief		Do a buffered render and save the lines in the obstacle to the given stack for later rendering.
/// @param		s - stack to put the point data for the lines in this object
/// @param		cam_pos - current position of the camera
/// @param		z - z value to position the object at
/// @returns	The number of lines that should be drawn
/// @remarks	This function is used in preference to a regular GameObjectBase::OnDraw() function because it allows
///				for batch rendering to be done by the manager of this object
/// @pre		Number of points must be at least 3 
//=====================================================================================================================

unsigned Obstacle::BufferedDraw( Stack & s , const Vec2f & cam_pos , float z ) const
{
	// Make sure ok:
	
	ENSURE( m_num_points >= 3 , "Number of points must be at least 3" );
	
	// Unwrap our position relative to the camera:
	
	Vec2f pos = UnwrapCoordinates( cam_pos , GetPosition() );

	// Current point we are on:
	
	Vec2f * p_current = m_points;
	
	// Position of the current point:
	
	Vec3f p_current_pos = Vec3f( m_points[0] + pos , z );

	// Push every line bar the last one onto the stack
	
	for ( unsigned i = m_num_points - 1; i != 0 ; --i )
	{	
		// Put onto the stack along with it's color
		
		p_current_pos.x = p_current->x + pos.x;
		p_current_pos.y = p_current->y + pos.y;
	
		s.Push( p_current_pos	);
		s.Push( OBSTACLE_COLOR	);
		
		// Next point:
		
		p_current++;
		
		// Put onto the stack along with it's color
	
		p_current_pos.x = p_current->x + pos.x;
		p_current_pos.y = p_current->y + pos.y;
	
		s.Push( p_current_pos	);
		s.Push( OBSTACLE_COLOR	);
	}
	
	// Put the last point onto the stack again
	
	s.Push( p_current_pos	);
	s.Push( OBSTACLE_COLOR	);	
	
	p_current_pos.x = m_points[0].x + pos.x;
	p_current_pos.y = m_points[0].y + pos.y;
	
	// Now put the first onto the stack: thus completing the loop
	
	s.Push( p_current_pos	);
	s.Push( OBSTACLE_COLOR	);
	
	// Return the number of lines we put onto the stack
	
	return m_num_points;
}
