/*****************************************************************************
 * $Id: eng_pilot.cc,v 1.1 2006/10/31 04:16:55 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Pilot, Animation, ChasePlane classes (closely associated with Dyna class).
 *//*
 * LEGAL:	COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define ENG_PILOT_CC 1
#include "eng.hh"
#include "eng_pilot.hh"
#include "eng_dyna.hh"

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  Animation  ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Wrappers around Multivar.
 * Rule: eAnimation_NONE should not be put into Multivar.
 *       Rather, it is an instruction to clear Multivar.
 *****************************************************************************/
void
Animation::Clear( void )
{
	mMultivar.Clear();
}
void
Animation::Remove( const eAnimation animation )
{
	mMultivar.Remove( animation );
}
void
Animation::Set( const eAnimation animation )
{
	if ( animation == eAnimation_NONE )  // clear Multivar, don't put this enum in
		mMultivar.Clear();
	else
		mMultivar.Set( animation );
}
void
Animation::Add( const eAnimation animation )
{
	if ( animation == eAnimation_NONE )  // regardless of "add", force no animation
		mMultivar.Clear();
	else
		mMultivar.Add( animation );
}
void
Animation::Toggle( const eAnimation animation )
{
ASSERT( animation != eAnimation_NONE );  // nonsensical, if multivar is empty, toggle to what?

	mMultivar.Toggle( animation );  // add/remove (not set/remove)
}
bool
Animation::Query( const eAnimation animation ) const
{
	if ( animation == eAnimation_NONE )
		return mMultivar.IfEmpty();
	else
		return mMultivar.Query( animation );
}

/*****************************************************************************
 * @return True if no animation.
 *****************************************************************************/
bool
Animation::IfNone( void ) const
{
// eAnimation_NONE should not be inside Multivar.
ASSERT( ! mMultivar.Query( eAnimation_NONE ) );

	return mMultivar.IfEmpty();
}

/*****************************************************************************
 * @return True if collision animation.
 *****************************************************************************/
bool
Animation::IfCollision( void ) const
{
	// TODO optimize away this loop (some sort of any-collision enum).
	for ( int animation = eAnimation_COLLISION_RANGE_START;
          animation <= eAnimation_COLLISION_RANGE_END;
          ++animation )
	{
		if ( Query( animation ) ) return true;
	}
	return false;
}
// static method
bool
Animation::IfCollision( const eAnimation animation )
{
    return animation >= eAnimation_COLLISION_RANGE_START
        && animation <= eAnimation_COLLISION_RANGE_END;
}

////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////  Pilot  /////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
Pilot::Pilot( Dyna& owner )
:   mOwner(owner)
{
	CHECK_TYPESIG(&owner,TYPESIG_DYNA);
    mAutoRotateAxis = AXIS_INVALID;
    mAutoRotateDir = 0;
}

/*****************************************************************************
 * Reset Pilot.
 *****************************************************************************/
void
Pilot::Reset( void )
{
    mAnimation.Clear();
}

/*****************************************************************************
 * Animation is driven by a timer.
 * See eng_dyna.hh about relationship of Animate() methods.
 *****************************************************************************/
int
Pilot::Animate( void )
{
	CHECK_TYPESIG(&mOwner,TYPESIG_DYNA);

    int redisplay = 0;  // also means if case was handled

    // Animation can be in multiple states.

    // No animation state?
    if ( mAnimation.IfNone() || mAnimation.Query( eAnimation_NONE ) )
    {
        return 0;
    }

    if ( mAnimation.Query( eAnimation_AUTO_TRANSLATE ) )
    {
        // Fly forward (one step per frame).
        mOwner.Step();
		redisplay = true;
    }

    if ( mAnimation.Query( eAnimation_AUTO_ROTATE ) )
    {
        // Fly forward (one step per frame).
	ASSERT( CHECK_AXIS(mAutoRotateAxis) );
	ASSERT( mAutoRotateDir == -1 || mAutoRotateDir == 1 );
		mOwner.RotateInc( mAutoRotateAxis, mAutoRotateDir );
		redisplay = true;
    }

    ASSERT( redisplay );  // catch unhandled animation case

    return redisplay;
}

/*****************************************************************************
 * Auto-rotate Dyna every frame.
 *****************************************************************************/
void
Pilot::SetAutoRotate( const uint axis, const int dir )
{
ASSERT( CHECK_AXIS(axis) );
ASSERT( dir == -1 || dir == 1 );
    mAnimation.Add( eAnimation_AUTO_ROTATE );
    mAutoRotateAxis = axis;
    mAutoRotateDir = dir;
}
