/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Aircraft specifications.
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define PHYSICS_AIRCRAFT_SPECS_CC 1
#include <fstream>
#include "base/module.hh"
#include "base/conf.hh"
#include "base/stream.hh"
using namespace base;
#include "math/module.hh"
#include "math/funcs_trig.hh"
using namespace math;
#include "object/module.hh"
#include "object/rotation_list.hh"
using namespace object;
#include "world/module.hh"
using namespace world;
#include "physics/module.hh"
#include "physics/aircraft_specs.hh"

namespace physics {

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////  AircraftSpecs  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
/// @brief Fills AircraftSpecs object from text .conf file.
///
/// Mass, max thrust, etc are defined in a data file (physics_*.conf).
/// These parameters are loaded into a AircraftSpecs for use by AircraftPhysics.
/// AircraftSpecs is data, AircraftPhysics is computational.
///
class AircraftSpecsConfTxt : public base::ConfTxt
{
public:
            AircraftSpecsConfTxt( AircraftSpecs& aircraftSpecs ) : mAircraftSpecs(aircraftSpecs) { }
    void    ProcessField( string& field, std::ifstream& conf );

private:
    AircraftSpecs& mAircraftSpecs;
};

/*****************************************************************************
 * ctor.
 *****************************************************************************/
AircraftSpecs::AircraftSpecs( void )
:   mMass(0),           // zero them for assertion checking
    mMaxThrustMag(0),   // magnitude (Newton1)
    mEngineType(eEngineType_PROPELLER),
    mDragAofALimit(0),
    mDragAofADegree(0),
    mLiftoffSpeed(0),
    mMaxAofA(0),
    mRangeOfLiftFactor(0),
    mRangeOfLiftFactorSpeed(0),
    mRangeOfLiftFactorAofA(0),
    mTurnDegreeLow(0),
    mTurnDegreeHigh(0),
    mLength(0),
    mHeight(0),
    mMaxSpeed(0),
    mStallSpeed(0),
    mRollRate(0),
    mPitchRate(0),
    mYawRate(0),
    mModelFile(),
    mModelScale(1.0f),
    mModelOffset(Meter(0),Meter(0),Meter(0)),
    mModelOffsetGear(0),
    mModelRotationList(),
    mModelShader("aircraft"),
    mAileronOffset(0,0,0),
    mElevatorOffset(0,0,0),
    mRudderOffset(0,0,0),
    mRudder2Offset(0,0,0),
    mAileronROF(Deg2Rad(12.0f)),
    mElevatorROF(Deg2Rad(12.0f)),
    mRudderROF(Deg2Rad(12.0f)),
    mAileronAngle(0),
    mRudderAngle(0)
{
    // NOP
}

/*****************************************************************************
 * Reset.
 *****************************************************************************/
void
AircraftSpecs::Reset( void )
{
    // Clear STL containers else they'd grow.
    mModelRotationList.Clear();
}

/*****************************************************************************
 * Read a Aircraft's specificiations/characteristics from a .conf file.
 * Supports reloading from file.
 *****************************************************************************/
void
AircraftSpecs::ReadSpecsFromConfFile( const string& confTxtFilename )
{
    // Reset state to support reloading.
    Reset();

    try
    {
        AircraftSpecsConfTxt confTxt( *this );  // will load this AircraftSpecs
        confTxt.Read( string(base::GetDataDir()) + string(confTxtFilename) );
    }
    catch (...)
    {
        CERROR << "ERROR: cannot read " << confTxtFilename << std::endl;
        EXIT( 1 );
    }
}

void
AircraftSpecsConfTxt::ProcessField( string& field, std::ifstream& conf )
{
    bool error = false;

    // -------------------------- mass -----------------------------------------
    if ( field == "Mass" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mMass = Kilogram( f );
    }
    // -------------------------- thrust -----------------------------------------
    else if ( field == "MaxThrust" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mMaxThrustMag = Newton1(f);  // magnitude
    }
    else if ( field == "EngineType" )
    {
        // Convert string to enum.
        string s; conf >> s;
        if ( s == "propeller" )
            mAircraftSpecs.mEngineType = AircraftSpecs::eEngineType_PROPELLER;
        else if ( s == "jet" )
            mAircraftSpecs.mEngineType = AircraftSpecs::eEngineType_JET;
        else if ( s == "rocket" )
            mAircraftSpecs.mEngineType = AircraftSpecs::eEngineType_ROCKET;
        else
            error = true;
    }
    // -------------------------- drag -----------------------------------------
    else if ( field == "DragAofALimit" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mDragAofALimit = f;
    }
    else if ( field == "DragAofADegree" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mDragAofADegree = f;
    }
    // -------------------------- lift -----------------------------------------
    else if ( field == "LiftoffSpeed" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mLiftoffSpeed = SpeedKPH(f);
    }
    else if ( field == "MaxAofA" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mMaxAofA = f;
    }
    else if ( field == "RangeOfLiftFactor" )
    {
        conf >> mAircraftSpecs.mRangeOfLiftFactor;
    }
    else if ( field == "RangeOfLiftFactorSpeed" )
    {
        conf >> mAircraftSpecs.mRangeOfLiftFactorSpeed;
    }
    else if ( field == "RangeOfLiftFactorAofA" )
    {
        conf >> mAircraftSpecs.mRangeOfLiftFactorAofA;
    }
    // -------------------------- turning -----------------------------------------
    else if ( field == "TurnDegreeLow" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mTurnDegreeLow = f;
    }
    else if ( field == "TurnDegreeHigh" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mTurnDegreeHigh = f;
    }
    // -------------------------- airframe -----------------------------------------
    else if ( field == "Length" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mLength = Meter(f);
    }
    else if ( field == "Height" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mHeight = Meter(f);
    }
    else if ( field == "MaxSpeed" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mMaxSpeed = SpeedKPH(f);
    }
    else if ( field == "StallSpeed" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mStallSpeed = SpeedKPH(f);
    }
    else if ( field == "RollRate" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mRollRate = f;
    }
    else if ( field == "PitchRate" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mPitchRate = f;
    }
    else if ( field == "YawRate" )
    {
        fp f; conf >> f;
        mAircraftSpecs.mYawRate = f;
    }
    // -------------------------- 3D model -------------------------------------
    else if ( field == "ModelFile" )
    {
        conf >> mAircraftSpecs.mModelFile;
    }
    else if ( field == "ModelScale" )
    {
        conf >> mAircraftSpecs.mModelScale;
    }
    else if ( field == "ModelOffset" )
    {
        fp f;
        conf >> f; mAircraftSpecs.mModelOffset[XX] = Meter(f);
        conf >> f; mAircraftSpecs.mModelOffset[YY] = Meter(f);
        conf >> f; mAircraftSpecs.mModelOffset[ZZ] = Meter(f);
    }
    else if ( field == "ModelOffsetGear" )
    {
        fp f; conf >> f; mAircraftSpecs.mModelOffsetGear = Meter(f);
    }
    else if ( field == "ModelRotationList" )
    {
        mAircraftSpecs.mModelRotationList.Parse( conf );
    }
    else if ( field == "ModelShader" )
    {
        conf >> mAircraftSpecs.mModelShader;
    }
    // -------------------------- Control Surfaces -------------------------------------
    else if ( field == "AileronOffset" )
    {
        conf >> mAircraftSpecs.mAileronOffset[XX];
        conf >> mAircraftSpecs.mAileronOffset[YY];
        conf >> mAircraftSpecs.mAileronOffset[ZZ];
    }
    else if ( field == "ElevatorOffset" )
    {
        conf >> mAircraftSpecs.mElevatorOffset[XX];
        conf >> mAircraftSpecs.mElevatorOffset[YY];
        conf >> mAircraftSpecs.mElevatorOffset[ZZ];
    }
    else if ( field == "RudderOffset" )
    {
        conf >> mAircraftSpecs.mRudderOffset[XX];
        conf >> mAircraftSpecs.mRudderOffset[YY];
        conf >> mAircraftSpecs.mRudderOffset[ZZ];
    }
    else if ( field == "Rudder2Offset" )
    {
        conf >> mAircraftSpecs.mRudder2Offset[XX];
        conf >> mAircraftSpecs.mRudder2Offset[YY];
        conf >> mAircraftSpecs.mRudder2Offset[ZZ];
    }
    else if ( field == "AileronDOF" )  // member is named ROF ("radian of freedom")
    {
        fp f; conf >> f; mAircraftSpecs.mAileronROF = Deg2Rad(f);
    }
    else if ( field == "ElevatorDOF" )
    {
        fp f; conf >> f; mAircraftSpecs.mElevatorROF = Deg2Rad(f);
    }
    else if ( field == "RudderDOF" )
    {
        fp f; conf >> f; mAircraftSpecs.mRudderROF = Deg2Rad(f);
    }
    else if ( field == "AileronAngle" )
    {
        fp f; conf >> f; mAircraftSpecs.mAileronAngle = Deg2Rad(f);
    }
    else if ( field == "RudderAngle" )
    {
        fp f; conf >> f; mAircraftSpecs.mRudderAngle = Deg2Rad(f);
    }

    // Errors?
    if ( error )
    {
        std::ostringstream msg;
        msg << "ERROR[conf]: parameter for " << field << " is invalid." << std::endl;
        throw Exception( msg.str() );
    }
}

} // namespace physics
