/*****************************************************************************
 * $Id: eng_dyna.hh,v 1.144 2006/08/10 03:18:58 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Dyna (dynamic object) class.
 * @remarks	FYI: This code is faster but wrong.  It'll cause typesig checks to fail.
 *               return reinterpret_cast<const WorldVertex*>( &mCm[Ox] );
 * @verbatim
 * ----------------------------------------------------------------------------
 * Relation of Animate() methods:
 *
 * The Engine's timer routine broadcasts an Animate event on the presumption
 * that a listening module will call the Animate() methods of Dyna objects.
 * Animate() methods return true if a redisplay event should be posted to the gfxsys.
 * Both Dyna and Pilot classes have Animate() methods.
 * The Dyna class contains a Pilot object.
 * Dyna::Animate() defers to Pilot::Animate().
 * All Animate() methods share a common set of eAnimate enums.
 * Animate() methods can be chained/hooked either in the Dyna or Pilot class descent.
 * ----------------------------------------------------------------------------
 * @endverbatim
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#ifndef ENG_DYNA_HH
#define ENG_DYNA_HH 1

#include "eng_object.hh"

class Dyna;

// NOTE: All Animate() methods must defined different enums (no overlapping enums).
#undef  eAnimate
#define eAnimate int
enum //eAnimate
{
	eAnimate_NONE,
	eAnimate_AUTO_TRANSLATE, eAnimate_DEFAULT = eAnimate_AUTO_TRANSLATE,
	eAnimate_CHASE_PLANE,

	// Range reserved for different kinds of collisions
	// (hard crash, minor crash shot-down, skid, spin, etc).
	eAnimate_COLLISION_RANGE_START,
		eAnimate_COLLISION_CRASH_HARD,		// movement stops
		eAnimate_COLLISION_CRASH_SOFT,		// movement stops
		eAnimate_COLLISION_SHOTDOWN_HARD,	// downward movement, destroyed
		eAnimate_COLLISION_SHOTDOWN_SOFT,	// downward movement, damaged but not destroyed
		//eAnimate_COLLISION_SKID,			// broken landing gear
		//eAnimate_COLLISION_TUMBLE,		// wings stalled during takeoff
	eAnimate_COLLISION_RANGE_END = eAnimate_COLLISION_RANGE_START + 20,

	// Other modules should start their enums from this.
	eAnimate_AFTER_DYNA_ENUMS
};

// @return True if animation state is any kind of collision (crash, shot-down, etc).
static inline bool
IfCollision( eAnimate animate )
{
	return animate >= eAnimate_COLLISION_RANGE_START
        && animate <= eAnimate_COLLISION_RANGE_END;
}

/// Stub.
class ChasePlane
{
PREVENT_COPYING(ChasePlane)
public:
	explicit		ChasePlane( const Dyna& dyna ) : mOwner(dyna) { }
	virtual			~ChasePlane() { }
	virtual void	Reset( void ) { }
	virtual int		Chase( void ) { return false; }
	virtual void	IncRotate( uint axis, int dir ) { }
	virtual void	IncDistance( int dir ) { }
	virtual void	SetDistance( fp dist ) { }
	virtual fp		GetDistance( void ) { return 0.0; }

protected:
    const Dyna&     mOwner;  // the Dyna object that the chase-plane chases.
};

/// Base Pilot class.  All but Pilot::Animate() are stubs.
class Pilot
{
PREVENT_COPYING(Pilot)
public:
	explicit			Pilot( Dyna& owner );
	virtual				~Pilot() { }
	virtual int			Animate( eAnimate animate );
	virtual	void		SetTarget( Dyna& target ) { }
	virtual void		SelectTarget( void ) { }
	virtual bool		IfProbing( void ) { return false; }

protected:
	Dyna&				mOwner;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief Base class for dynamic objects that have their own local coordinate systems and matrix.
///
/// About the set of matrixes:
/// The purpose of the extended-precision matrix xm[] is to use maximum accuracy
/// for rotating a matrix.  The faster single-precision matrix cm[] is used
/// far more frequently for transforming vertexes.  cm[] is a const reference
/// to the protected sm[].  sm[] should never be modified except by SyncMatrix().
/// This dual matrix scheme is a trade-off between speed vs. code complexity.
/// Any modification to xm[] requires synchronizing sm[].  But this requirement
/// is limited to the code in this class as xm[]/sm[] are protected.
///
class Dyna : public virtual Object
{
PREVENT_COPYING(Dyna)
// Trade-off matrix precision for speed.
#if SPEED
#define mXm mSm
#endif
private:	typedef Object Parent;
public:							Dyna( void );
public:		virtual				~Dyna();
// abstract methods:
public:		virtual void		Draw( void ) = 0;
public:		virtual void		Rotate( uint axis, fp deg ) = 0;
public:		virtual void		Translate( uint axis, fp inc ) = 0;
// virtual methods:
public:		virtual void		Init( void );
public:		virtual void		Reset( void ) { Parent::Reset(); }
public:		virtual int			Animate( void );
public:		virtual void		SetAnimation( eAnimate animate ) { mAnimate = animate; }
public:		virtual eAnimate	GetAnimation( void ) { return mAnimate; }
public:		virtual void		Reload( const MatrixExt& xm ) { CopyMatrix( mXm/*dest*/, xm/*src*/ ); SyncMatrix(); }
public:		virtual void		SetOrigin( uint Oxyz, fp coord );
public:		virtual void		RotateInc( uint axis, int dir );
public:		virtual void		TranslateInc( uint axis, int dir, fp stride = 0.0 );
public:		virtual void		Step( void );
public:		virtual void		Fence( void );
public:		virtual void		Revive( void ) { }
public:		virtual bool		IfDestroyed( void ) { return IfCollision( GetAnimation() ); }  // if crashed, shotdown, etc
// methods:
public:		const Matrix&		GetWorldEyeMatrix( void );
public:		bool				Reproducible( void ) { return false; }
public:		void				ToggleAnimation( eAnimate animate1, eAnimate animate2 );
// Get/set position methods.  Extended-precision float type is necessary
// to maintain extended-precision matrix (mXm).
// hi-freq (non-virtual would be faster, but it must be virtual,
// and anyway Object::GetPosition() declares it virtual).
public:		virtual Position GetPosition( void )
			{
				return mPos;
			}
// Position arg should have positive values.
public:		virtual void SetPosition( const Position& pos )
			{
				ASSERT( pos.x >= 0 && pos.y >= 0 && pos.z >= 0 );
				mXm[Ox] = pos.x;
				mXm[Oy] = pos.y;
				mXm[Oz] = pos.z;
				SyncMatrix();		// to update mCm[]/mSm[]
			}
// Maneuverability vs. control response.
// Maneuverability represents a craft's performance characteristics (roll/yaw/pitch rate).
// The user can adjust the control response which scales the base maneuverability values.
// Craft performance characteristics:
public:		void				SetManeuverability( uint axis, fp degreeMilsec ) { mManeuverability[axis] = degreeMilsec; }
public:		fp					GetManeuverability( uint axis )                  { return mManeuverability[axis]; }
public:		void				SetEnginePower( fp power ) { }
// Translation rate:
public:		virtual void		SetThrottle( Percent percent );
public:		Percent				GetThrottle( void );
public:		fp					GetStride( uint axis = AXIS_Z );
// Input device methods, user preferences:
public:		void				ClaimInput( void );
public:		bool				HaveInput( void );
public:		void				SetResponse( fp response ) { mResponse = response; } // 1.0 is normal/default response
public:		fp					GetResponse( void )        { return mResponse; }
private:	int					GetMilsecForDyna( void );
// Update single-precision matrix (and mPos) after extended-precision one is modified.
protected:	virtual void		SyncMatrix( void )
			{
			#if ! SPEED
				mSm[Xx] = mXm[Xx];
				mSm[Xy] = mXm[Xy];
				mSm[Xz] = mXm[Xz];
				mSm[Yx] = mXm[Yx];
				mSm[Yy] = mXm[Yy];
				mSm[Yz] = mXm[Yz];
				mSm[Zx] = mXm[Zx];
				mSm[Zy] = mXm[Zy];
				mSm[Zz] = mXm[Zz];
				mSm[Ox] = mPos.x = mXm[Ox];	// update Position also
				mSm[Oy] = mPos.y = mXm[Oy];
				mSm[Oz] = mPos.z = mXm[Oz];
			#else
				mPos.x = mXm[Ox];	// update Position also
				mPos.y = mXm[Oy];
				mPos.z = mXm[Oz];
			#endif // SPEED
			}
// Data:
public:		const Matrix&		mCm;					///< cm[] is a const reference to sm[]
public:		ChasePlane*			mChasePlane;            /// derived class may reassign pointer
public:		Pilot*				mPilot;					/// derived class may reassign pointer
protected:
#if ! SPEED
protected:	MatrixExt			mXm;					///< extended-precision matrix for accuracy
#endif
protected:	Matrix				mSm;					///< single-precision copy of xm[] for speed
protected:	static Matrix		mWEm;					///< temp Dyna matrix multiplied by Eye matrix (World/Eye)
protected:	Position			mPos;					///< tied with the matrix
protected:	eAnimate			mAnimate;				///< animation method
protected:	Normal*				mNormals;
protected:	fp					mAxisDir[3];			///< axis orientation (+-1)
protected:	fp					mYmin;					///< must be positive (0 if N/A)
protected:	Percent				mThrottle;
protected:	fp					mSpeed;					///< coord/millisecond
// Maneuverability is defined as by degree/millisecond rate of rotation.
// This rate is independent of the joystick/keyboard response.
protected:	fp					mManeuverability[3];	///< degree/millisecond
protected:	fp					mResponse;				///< scales base maneuverability to suit the user
private:	uint				mFrameWorldEyeMatrix;	///< used to avoid recalculating
private:	static Ptr<Dyna>	mgInputOwner;			///< there can be only one
public:		DECL_TYPESIG( TYPESIG_DYNA )
friend class Tracer;
friend class Pilot;
friend void ConfigCopyMatrix( MatrixExt& dest, const Dyna& dyna );
// Let other modules define friends.
#ifdef DYNA_FRIENDS
DYNA_FRIENDS
#endif
};

#if DEBUG
typedef       Ptr<Dyna> DynaPtr;
typedef const Ptr<Dyna> DynaPtrc;
#else
typedef Dyna* DynaPtr;
typedef Dyna* DynaPtrc;
#endif

////////////////////////////////////////////////////////////////////////////////
/// @brief Dyna object with a geometric mesh.
///
class DynaMesh : public Dyna, public ObjectMesh
{
PREVENT_COPYING(DynaMesh)
public:								DynaMesh( QuadrantPtrc q );
public:								~DynaMesh();
private:	void					Init( void );
public:		void					Reset( void ) { Dyna::Reset(); ObjectMesh::Reset(); }
public:		virtual Position		GetPosition( void ) { return Dyna::GetPosition(); }
public:		virtual void			SetPosition( const Position& pos ) { Dyna::SetPosition(pos); }
public:		bool					CollisionDetection( const Dyna* gunner = NULL );
public:		virtual LocalVertex*		GetLocalVertexs( void ) = 0;
public:		virtual const WorldVertex*	GetWorldVertexs( void );
public:		virtual const EyeVertex*	GetEyeVertexs( void );
public:		virtual Normal*				GetWorldNormals( void );
/// Avoids recalculating vertexs for the same object in the same frame.
/// These macros should be used in any GetWorldVertexs() that
/// dynamically calculates world vertexs every frame.
/// Unnecessary for objects that pre-calculate world vertexs
/// or objects that cache world vertexs.
/// Be sure that world vertex array will live long enough.
#define RETURN_IF_SAVED_DYNA_WORLD_VERTEXS \
	const WorldVertex* wvSaved = GetSavedWorldVertexs(); \
	if ( wvSaved ) return wvSaved;
#define SAVE_DYNA_WORLD_VERTEXS( WV ) \
	SaveWorldVertexs( (WV) );
private:	static DynaPtr				mWorldVertexsSavedOwner;
private:	static const WorldVertex*	mWorldVertexsSaved;
private:	static fp					mWorldVertexsSaved0x;	// wv[0].x
protected:	const WorldVertex* GetSavedWorldVertexs( void )
			{
				// Did this Dyna save its world vertexs on this frame?
				if ( EX(mWorldVertexsSavedOwner != this) )
					return NULL;  // need to calculate world vertexs
				else			
				{
					// Catch if array contents changed.
					CHECK_TYPESIG( &mWorldVertexsSaved[0], TYPESIG_LIB_VERTEX_WORLD );
					ASSERT( mWorldVertexsSaved[0].x == mWorldVertexsSaved0x );
					return mWorldVertexsSaved;
				}
			}
protected:	void SaveWorldVertexs( const WorldVertex* wv )
			{
				mWorldVertexsSavedOwner = this;  // remember to avoid recalculating
				mWorldVertexsSaved = wv;
				DEBUG_CODE( mWorldVertexsSaved0x = wv[0].x; )
			}
/// Reset state prior to rendering a frame.
public:		static void StartFrame( void )
			{
				// Forget.  Necessary in case one Dyna exists.
				mWorldVertexsSavedOwner = NULL;
				mWorldVertexsSaved = NULL;
			}
};

#endif // ENG_DYNA_HH
