/*
	Trampoline Sim.
	Copyright (c) 2009, 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 GAME_SHAPE_HEIGHTFIELD_H
#define GAME_SHAPE_HEIGHTFIELD_H

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

#include "Physics/Shape.h"

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

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

	class ShapePlane;
	class ShapeBox;

	//=================================================================================================================
	/// @brief		This shape represents a heightfield shape
	//=================================================================================================================
	
	class ShapeHeightfield : public Shape
	{
		public:
		
			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				//=====================================================================================================
				/// @brief		Creates the heightfield shape
				/// @param		squares		- number of squares in the heighfield in x and z direcitons
				/// @param		xz_scale	- xz scale of the heightfield
				/// @param		position	- position the shape is initially in
				/// @param		squares must be >= 1
				/// @remarks	All heightfield vertices will be at their default settings after this is finished
				//=====================================================================================================
				
				ShapeHeightfield
				( 
					unsigned		squares	 = 64				,
					const Vec2f	&	xz_scale = Vec2f::One()		, 
					const Vec3f	&	position = Vec3f::Zero()	
				)
				: Shape(position,Quaternionf::Identity(),SHAPE_HEIGHTFIELD)
				{
					// Make sure this is ok:
					
					ENSURE( squares >= 1 , "squares must be >= 1" )
				
					// Save the square count
					
					m_num_squares = squares; m_num_squares_plus_1 = squares + 1;
					
					// Save these cached variables
					
					m_num_squares_f	= float(m_num_squares); m_num_squares_f_inv = 1.0f / float(m_num_squares);
					
					// Make vertex array
					
					m_vertices = new Vec3f[ ( m_num_squares + 1 ) * ( m_num_squares + 1 ) ];

					// Make the velocities array

					m_velocities = new float[ ( m_num_squares + 1 ) * ( m_num_squares + 1 ) ];
					
					// Fill in the vertex array:
					
					for ( unsigned r = 0 ; r <= m_num_squares ; r++ )
					{
						for ( unsigned c = 0 ; c <= m_num_squares ; c++ )
						{
							// Make up the position:
							
							Vec3f pos = Vec3f
							(
								( ( float(c) * m_num_squares_f_inv ) - 0.5f ) * 2.0f	,
								0.0f													,
								( ( float(r) * m_num_squares_f_inv ) - 0.5f ) * 2.0f
							);
							
							// Save:
						
							m_vertices[ ( r * m_num_squares_plus_1 ) + c ] = pos;

							// Set initial velocity to zero

							m_velocities[ ( r * m_num_squares_plus_1 ) + c ] = 0;
						}
					}
				
					// Set scale:
				
					SetXZScale(xz_scale);
				}
				
				//=====================================================================================================
				/// @brief		Destructor.
				//=====================================================================================================
				
				virtual ~ShapeHeightfield()
				{
					// Delete the vertices and speeds:
					
					delete[] m_vertices;
					delete[] m_velocities;
				}
				
				//=====================================================================================================
				/// @brief		Returns the number of squares in the heightfield
				/// @returns	Number of squares in the heightfield
				//=====================================================================================================
				
				inline unsigned GetNumSquares() const { return m_num_squares; }
				
				//=====================================================================================================
				/// @brief		Sets the scale of the heightfield
				/// @param		xz_scale - size of the heightfield
				/// @pre		Scale must be valid
				/// @pre		Scale must be > 0 on all axes
				//=====================================================================================================

				inline void SetXZScale( const Vec2f & xz_scale )
				{
					// Make sure ok

					ENSURE( xz_scale.x > 0 , "Scale must be > 0 on all axes" )
					ENSURE( xz_scale.y > 0 , "Scale must be > 0 on all axes" )

					ENSURE( Float::IsValid(xz_scale.x) , "Scale must be valid" )
					ENSURE( Float::IsValid(xz_scale.y) , "Scale must be valid" )

					m_xz_scale = xz_scale;
					
					// Calculate the inverse scale:
					
					m_xz_scale_inv.x = 1.0f / m_xz_scale.x;
					m_xz_scale_inv.y = 1.0f / m_xz_scale.y;
				}

				//=====================================================================================================
				/// @brief		Gets the xz scale of the heightfield
				/// @returns	XZ scale of the heightfield
				//=====================================================================================================

				inline const Vec2f & GetXZScale() const { return m_xz_scale; }
				
				//=====================================================================================================
				/// @brief		Generates a list of collision contacts with the other shape
				/// @param		other - other shape to generate a list of for 
				/// @param		contacts - where to save the resulting contacts
				//=====================================================================================================

				virtual void GenerateContacts( const Shape & other , std::vector<Contact> & contacts ) const;

				//=====================================================================================================
				/// @brief		Raycasts against this shape and returns the result
				/// @param		ray - ray to cast against the shape
				/// @returns	Result of the raycast
				//=====================================================================================================

				virtual RaycastResult Raycast( Ray & ray ) const;
				
				//=====================================================================================================
				/// @brief		Returns the height of one point in the heightfield
				/// @param		r - which row to get height of point from
				/// @param		c - which column to get height of point from
				/// @returns	Height of the specified point in the heightfield
				/// @remarks	Number of rows and columns is the number of squares in the heighfield plus 1
				/// @pre		Given indexes must be in range
				//=====================================================================================================
				
				inline float GetHeight( unsigned r , unsigned c ) const
				{
					// Make sure ok
					
					ENSURE( r <= m_num_squares , "Given indexes must be in range" )
					ENSURE( c <= m_num_squares , "Given indexes must be in range" )
					
					// Return:
					
					return m_vertices[ r * m_num_squares_plus_1 + c ].y;
				}
				
				//=====================================================================================================
				/// @brief		Sets the height of one point in the heightfield
				/// @param		r - which row to set height for
				/// @param		c - which column to set height for
				/// @param		height - height of the point
				/// @remarks	Number of rows and columns is the number of squares in the heighfield plus 1
				/// @pre		Given indexes must be in range
				//=====================================================================================================
				
				inline void SetHeight( unsigned r , unsigned c , float height )
				{
					// Make sure ok
					
					ENSURE( r <= m_num_squares , "Given indexes must be in range" )
					ENSURE( c <= m_num_squares , "Given indexes must be in range" )
					
					// Set:
					
					m_vertices[ r * m_num_squares_plus_1 + c ].y = height;
				}
				
				//=====================================================================================================
				/// @brief		Samples the height at a particular point in the heightfield
				/// @param		xz_position - xz position to sample at
				/// @returns	Height at given position
				/// @remarks	This function will clamp to the edges of the heightfield if the xz position is out of range
				/// @remarks	This function does not support rotated heightfields. The rotation must be at the identity rotation.
				//=====================================================================================================
				
				float SampleHeight( const Vec2f & xz_position ) const;
				
				//=====================================================================================================
				/// @brief		Samples the normal at a particular point in the heightfield
				/// @param		xz_position - xz position to sample at
				/// @returns	Normal at given position
				/// @remarks	This function will clamp to the edges of the heightfield if the xz position is out of range
				/// @remarks	The normal returned will be normalized
				/// @remarks	This function does not support rotated heightfields. The rotation must be at the identity rotation.
				//=====================================================================================================
				
				Vec3f SampleNormal( const Vec2f & xz_position ) const;

				//=====================================================================================================
				/// @brief		Samples the velocity at a particular point in the heightfield.
				/// @param		xz_position - xz position to sample velocity at.
				/// @returns	Velocity at a given position in the heightfield.
				//=====================================================================================================

				float SampleVelocity( const Vec2f & xz_position ) const;

				//=====================================================================================================
				/// @brief		Returns the vertex array for this heightfield
				/// @returns	Heightfield vertex array
				//=====================================================================================================
				
				inline Vec3f * GetVertices(){ return m_vertices; }

				//=====================================================================================================
				/// @brief		Returns the velocity array for this heightfield. This array holds the velocity of each
				///				vertex in the heightfield.
				/// @returns	Heightfield velocity array/
				//=====================================================================================================
				
				inline float * GetVelocities(){ return m_velocities; }

		private:

			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				/// Copying is disallowed
				
				ShapeHeightfield( const ShapeHeightfield & other );

				/// Copying is disallowed
				
				ShapeHeightfield & operator = ( const ShapeHeightfield & other );
				
				/// Generates contacts for this shape with a box
				
				void BoxContacts( const ShapeBox & other , std::vector<Contact> & contacts ) const;
		
			//=========================================================================================================
			// Variables
			//=========================================================================================================
			
				/// This is the xz scale of the heightfield

				Vec2f m_xz_scale;
				
				/// This is the inverse xz scale of the heightfield
				
				Vec2f m_xz_scale_inv;
				
				/// This is the number of squares in the x and z directions
				
				unsigned m_num_squares;
				
				/// This is the number of squares in the x and z directions plus 1
				
				unsigned m_num_squares_plus_1;
				
				/// This is the inverse of number of squares in floating point format
				
				float m_num_squares_f_inv;
				
				/// This is the number of squares in floating point format
				
				float m_num_squares_f;
				
				/// This is the array of vertices for the heightfield
				
				Vec3f * m_vertices;

				/// This is the array of velocities (y) for each vertex in the heightfield

				float * m_velocities;
	};
}

#endif
