/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NPHYSICWORLD__
#define	__NPHYSICWORLD__


		#include "physic/physic_world_interface.h"
		#include "physic/physic_message.h"
		#include "math/vector.h"
		#include "timing/benchmark.h"
		#include "container/nlist.h"
		#include "nstring/nstring.h"


namespace GS {

	namespace Core { class Camera; }

	namespace Render {
		class Renderer;
		class RasterFont;
	}
	namespace S3D {
		class PhysicItem;
		class PhysicConstraint;
		struct PhysicShape;

		using Core::Camera;
		using Render::Renderer;
		using Render::RasterFont;

/// Raytrace hit result structure.
struct	PhysicTrace
{
		Vector4			n, p;		///< Intersection normal and point in world space.
		String			m;			///< Material name at intersection point.
		PhysicShape		*s;
		PhysicItem		*i;

		PhysicTrace() : m(0), s(0), i(0) {}
};

/// Collision pair.
struct	CollisionPair
{
		PhysicItem		*a, *b;
		uint			contact_count;
		Vector4			contact[4], normal[4];

		CollisionPair() : a(0), b(0), contact_count(0) {}
};

/*
	@short	Abstract physic world.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
class	PhysicWorld
{
protected:

		float			timestep,
						substep_dt;
		Vector4			gravity;

		IPhysicWorld	*world_interface;

public:

		Benchmark	bench_step;

virtual	PhysicItem		*NewItem() = 0;
virtual	PhysicConstraint *NewConstraint() = 0;

		/// Clear convex/mesh cache.
virtual	void			ClearConvexMeshCache() = 0;

virtual	bool			HasDebugger() const { return false; }
virtual	void			CreateDebugger(Renderer * = 0) {}

		/// Draw debug informations (native library display).
virtual	void			DrawDebug(Renderer &, Camera *, RasterFont *, bool xray_first_pass) {}

		void			SetWorldInterface(IPhysicWorld *i) { world_interface = i; }
		IPhysicWorld	*GetWorldInterface() const { return world_interface; }

		void			SetTimestep(float t = 1.f / 60.f) { timestep = t; }
		float			GetTimestep() const { return timestep; }

		void			SetGravity(const Vector4 &g) { gravity = g; }
const	Vector4			&GetGravity() const { return gravity; }

virtual	bool			Create() = 0;
virtual	void			Delete() = 0;

		/// Raytrace world.
virtual	bool			Raytrace(const Vector4 &s, const Vector4 &d, PhysicTrace &hit, int collision_mask = ~0, int shape_mask = ~0, float max_distance = -1.f) = 0;

		/// Return the number of collision pair in the previous step.
virtual	uint			GetCollisionPairCount() = 0;
		/// Return the number of collision pair in the previous step.
virtual	bool			GetCollisionPair(uint n, CollisionPair &) = 0;

virtual	void			Step(const Time &dt) = 0;

/*!
	@name	Low-level message broadcasting system.
	@{
*/
protected:

		/// Physics message listener.
		List <nPhysicListener *> listener_list;

public:

		bool			RegisterMessageListener(nPhysicListener *listener) { return asbool(listener_list.Add(listener)); }
		bool			UnregisterMessageListener(nPhysicListener *listener) { return listener_list.Remove(listener); }
		void			BroadcastMessage(nPhysicMessage msg, void *parm = 0)
		{
			ListForeachPtr(nPhysicListener *, listener, listener_list)
				listener->ProcessMessage(msg, parm);
		}
/// @}

		PhysicWorld();
virtual	~PhysicWorld() {}
};

	} // S3D
} // GS


#endif	// __NPHYSICWORLD__
