#ifndef _LIB_COLIDERCACHE_H_
#define _LIB_COLIDERCACHE_H_

#include "c4d_library.h"

#include "ge_math.h"
#include "ge_vector.h"
#include "ge_matrix.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();

	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();

	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();

	public:

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

		~GeRayCollider();

		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								(GeColliderCache::*CopyTo					)(GeColliderCache *dest);
		LONG								(GeColliderCache::*BeginInput			)(LONG cnt_tris);
		LONG								(GeColliderCache::*AddTriangle		)(const Vector& p1, const Vector& p2, const Vector& p3, LONG id);
		LONG								(GeColliderCache::*EndInput				)();
};																												
																													
#define LIB_COLLIDERENGINE 1000488												
																													
struct ColliderEngineLib : public C4DLibrary							
{																													
		GeColliderEngine*		(*Alloc														)();
		void								(*Free														)(GeColliderEngine *&data);
																													
		LONG								(GeColliderEngine::*DoCollide			)(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, LONG flag = COL_ALL_CONTACTS);
		LONG								(GeColliderEngine::*DoPolyPairs		)(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real tolerance);
		LONG								(GeColliderEngine::*DoTolerance		)(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real tolerance);
		LONG								(GeColliderEngine::*DoDistance		)(const Matrix& M1, GeColliderCache *o1, const Matrix& M2, GeColliderCache *o2, Real rel_err, Real abs_err);
		void								(GeColliderEngine::*FreePairsList	)();
		LONG								(GeColliderEngine::*GetNumPairs		)();
		LONG								(GeColliderEngine::*GetId1				)(LONG k);
		LONG								(GeColliderEngine::*GetId2				)(LONG k);
		Bool								(GeColliderEngine::*IsCloser			)();
		Real								(GeColliderEngine::*GetDistance		)();
		const Vector&				(GeColliderEngine::*GetP1					)();
		const Vector&				(GeColliderEngine::*GetP2					)();
		LONG								(GeColliderEngine::*DoRayCollide	)(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						(GeRayCollider::*Init									)(BaseObject *goal, Bool force);
		Bool						(GeRayCollider::*Intersect						)(const Vector &ray_p, const Vector &ray_dir, Real ray_length, Bool only_test);
		LONG						(GeRayCollider::*GetIntersectionCount	)();
		Bool						(GeRayCollider::*GetIntersection			)(LONG number, GeRayColResult *res);
		Bool						(GeRayCollider::*GetNearestIntersection)(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
