/*******************************************************************************

 Module:       FGAircraft.cpp
 Author:       Jon S. Berndt
 Date started: 12/12/98                                   
 Purpose:      Encapsulates an aircraft
 Called by:    FGFDMExec

 ------------- Copyright (C) 1999  Jon S. Berndt (jsb@hal-pc.org) -------------

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU General Public License can also be found on
 the world wide web at http://www.gnu.org.

FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
Models the aircraft reactions and forces. This class is instantiated by the
FGFDMExec class and scheduled as an FDM entry. LoadAircraft() is supplied with a
name of a valid, registered aircraft, and the data file is parsed.

HISTORY
--------------------------------------------------------------------------------
12/12/98   JSB   Created
04/03/99   JSB   Changed Aero() method to correct body axis force calculation
                 from wind vector. Fix provided by Tony Peden.
05/03/99   JSB   Changed (for the better?) the way configurations are read in.

********************************************************************************
COMMENTS, REFERENCES,  and NOTES
********************************************************************************
[1] Cooke, Zyda, Pratt, and McGhee, "NPSNET: Flight Simulation Dynamic Modeling
      Using Quaternions", Presence, Vol. 1, No. 4, pp. 404-420  Naval Postgraduate
      School, January 1994
[2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
      JSC 12960, July 1977
[3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
      NASA-Ames", NASA CR-2497, January 1975
[4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
      Wiley & Sons, 1979 ISBN 0-471-03032-5
[5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
      1982 ISBN 0-471-08936-2

The aerodynamic coefficients used in this model are:

Longitudinal
  CL0 - Reference lift at zero alpha
  CD0 - Reference drag at zero alpha
  CDM - Drag due to Mach
  CLa - Lift curve slope (w.r.t. alpha)
  CDa - Drag curve slope (w.r.t. alpha)
  CLq - Lift due to pitch rate
  CLM - Lift due to Mach
  CLadt - Lift due to alpha rate

  Cmadt - Pitching Moment due to alpha rate
  Cm0 - Reference Pitching moment at zero alpha
  Cma - Pitching moment slope (w.r.t. alpha)
  Cmq - Pitch damping (pitch moment due to pitch rate)
  CmM - Pitch Moment due to Mach

Lateral
  Cyb - Side force due to sideslip
  Cyr - Side force due to yaw rate

  Clb - Dihedral effect (roll moment due to sideslip)
  Clp - Roll damping (roll moment due to roll rate)
  Clr - Roll moment due to yaw rate
  Cnb - Weathercocking stability (yaw moment due to sideslip)
  Cnp - Rudder adverse yaw (yaw moment due to roll rate)
  Cnr - Yaw damping (yaw moment due to yaw rate)

Control
  CLDe - Lift due to elevator
  CDDe - Drag due to elevator
  CyDr - Side force due to rudder
  CyDa - Side force due to aileron

  CmDe - Pitch moment due to elevator
  ClDa - Roll moment due to aileron
  ClDr - Roll moment due to rudder
  CnDr - Yaw moment due to rudder
  CnDa - Yaw moment due to aileron

This class expects to be run in a directory which contains the subdirectory
structure shown below (where example aircraft X-15 is shown):

aircraft/
         X-15/
              X-15.dat reset00 reset01 reset02 ...
              CDRAG/
                 a0 a M De
              CSIDE/
                 b r Dr Da
              CLIFT/
                 a0 a M adt De
              CROLL/
                 b p r Da Dr
              CPITCH/
                 a0 a adt q M De
              CYAW/
                 b p r Dr Da
         F-16/
              F-16.dat reset00 reset01 ...
              CDRAG/
                 a0 a M De
              ...

The General Idea

The file structure is arranged so that various modeled aircraft are stored in
their own subdirectory. Each aircraft subdirectory is named after the aircraft.
There should be a file present in the specific aircraft subdirectory (e.g.
aircraft/X-15) with the same name as the directory with a .dat appended. This
file contains mass properties information, name of aircraft, etc. for the
aircraft. In that same directory are reset files numbered starting from 0 (two
digit numbers), e.g. reset03. Within each reset file are values for important
state variables for specific flight conditions (altitude, airspeed, etc.). Also
within this directory are the directories containing lookup tables for the
stability derivatives for the aircraft.

********************************************************************************
INCLUDES
*******************************************************************************/

#include <sys/stat.h>
#include <sys/types.h>

#ifdef FGFS
# ifndef __BORLANDC__
#  include <Include/compiler.h>
# endif
#  ifdef FG_HAVE_STD_INCLUDES
#    include <cmath>
#  else
#    include <math.h>
#  endif
#else
#  include <cmath>
#endif

#include "FGAircraft.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGAtmosphere.h"
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGFCS.h"
#include "FGPosition.h"
#include "FGAuxiliary.h"
#include "FGOutput.h"

/*******************************************************************************
************************************ CODE **************************************
*******************************************************************************/

FGAircraft::FGAircraft(FGFDMExec* fdmex) : FGModel(fdmex)
{
  int i;

  Name = "FGAircraft";

  for (i=0;i<6;i++) coeff_ctr[i] = 0;
}


FGAircraft::~FGAircraft(void)
{
}

bool FGAircraft::LoadAircraftEx(string aircraft_path, string engine_path, string fname)
{
  string path;
  string fullpath;
  string filename;
  string aircraftDef;
  string tag;
  string holding_string;
  char scratch[128];
  ifstream coeffInFile;

  aircraftDef = aircraft_path + "/" + fname + "/" + fname + ".cfg";
  ifstream aircraftfile(aircraftDef.c_str());
  cout << "Reading Aircraft Configuration File: " << aircraftDef << endl;

  numTanks = numEngines = 0;
  numSelectedOxiTanks = numSelectedFuelTanks = 0;

  while (!aircraftfile.fail()) {
  	holding_string.erase();
    aircraftfile >> holding_string;
    if (holding_string.compare("//",0,2) != 0) {
//    if (holding_string.compare(0, 2, "//") != 0) {

      if (holding_string == "AIRCRAFT") {
      	cout << "Reading in Aircraft parameters ..." << endl;
      } else if (holding_string == "AERODYNAMICS") {
      	cout << "Reading in Aerodynamic parameters ..." << endl;
			} else if (holding_string == "AC_NAME") {
		    aircraftfile >> AircraftName;   // String with no embedded spaces
		    cout << "Aircraft Name: " << AircraftName << endl;
			} else if (holding_string == "AC_WINGAREA") {
				aircraftfile >> WingArea;
				cout << "Aircraft Wing Area: " << WingArea << endl;
			} else if (holding_string == "AC_WINGSPAN") {
				aircraftfile >> WingSpan;
				cout << "Aircraft WingSpan: " << WingSpan << endl;
			} else if (holding_string == "AC_CHORD") {
				aircraftfile >> cbar;
				cout << "Aircraft Chord: " << cbar << endl;
			} else if (holding_string == "AC_IXX") {
				aircraftfile >> Ixx;
				cout << "Aircraft Ixx: " << Ixx << endl;
			} else if (holding_string == "AC_IYY") {
				aircraftfile >> Iyy;
				cout << "Aircraft Iyy: " << Iyy << endl;
			} else if (holding_string == "AC_IZZ") {
				aircraftfile >> Izz;
				cout << "Aircraft Izz: " << Izz << endl;
			} else if (holding_string == "AC_IXZ") {
				aircraftfile >> Ixz;
				cout << "Aircraft Ixz: " << Ixz << endl;
			} else if (holding_string == "AC_EMPTYWT") {
				aircraftfile >> EmptyWeight;
				EmptyMass = EmptyWeight / GRAVITY;
				cout << "Aircraft Empty Weight: " << EmptyWeight << endl;
			} else if (holding_string == "AC_CGLOC") {
				aircraftfile >> Xcg >> Ycg >> Zcg;
				cout << "Aircraft C.G.: " << Xcg << " " << Ycg << " " << Zcg << endl;
			} else if (holding_string == "AC_EYEPTLOC") {
				aircraftfile >> Xep >> Yep >> Zep;
				cout << "Pilot Eyepoint: " << Xep << " " << Yep << " " << Zep << endl;
			} else if (holding_string == "AC_TANK") {
        Tank[numTanks] = new FGTank(aircraftfile);
        switch(Tank[numTanks]->GetType()) {
        case FGTank::ttFUEL:
          numSelectedFuelTanks++;
					cout << "Reading in Fuel Tank #" << numSelectedFuelTanks << " parameters ..." << endl;
          break;
        case FGTank::ttOXIDIZER:
          numSelectedOxiTanks++;
					cout << "Reading in Oxidizer Tank #" << numSelectedOxiTanks << " parameters ..." << endl;
          break;
        }
        numTanks++;

			} else if (holding_string == "AC_ENGINE") {

        aircraftfile >> tag;
				cout << "Reading in " << tag << " Engine parameters ..." << endl;
        Engine[numEngines] = new FGEngine(FDMExec, engine_path, tag, numEngines);
        numEngines++;

			} else if (holding_string == "}") {

			} else if (holding_string == "{") {

			} else if (holding_string == "LIFT") {

				cout << "   Lift Coefficients ..." << endl;
        aircraftfile >> tag;
        streampos gpos = aircraftfile.tellg();
				aircraftfile >> tag;
				if (tag != "}" ) {
					aircraftfile.seekg(gpos);
	        Coeff[LiftCoeff][coeff_ctr[LiftCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
  	      coeff_ctr[LiftCoeff]++;
  	    } else {
  	    	cout << "      None found ..." << endl;
  	    }

			} else if (holding_string == "DRAG") {

				cout << "   Drag Coefficients ..." << endl;
        aircraftfile >> tag;
        streampos gpos = aircraftfile.tellg();
				aircraftfile >> tag;
				if (tag != "}" ) {
					aircraftfile.seekg(gpos);
	        Coeff[DragCoeff][coeff_ctr[DragCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
  	      coeff_ctr[DragCoeff]++;
  	    } else {
  	    	cout << "      None found ..." << endl;
  	    }

			} else if (holding_string == "SIDE") {

				cout << "   Side Coefficients ..." << endl;
        aircraftfile >> tag;
        streampos gpos = aircraftfile.tellg();
				aircraftfile >> tag;
				if (tag != "}" ) {
					aircraftfile.seekg(gpos);
	        Coeff[SideCoeff][coeff_ctr[SideCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
  	      coeff_ctr[SideCoeff]++;
  	    } else {
  	    	cout << "      None found ..." << endl;
  	    }

			} else if (holding_string == "ROLL") {

				cout << "   Roll Coefficients ..." << endl;
        aircraftfile >> tag;
        streampos gpos = aircraftfile.tellg();
				aircraftfile >> tag;
				if (tag != "}" ) {
					aircraftfile.seekg(gpos);
	        Coeff[RollCoeff][coeff_ctr[RollCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
  	      coeff_ctr[RollCoeff]++;
  	    } else {
  	    	cout << "      None found ..." << endl;
  	    }

			} else if (holding_string == "PITCH") {

				cout << "   Pitch Coefficients ..." << endl;
        aircraftfile >> tag;
        streampos gpos = aircraftfile.tellg();
				aircraftfile >> tag;
				if (tag != "}" ) {
					aircraftfile.seekg(gpos);
	        Coeff[PitchCoeff][coeff_ctr[PitchCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
  	      coeff_ctr[PitchCoeff]++;
  	    } else {
  	    	cout << "      None found ..." << endl;
  	    }

			} else if (holding_string == "YAW") {

				cout << "   Yaw Coefficients ..." << endl;
        aircraftfile >> tag;
        streampos gpos = aircraftfile.tellg();
				aircraftfile >> tag;
				if (tag != "}" ) {
					aircraftfile.seekg(gpos);
	        Coeff[YawCoeff][coeff_ctr[YawCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
  	      coeff_ctr[YawCoeff]++;
  	    } else {
  	    	cout << "      None found ..." << endl;
  	    }

			} else {
			}

    } else {
    	aircraftfile.getline(scratch, 127);
    }
  }
	cout << "End of Configuration File Parsing." << endl;
}


bool FGAircraft::Run(void)
{
  if (!FGModel::Run()) {                 // if false then execute this Run()
    GetState();

    for (int i = 0; i < 3; i++)  Forces[i] = Moments[i] = 0.0;

    MassChange();

    FProp(); FAero(); FGear(); FMass();
    MProp(); MAero(); MGear(); MMass();

    PutState();
  } else {                               // skip Run() execution this time
  }
  return false;
}


void FGAircraft::MassChange()
{
  // UPDATE TANK CONTENTS
  //
  // For each engine, cycle through the tanks and draw an equal amount of
  // fuel (or oxidizer) from each active tank. The needed amount of fuel is
  // determined by the engine in the FGEngine class. If more fuel is needed
  // than is available in the tank, then that amount is considered a shortage,
  // and will be drawn from the next tank. If the engine cannot be fed what it
  // needs, it will be considered to be starved, and will shut down.

  float Oshortage, Fshortage;

  for (int e=0; e<numEngines; e++) {
    Fshortage = Oshortage = 0.0;
    for (int t=0; t<numTanks; t++) {
      switch(Engine[e]->GetType()) {
      case FGEngine::etRocket:

        switch(Tank[t]->GetType()) {
        case FGTank::ttFUEL:
          if (Tank[t]->GetSelected()) {
            Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
                                   numSelectedFuelTanks)*(dt*rate) + Fshortage);
          }
          break;
        case FGTank::ttOXIDIZER:
          if (Tank[t]->GetSelected()) {
            Oshortage = Tank[t]->Reduce((Engine[e]->CalcOxidizerNeed()/
                                    numSelectedOxiTanks)*(dt*rate) + Oshortage);
          }
          break;
        }
        break;

      case FGEngine::etPiston:
      case FGEngine::etTurboJet:
      case FGEngine::etTurboProp:

        if (Tank[t]->GetSelected()) {
          Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
                                   numSelectedFuelTanks)*(dt*rate) + Fshortage);
        }
        break;
      }
    }
    if ((Fshortage <= 0.0) || (Oshortage <= 0.0)) Engine[e]->SetStarved();
    else Engine[e]->SetStarved(false);
  }

  Weight = EmptyWeight;
  for (int t=0; t<numTanks; t++)
    Weight += Tank[t]->GetContents();

  Mass = Weight / GRAVITY;
}


void FGAircraft::FAero(void)
{
  float F[3];

  F[0] = F[1] = F[2] = 0.0;

  for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
    for (int ctr=0; ctr < coeff_ctr[axis_ctr]; ctr++)
      F[axis_ctr] += Coeff[axis_ctr][ctr]->TotalValue();

  Forces[0] +=  F[DragCoeff]*cos(alpha)*cos(beta) - F[SideCoeff]*cos(alpha)*sin(beta) - F[LiftCoeff]*sin(alpha);
  Forces[1] +=  F[DragCoeff]*sin(beta)            + F[SideCoeff]*cos(beta);
  Forces[2] +=  F[DragCoeff]*sin(alpha)*cos(beta) - F[SideCoeff]*sin(alpha)*sin(beta) + F[LiftCoeff]*cos(alpha);
}


void FGAircraft::FGear(void)
{
  if (GearUp) {
  } else {
  }
}


void FGAircraft::FMass(void)
{
  Forces[0] += -GRAVITY*sin(tht) * Mass;
  Forces[1] +=  GRAVITY*sin(phi)*cos(tht) * Mass;
  Forces[2] +=  GRAVITY*cos(phi)*cos(tht) * Mass;
}


void FGAircraft::FProp(void)
{
  for (int i=0;i<numEngines;i++) {
    Forces[0] += Engine[i]->CalcThrust();
  }
}


void FGAircraft::MAero(void)
{
	int axis_ctr, ctr;
	
  for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
    for (ctr = 0; ctr < coeff_ctr[axis_ctr+3]; ctr++) {
      Moments[axis_ctr] += Coeff[axis_ctr+3][ctr]->TotalValue();
    }
  }
}


void FGAircraft::MGear(void)
{
  if (GearUp) {
  } else {
  }
}


void FGAircraft::MMass(void)
{
}


void FGAircraft::MProp(void)
{
}


void FGAircraft::GetState(void)
{
  dt = State->Getdt();

  alpha = Translation->Getalpha();
  beta = Translation->Getbeta();
  phi = Rotation->Getphi();
  tht = Rotation->Gettht();
  psi = Rotation->Getpsi();
}


void FGAircraft::PutState(void)
{
}