/////////////////////////////////////////////////////////////
// CINEMA 4D SDK                                           //
/////////////////////////////////////////////////////////////
// (c) MAXON Computer GmbH, all rights reserved            //
/////////////////////////////////////////////////////////////

#ifndef _LIB_COLIDERCACHE_H_
#define _LIB_COLIDERCACHE_H_

#include "c4d_library.h"

#include "ge_math.h"

// API Return Values

#define COL_OK													 0
#define COL_ERR_OUT_OF_MEMORY						-1
#define COL_ERR_UNPROCESSED_MODEL				-2
#define COL_ERR_WRONG_BUILD_ORDER				-3
#define COL_ERR_EMPTY_OBJECT						-4

// Flags for DoCollide

#define COL_ALL_CONTACTS		1
#define COL_FIRST_CONTACT		2


// Collision Cache

#ifndef _INTERNAL_DEF_COLLIDER_

class GeColliderCache
{
	private:
		GeColliderCache();
		~GeColliderCache();

	public:

		static GeColliderCache* Alloc();
		static void Free(GeColliderCache *&data);

		Bool CopyTo(GeColliderCache *dest);

		LONG BeginInput(LONG cnt_tris);
		LONG AddTriangle(const Vector& p1, const Vector& p2, const Vector& p3, LONG id);
		LONG EndInput();
};


// The Collision Engine

class GeColliderEngine
{
	private:
		GeColliderEngine();
		~GeColliderEngine();

	public:

		static GeColliderEngine* Alloc();
		static void Free(GeColliderEngine *&data);

		LONG DoCollide(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, LONG flag);
		LONG DoPolyPairs(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real tolerance);
		LONG DoTolerance(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real tolerance);
		LONG DoDistance(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real rel_err, Real abs_err);

		void FreePairsList();


	// Results for Collison Query

		LONG GetNumPairs();
		LONG GetId1(LONG k);
		LONG GetId2(LONG k);


	// Results for Tolerance Query

		Bool IsCloser();


	// Results for Distance & Tolerance Query

		Real GetDistance();
		const Vector& GetP1();
		const Vector& GetP2();


	// RayCollider API
	
		LONG DoRayCollide(GeColliderCache *o1, const Vector &ray_p, const Vector &ray_dir, Real ray_length);
};

struct GeRayColResult
{
    LONG		face_id;
    LONG		tri_face_id;	// if > 0 then face_id + 1 else - (face_id + 1)
    
    Vector	hitpos;
    Real		distance;
    Vector	s_normal;			// phong normal
    Vector	f_normal;			// face normal
    Vector	barrycoords;	// x = u, y = v, z = d;
    Bool		backface;
};

class GeRayCollider
{
	private:
		GeRayCollider();
		~GeRayCollider();

	public:

		static GeRayCollider* Alloc();
		static void Free(GeRayCollider *&data);

		Bool Init(BaseObject *goal, Bool force = FALSE); // force == TRUE overwrites the (not) dirty flag

		Bool Intersect(const Vector &ray_p, const Vector &ray_dir, Real ray_length, Bool only_test = FALSE);
		
		LONG GetIntersectionCount();
		Bool GetIntersection(LONG number, GeRayColResult *res);
		Bool GetNearestIntersection(GeRayColResult *res);	
};

#else

#define GeColliderCache GeColliderCacheI
#define GeColliderEngine GeColliderEngineI
#define GeRayCollider GeRayColliderI

#endif

// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF


#define LIB_COLLIDERCACHE 1000487

struct ColliderCacheLib : public C4DLibrary
{
		GeColliderCache*		(*Alloc														)();
		void								(*Free														)(GeColliderCache *&data);
		Bool								(*CopyTo					)(GeColliderCache *this_ptr, GeColliderCache *dest);
		LONG								(*BeginInput			)(GeColliderCache *this_ptr, LONG cnt_tris);
		LONG								(*AddTriangle		)(GeColliderCache *this_ptr, const Vector& p1, const Vector& p2, const Vector& p3, LONG id);
		LONG								(*EndInput				)(GeColliderCache *this_ptr);
};																												
																													
#define LIB_COLLIDERENGINE 1000488												
																													
struct ColliderEngineLib : public C4DLibrary							
{																													
		GeColliderEngine*		(*Alloc														)();
		void								(*Free														)(GeColliderEngine *&data);
																													
		LONG								(*DoCollide			)(GeColliderEngine *this_ptr, const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, LONG flag);
		LONG								(*DoPolyPairs		)(GeColliderEngine *this_ptr, const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real tolerance);
		LONG								(*DoTolerance		)(GeColliderEngine *this_ptr, const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real tolerance);
		LONG								(*DoDistance		)(GeColliderEngine *this_ptr, const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real rel_err, Real abs_err);
		void								(*FreePairsList	)(GeColliderEngine *this_ptr);
		LONG								(*GetNumPairs		)(GeColliderEngine *this_ptr);
		LONG								(*GetId1				)(GeColliderEngine *this_ptr, LONG k);
		LONG								(*GetId2				)(GeColliderEngine *this_ptr, LONG k);
		Bool								(*IsCloser			)(GeColliderEngine *this_ptr);
		Real								(*GetDistance		)(GeColliderEngine *this_ptr);
		const Vector&				(*GetP1					)(GeColliderEngine *this_ptr);
		const Vector&				(*GetP2					)(GeColliderEngine *this_ptr);
		LONG								(*DoRayCollide	)(GeColliderEngine *this_ptr, GeColliderCache *o1, const Vector &ray_p, const Vector &ray_dir, Real ray_lengt);
};

#define LIB_RAYCOLLIDER 1000499												
																													
struct RayColliderLib : public C4DLibrary							
{																													
		GeRayCollider*	(*Alloc																)();
		void						(*Free																)(GeRayCollider *&data);
																													
		Bool						(*Init									)(GeRayCollider *this_ptr, BaseObject *goal, Bool force);
		Bool						(*Intersect						)(GeRayCollider *this_ptr, const Vector &ray_p, const Vector &ray_dir, Real ray_length, Bool only_test);
		LONG						(*GetIntersectionCount	)(GeRayCollider *this_ptr);
		Bool						(*GetIntersection			)(GeRayCollider *this_ptr, LONG number, GeRayColResult *res);
		Bool						(*GetNearestIntersection)(GeRayCollider *this_ptr, GeRayColResult *res);	
};

// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF

#endif
