/*****************************************************************************
 * $Id: mod_pony_craft.cc,v 1.1 2006/11/24 21:56:58 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Craft derivative class.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define MOD_PONY_CRAFT_CC 1
#include "mod_pony_common.hh"
#include "mod_base_craft.hh"
#include "mod_pony_craft.hh"

////////////////////////////////////////////////////////////////////////////////
//////////////////////////////  CraftF14Tomcat  ////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// Sweeping wings:
// In the Blender model, the wings were marked as "Vertex Groups".
// The Python script export-palomino.py exports Vertex Groups.
// Two Vertex Groups define a wing, two more Vertex Groups define
// a single vertex around which the wing will be rotated (wing sweep).
// When this program starts, the base DynaModel imports those Vertex Groups
// into an Importation object, which becomes available here
// as CraftF14Tomcat::mModels[].GetVertexGroupVixs().
// Two matrixs are positioned at a vertex on each each wing,
// and the matrix is used to transform the vertexs of the wing.
// The vertexs are indexed by the vixs (vertex indexs).
// -----------------------------------------------------------------------------

const Percent F14_WING_SWEEP_SPEED		= 20;		// min speed when wings will sweep back
const Degree  F14_WING_SWEEP_DEGREE		= 45.0;		// max wing sweep-back (delta configuration)
const Degree  F14_GLOVE_VANE_DEGREE		= 43.0;		// wing sweep threshold when vanes will extend
const uint	  F14_ADJUST_WINGS_MILSEC	= 100;		// to smoothly adjust wings
const uint	  F14_ADJUST_GEARS_MILSEC	= 100;
const Degree  F14_DEG_WINGS_SPREAD      = 0.0;
const Degree  F14_DEG_GEARS_RETRACTED   = 90.0;
const Degree  F14_DEG_GEARS_EXTENDED    = 0.0;
const Degree  F14_DEG_TAILPLANES_MAX    = 10.0;     // at full rotation
const Degree  F14_DEG_TAILPLANES_INC    = 2.0;

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
CraftF14Tomcat::CraftF14Tomcat( QuadrantPtrConst q, const Position& pos, const vector<string>& fnameModelVec )
:   Craft(q,pos,fnameModelVec),
	mDegSweep(0.0),
	mDegGloveVanes(0.0),
    mDegLeftTailplane(0.0),
    mDegRightTailplane(0.0),
    mDegLeftTailplaneGoal(0.0),
    mDegRightTailplaneGoal(0.0),
	mDegGears(0.0),
	mAdjustWingsMilsec(0),
	mAdjustGearsMilsec(0),
	mGearState(eGear_DOWN),
    mPartLeftWing(       *this, mModels[0].mImportation.GetVertexGroupVixs("LeftWing"),       mModels[0].mImportation.GetVertexGroupVixs("LeftWingPivot")[0] ),
    mPartRightWing(      *this, mModels[0].mImportation.GetVertexGroupVixs("RightWing"),      mModels[0].mImportation.GetVertexGroupVixs("RightWingPivot")[0] ),
    mPartLeftGloveVane(  *this, mModels[0].mImportation.GetVertexGroupVixs("LeftGloveVane"),  mModels[0].mImportation.GetVertexGroupVixs("LeftGloveVanePivot")[0] ),
    mPartRightGloveVane( *this, mModels[0].mImportation.GetVertexGroupVixs("RightGloveVane"), mModels[0].mImportation.GetVertexGroupVixs("RightGloveVanePivot")[0] ),
    mPartLeftTailplane(  *this, mModels[0].mImportation.GetVertexGroupVixs("LeftTailplane"),  mModels[0].mImportation.GetVertexGroupVixs("LeftTailplanePivot")[0] ),
    mPartRightTailplane( *this, mModels[0].mImportation.GetVertexGroupVixs("RightTailplane"), mModels[0].mImportation.GetVertexGroupVixs("RightTailplanePivot")[0] ),
    mPartFrontGear(      *this, mModels[0].mImportation.GetVertexGroupVixs("FrontGear"),      mModels[0].mImportation.GetVertexGroupVixs("FrontGearPivot")[0] ),
    mPartLeftGear(       *this, mModels[0].mImportation.GetVertexGroupVixs("LeftGear"),      mModels[0].mImportation.GetVertexGroupVixs("LeftGearPivot")[0] ),
    mPartRightGear(      *this, mModels[0].mImportation.GetVertexGroupVixs("RightGear"),      mModels[0].mImportation.GetVertexGroupVixs("RightGearPivot")[0] )
{
	DEBUG_CODE( CheckObjectCtor(); )

	// Blender model has both wings spread and glove vanes extended.
	// Retract glove vanes initially.
	AdjustGloveVanes( F14_DEG_WINGS_SPREAD );

    // If game mode, Tomcats are flying, so retract landing gears.
    if ( gModule.IfGameMode() )
    {
        RetractLandingGears();
    }
}
CraftF14Tomcat::~CraftF14Tomcat()
{
	DEBUG_CODE( CheckObjectDtor(); )
}

/*****************************************************************************
 * Reset.
 *****************************************************************************/
void
CraftF14Tomcat::Reset( void )
{
    // Spread wings and lower landing gears immediately.
    AdjustWings2( 0 );
    ExtendLandingGears();
    Parent::Reset();
}

/*****************************************************************************
 * Animation is driven by a timer.
 * See eng_dyna.hh about relationship of Animate() methods.
 *****************************************************************************/
int
CraftF14Tomcat::Animate( void )
{
	int redisplay = 0;

	redisplay |= AdjustWings();			// Tomcat wing-sweep changes with speed
	redisplay |= AdjustGears();
    redisplay |= AdjustTailplanes();
	redisplay |= Parent::Animate();

	return redisplay;
}

/*****************************************************************************
 * Hooks to rotate tailerons.
 *****************************************************************************/
void
CraftF14Tomcat::Rotate( uint axis, Degree deg )
{
// HACK/TODO/FIXME: This malfunctions when joystick is used (code was written/tested with keyboard only).
if ( ! gJoy.IfEnabled() )
{
    switch ( axis )
    {
        case AXIS_X: // pitch
        {
            const fp dir = deg < 0 ? 1 : -1;
            AdjustTailplanes( dir, dir );
        }
        break;

        case AXIS_Z: // roll
        {
            const fp dir = deg < 0 ? -1 : 1;
            AdjustTailplanes( dir, -dir );
        }
        break;

        default:
        break;
    }
}// HACK/TODO/FIXME

    // Call base method.
    Parent::Rotate( axis, deg );
}

/*****************************************************************************
 * Sweep F-14 Tomcat wings.
 * @return True if wings were adjusted.
 *****************************************************************************/
bool
CraftF14Tomcat::AdjustWings( void )
{
	// TODO: Speed should be a Mach float value, not a simple percent.
	// TODO: A real F-14 sweeps its wings according to speed, not throttle.

	// Adjust wings according to time (try to avoid flutter).
	const uint milsec = gEng.GetMilsecTotal();
	if ( milsec - mAdjustWingsMilsec < F14_ADJUST_WINGS_MILSEC )
	{
		return false;
	}
	mAdjustWingsMilsec = milsec;  // remember

	// Gradually adjust wings by a small degree every frame until
	// the desired sweep is reached.
	const fp inc = 1.5;
	const Percent speed = GetThrottle();
	const Degree degSweep = (speed <= F14_WING_SWEEP_SPEED) ? 0.0 : fp(speed) / 100.0 * F14_WING_SWEEP_DEGREE;
	AdjustGloveVanes( mDegSweep );  // current wing sweep
	if ( degSweep - mDegSweep < -inc )
	{
		// Sweep back.
		mDegSweep -= inc;
        AdjustWings2( mDegSweep );
    	return true;
	}
	else if ( degSweep - mDegSweep > inc )
	{
		// Sweep forward.
		mDegSweep += inc;
        AdjustWings2( mDegSweep );
    	return true;
	}
	else
	{
		// Desired sweep was reached.
		return false;
	}
}
// Adjust wings directly.
void
CraftF14Tomcat::AdjustWings2( const Degree deg )
{
    mDegSweep = deg;
    mPartLeftWing.Rotate(  AXIS_Y, -mDegSweep );
    mPartRightWing.Rotate( AXIS_Y,  mDegSweep );
}

/*****************************************************************************
 * Adjust glove vanes based on wing sweep.
 *****************************************************************************/
bool
CraftF14Tomcat::AdjustGloveVanes( const Degree degSweep )
{
	// Gradually adjust glove vanes.
	const fp inc = 2.5;
    const Degree degGloveVane = degSweep >= F14_GLOVE_VANE_DEGREE ? 0.0 : 45.0;
	if ( degGloveVane - mDegGloveVanes < -inc )
	{
		mDegGloveVanes -= inc;
	}
	else if ( degGloveVane - mDegGloveVanes > inc )
	{
		mDegGloveVanes += inc;
	}
	else
	{
		return false;
	}

    // Rotate glove vanes.
	mPartLeftGloveVane.Rotate(  AXIS_Y, -mDegGloveVanes );
	mPartRightGloveVane.Rotate( AXIS_Y,  mDegGloveVanes );
	return true;
}

/*****************************************************************************
 * Adjust tailplanes.
 * @param   dirLeft, dirRight
 *          Direction to rotate tailplanes/tailerons.
 *****************************************************************************/
// This sets the goal degree the tailplanes should rotate to.
bool
CraftF14Tomcat::AdjustTailplanes( const fp dirLeft, const fp dirRight )
{
    // In the model, the mesh parts are opposite,
    // so using the same sign for the degree would rotate them oppositely.
    mDegLeftTailplaneGoal  = dirLeft  * F14_DEG_TAILPLANES_MAX;
    mDegRightTailplaneGoal = dirRight * F14_DEG_TAILPLANES_MAX;
    return true;
}
// (private) Gradually rotate tailplanes to the goal degree.
bool
CraftF14Tomcat::AdjustTailplanes( void )
{
    return AdjustOneTailplane( mDegLeftTailplane,  mDegLeftTailplaneGoal, mPartLeftTailplane )
         | AdjustOneTailplane( mDegRightTailplane, mDegRightTailplaneGoal, mPartRightTailplane );
         
}
// (private) Subroutine to rotate one tailplane.
bool
CraftF14Tomcat::AdjustOneTailplane( Degree& deg, Degree& degGoal, MeshPart& part ) // IN/OUT
{
    // Gradually adjust tailplanes.
	// Determine degree to rotate incrementally.
	const Degree inc = F14_DEG_TAILPLANES_INC;
    if ( degGoal == 0.0 )  // rotate tailplane level?
    {
        if ( deg < 0 )
            deg += inc;
        else if ( deg > 0 )
            deg -= inc;
        else
            return false;
    }
    else if ( degGoal < 0.0 )  // rotate tailplane up?
    {
    	if ( degGoal - deg < -inc )
    		deg -= inc;
    	else
    		return false;
	}
    else if ( degGoal > 0.0 )  // rotate tailplane down?
    {
    	if ( degGoal - deg > inc )
    		deg += inc;
    	else
    		return false;
	}

    // Rotate a tailplane (mesh part).
	part.Rotate( AXIS_X, deg );
	return true;
}

/*****************************************************************************
 * Adjust landing gear.
 * @return True if gears were adjusted.
 *****************************************************************************/
bool
CraftF14Tomcat::AdjustGears( void )
{
	const uint milsec = gEng.GetMilsecTotal();
	if ( milsec - mAdjustGearsMilsec < F14_ADJUST_GEARS_MILSEC )
	{
		return false;
	}
	mAdjustGearsMilsec = milsec;  // remember

	// Gradually adjust by a small degree every frame.
	const fp inc = 2.0;
	const Degree degGears = (mGearState == eGear_DOWN) ? F14_DEG_GEARS_EXTENDED : F14_DEG_GEARS_RETRACTED;
	if ( degGears - mDegGears < -inc )
	{
		mDegGears -= inc;
        AdjustGears2( mDegGears );
    	return true;
	}
	else if ( degGears - mDegGears > inc )
	{
		mDegGears += inc;
        AdjustGears2( mDegGears );
    	return true;
	}
	else
	{
		// Desired rotation was reached.
		return false;
	}
}
// Adjust landing gears directly.
void
CraftF14Tomcat::AdjustGears2( const Degree deg )
{
    mDegGears = deg;
    if ( deg >= F14_DEG_GEARS_RETRACTED * 0.9 )  // nearly at extreme retraction?
    {
        // A reliable way to make the gears disappear
        // is to write (0,0,0) to each vertex so gear
        // is just a tiny point in center of craft.
        const static LocalVertex point(0,0,0);
        mPartFrontGear.Implode( point );
        mPartLeftGear.Implode( point );
        mPartRightGear.Implode( point );
    }
    else
    {
        mPartFrontGear.Rotate(  AXIS_X, mDegGears );
        mPartLeftGear.Rotate(   AXIS_X, mDegGears );
        mPartRightGear.Rotate(  AXIS_X, mDegGears );
    }
}
void
CraftF14Tomcat::RetractLandingGears( void )
{
    mGearState = eGear_UP;
    AdjustGears2( F14_DEG_GEARS_RETRACTED );
}
void
CraftF14Tomcat::ExtendLandingGears( void )
{
    mGearState = eGear_DOWN;
    AdjustGears2( F14_DEG_GEARS_EXTENDED );
}
