/*****************************************************************************
 * $Id: eng_collision.hh,v 1.39 2006/08/03 02:17:21 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Collision class.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#ifndef ENG_COLLISION_HH
#define ENG_COLLISION_HH 1

#include "eng_view.hh"

class Object;
class ObjectMesh;
class Dyna;

////////////////////////////////////////////////////////////////////////////////
/// @brief Holds state for polygon interior testing across frames (per 3D view).
/// Used only by Detector::DetectByPolygonInterior().
/// To conserve memory, this is allocated only when needed.
/// Because of the possibility of multiple views, state is saved per-view across frames.
class PolygonInterior
{
PREVENT_COPYING(PolygonInterior)
public:
	explicit PolygonInterior( uint polygonCnt )
	{
        // For speed, allocate an array and subdivide it (rather than allocating multiples).
        mFacing = new Bool[2*VIEWS_MAX*polygonCnt];
		for ( uint view = 0; view < VIEWS_MAX; ++view )
		{
			mFrame[view]      = UINT_MAX;
			mFacingPrev[view] = &mFacing[view*polygonCnt];
			mFacingCurr[view] = &mFacing[view*polygonCnt + VIEWS_MAX*polygonCnt];
		}
	}
	~PolygonInterior()
	{
        delete[] mFacing;  // mFacing owns the array (not mFacingPrev/Curr)
	}
	void	SetFrame( uint viewNum, uint frame ) { mFrame[viewNum] = frame; }
	uint	GetFrame( uint viewNum )		     { return mFrame[viewNum]; }
	void	SwapArrays( uint viewNum )		     { Swap( mFacingCurr[viewNum], mFacingPrev[viewNum] ); }
	void	SetFacingPrev( uint viewNum, uint i, Bool f ) { mFacingPrev[viewNum][i] = f; }
	void	SetFacingCurr( uint viewNum, uint i, Bool f ) { mFacingCurr[viewNum][i] = f; }
	Bool	GetFacingPrev( uint viewNum, uint i )         { return mFacingPrev[viewNum][i]; }
	Bool	GetFacingCurr( uint viewNum, uint i )         { return mFacingCurr[viewNum][i]; }

private:
	uint	mFrame[VIEWS_MAX];
    Bool*   mFacing;                    ///< owner of array
	Bool*	mFacingPrev[VIEWS_MAX];		///< previous frame (doesn't own array)
	Bool*	mFacingCurr[VIEWS_MAX];		///< current frame  (doesn't own array)
};

////////////////////////////////////////////////////////////////////////////////
/// @brief Abstract base class for collision-detection.  See notes_collision.cc.
class Detector
{
PREVENT_COPYING(Detector)
public:                     Detector( ObjectMesh& obj ) : mOwner(obj), mEnabled(true) { DEBUG_CODE(mValid=true;) }
public:     virtual         ~Detector() { }
public: 	virtual bool	Collided( Dyna& dyna ) = 0;
public: 	void			Enable( bool flag ) { mEnabled = flag; }  // can make an Object indestructible
public: 	bool			IfEnabled( void ) const { return mEnabled; }
public:     ObjectMesh&     GetOwner( void ) const { return mOwner; }

protected:  ObjectMesh&		mOwner;     // Object that owns this Detector object
protected:	Bool			mEnabled;
#if DEBUG
protected:	Bool			mValid;
#endif
};

////////////////////////////////////////////////////////////////////////////////
/// @brief Collision detector class using only radius algorith.  See eng_collision.cc.
class DetectorRadius : public Detector
{
PREVENT_COPYING(DetectorRadius)
private: typedef Detector Parent;
public:                     DetectorRadius( ObjectMesh& obj );
public:                     ~DetectorRadius() { }
public: 	virtual bool	Collided( Dyna& dyna );

protected:	bool			DetectByRadius( Dyna& dyna );

/// @brief Detection test by distance/radius.
/// radiusSquare is computed once (assumes the size of an object doesn't change).
/// An object's center may be dynamic (shouldn't stored once in a member).
private:	fp				mRadiusSquare;  // radius*radius
};

////////////////////////////////////////////////////////////////////////////////
/// @brief Collision detector class using all detection algorithms.  See eng_collision.cc.
class DetectorAll : public DetectorRadius
{
PREVENT_COPYING(DetectorAll)
private: typedef DetectorRadius Parent;
public:     				DetectorAll( ObjectMesh& obj, fp minY = 0.0, fp maxY = 0.0 );
public:     virtual 		~DetectorAll();
public: 	bool			Collided( Dyna& dyna );

private:	bool			DetectBySlices( Dyna& dyna );
private:	bool			DetectByPolygonInterior( Dyna& dyna );

/// @brief Detection test by bounding rectangular slices stacked vertically.
private:	uint16				mSliceCnt;
private:	RectXZ*				mSlices;        	///< slices[sliceCnt]
private:	fp					mSlicesMinY;    	///< bottom Y of stack of slices
private:	fp					mSlicesMaxY;    	///< top Y
private:	PolygonInterior*	mPolygonInterior;   ///< lazy allocated
};

#endif // ENG_COLLISION_HH
