/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  Riley Rainey
 *
 *  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; version 2 dated June, 1991.
 *
 *  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., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

#include <math.h>
#include <stdio.h>
#include <string.h>
#include "alarm.h"
#include "damage.h"
#include "gear.h"
#include "inventory.h"
#include "pm.h"
#include "terrain.h"
#include "weapon.h"
#include "../util/error.h"
#include "../util/units.h"

#define planes_IMPORT
#include "planes.h"


static double planes_noEngineThrust(craft *c)
{
	c->rpm = 0.0;
	return 0.0;
}


/**
 * Generic piston engine performances with altitude.
 * The formula has been estimated from the reference below, in particular from
 * table 1 page 19 from which I deduced the following relation:
 * 
 *    P[h]/P[0] = pow(rho[h]/rho[0], 1.6)
 * 
 * where P[h] is the maximum power at the given altitude and rho[h] is the
 * air density at the given altitude. As stated in the conclusions of the
 * reference, the effects of the air temperature are minimal, at least on
 * the altitude range of a small piston engine like our C-172RG, so the air
 * temperature is here ignored.
 * 
 * Performances were measured on a small 2-strokes engine on a simulated
 * environment. I don't know how much these data are representative of a real
 * piston engine on a real airplane.
 * 
 * Reference:
 * Travis Don Husaboe, Effects of temperature on the performance of a small
 * internal combustion engine at altitude", thesis at Department of the Air
 * Force, Air University, Ohio, March 2013.
 * @param c
 * @return Thrust (lbf).
 */
static double planes_genericPistonEngineThrust(craft *c)
{
	if ( c->fuel > 0.0 )
		c->throttle = c->throttleComm;
	else
		c->throttle = 0;
	double ts = c->throttle / 32768.0;
	c->rpm = (c->rpm - ts) * exp(deltaT / c->cinfo->engineLag) + ts;
	return c->rpm
		* interpolate_value(c->cinfo->Thrust, c->mach) * c->cinfo->maxThrust
		* pow(c->air.rho / units_RHO_0, 1.6);
}


static double planes_genericJetEngineThrust(craft *c)
{
	double t, ts;

	if (c->flags & FL_AFTERBURNER) {
		t = interpolate_value(c->cinfo->ABThrust, c->mach) * c->cinfo->maxABThrust;
	}
	else {
		t = interpolate_value(c->cinfo->Thrust, c->mach) * c->cinfo->maxThrust;
	}

	if ( c->fuel > 0.0 )
		c->throttle = c->throttleComm;
	else
		c->throttle = 0;
	ts = c->throttle / 32768.0;
	c->rpm = (c->rpm - ts) * exp(deltaT / c->cinfo->engineLag) + ts;

	return t * c->rpm * c->rpm * c->air.rho / units_RHO_0;
}


static double planes_genericRocketEngineThrust(craft *c)
{
	if ( c->fuel > 0.0 )
		c->throttle = c->throttleComm;
	else
		c->throttle = 0;
	double ts = c->throttle / 32768.0;
	c->rpm = (c->rpm - ts) * exp(deltaT / c->cinfo->engineLag) + ts;
	if( c->rpm < 0 )
		c->rpm = 0;
	else if( c->rpm > 1 )
		c->rpm = 1;
	return c->rpm * c->cinfo->maxThrust;
}


inventory_ThrustCalculator * planes_getThrustCalculator(craftType * cinfo)
{
	switch(cinfo->engineType){
	case inventory_NoEngine:  return planes_noEngineThrust;
	case inventory_GenericPistonEngine: return planes_genericPistonEngineThrust;
	case inventory_GenericJetEngine: return planes_genericJetEngineThrust;
	case inventory_GenericRocketEngine: return planes_genericRocketEngineThrust;
	default: error_internal("unknown engine type: %d", cinfo->engineType);
	}
}

double
planes_fuelUsed( craft * c )
{
	double    spFuelConsump;

	if (c->flags & FL_AFTERBURNER) {
		spFuelConsump = c->cinfo->spABFuelConsump;
	}
	else {
		spFuelConsump = c->cinfo->spFuelConsump;
	}
	return spFuelConsump * c->curThrust * deltaT / 3600.0;
}

void
planes_genericResupply(craft * c)
{
	int i;

	if( c->type == CT_DRONE ){
		/* half refueling for drones: */
		c->fuel = 0.5 * c->cinfo->maxFuel;
#ifdef FIXME_NOT_DEFINED
	} else {
		/* +10% every 30 s while on ground: */
		c->fuel += c->cinfo->maxFuel / 10.0;
		if ( c->fuel > c->cinfo->maxFuel )
			c->fuel = c->cinfo->maxFuel;
#endif
	}

	for (i = 0; i < c->cinfo->sCount; i++) {
		c->station[i] = c->cinfo->station[i];
	}
	damage_reset(c);
}


void planes_doResupply(void *arg1, void *arg2)
{
	craft    *c;
	int       i;
	double    d;

	for ((i = 0, c = &ptbl[0]); i < manifest_MAXPLAYERS; (++i, ++c)) {

		if (c->type != CT_PLANE)
			continue;

		if (VMagnitude(&c->Cg) < 5.0) {
			d = 0.0;
			if (d <= manifest_MAX_GROUND_DISTANCE)
				(*c->cinfo->resupply) (c);
			/* ignore = */ weapon_selectByName(c, c->curWeapon);
		}
	}

	/* id = */ alarm_add(manifest_RESUPPLY_INTERVAL, planes_doResupply, NULL, NULL);
}


int planes_newPlane(char *planeType)
{
	int       i;
	craft    *c;

	for (i = 0; i < manifest_MAXPLAYERS; ++i) {
		if (ptbl[i].type == CT_FREE || ptbl[i].type == CT_RESERVED) {
			break;
		}
	}

	if (i == manifest_MAXPLAYERS)
		return -1;

	c = &ptbl[i];
	if ((c->cinfo = inventory_craftTypeSearchByZoneAndName(NULL, planeType)) == NULL) {
		return -2;
	}

	c->type = CT_PLANE;
	c->Cg.x = 0.0;
	c->Cg.y = 0.0;
	c->Cg.z = 0.0;

	/*
		Ensure terrain_local_altitude() will update the local altitude at the
		first call
	*/

	c->terrain_altitude_timeout = curTime - 10.0;
	/* c->terrain_altitude = UNKNOW; */

	c->p = c->q = c->r = 0.0;
	c->pitchComm = c->rollComm = c->steerComm = c->rudderComm = 0.0;
	c->Se = c->Sr = c->Sa = 0.0;
	c->SeTrim = c->SaTrim = 0.0;

	c->throttleComm = 0.20 * 32768.0; /* 20% */
	c->throttle = 0;
	c->curThrust = (c->cinfo->thrust) (c);
	c->rpm = (double) c->throttle / 32768.0;
	c->thrust_reverse_on = FALSE;
	c->thrust_reverse_pos = 1.0;

	c->curFlap = 0.0;
	c->flapSetting = 0.0;
	c->curSpeedBrake = 0.0;
	c->speedBrakeSetting = 0.0;
	c->curHeading = c->curPitch = c->curRoll = 0.0;
	VIdentMatrix(&(c->trihedral));
	c->flags = 0;
	c->curRadarTarget = -1;
	pm_hud_strings_alloc(c);

	/* AutoPilot System (automatically allocated by aps.c): */
	c->aps = NULL;

	/* Magnetic VAR handling: */
	c->showMag = 0;
	c->actualLocalVAR = 0.0;
	c->updActualMagneticField = 0.0;
	c->indicatedLocalVAR = 0.0;
	c->updIndicatedMagneticField = 0.0;
	c->radarMode = RM_OFF;

	gear_allocate(c);
	gear_down(c);

/*
 *  rearm and fuel the aircraft.
 */

	(*c->cinfo->resupply) (c);

	c->w.z = 0.0; /* sea level -- caller must set properly */

	/* Set c->curWeapon: */
	weapon_selectByName(c, weapon_NULL);

	return i;
}
