/*
 *  Copyright (c) 1994, Riley Rainey,  riley@netcon.com
 *
 *  Permission to use, copy, modify and distribute (without charge) this
 *  software, documentation, images, etc. is granted, provided that this 
 *  comment and the author's name is retained.
 *
 *  This software is provided by the author as is, and without any expressed
 *  or implied warranties, including, but not limited to, the implied
 *  warranties of merchantability and fitness for a particular purpose.  In no
 *  event shall the author be liable for any direct, indirect, incidental, or
 *  consequential damages arising in any way out of the use of this software.
 */

#include <math.h>
#include <stdio.h>
#include <Xm/Xm.h>
#include <Xm/LabelG.h>
#include <Xm/TextF.h>

#include "shared.h"
#include "cell.h"
#include "CrExFormDlg.h"

#define dialog_IMPORT
#include "dialog.h"

void
balance (struct balance_data *s)
{

	double  theta, cosTheta, sinTheta;
	double  Fmz;

/*
 *  Determine the rest pitch angle of the aircraft body
 */

	theta = - atan2 (s->rn.z - s->rm.z, s->rn.x - s->rm.x);
	cosTheta = cos(theta);
	sinTheta = sin(theta);
	s->theta = theta;

/*
 *  Determine correct rm/rn values
 */
	s->rn.z = s->rn.z - s->Gn - s->cn;
	s->rm.z = s->rm.z - s->Gm - s->cm;

/*
 *  Determine spring constants
 */

	Fmz = (s->weight * cosTheta * s->rn.x) / (s->rm.x - s->rn.x);

	s->Km = - Fmz * s->cm;
	s->Kn = (- Fmz - s->weight * cosTheta) * - s->cn;

/*
 *  Determine the initial grounding point
 */

	s->Gpz = s->rm.x * sinTheta + (s->rm.z + s->Gm + s->cm) * cosTheta;

}


enum {
	InfoName,
	InfoEmptyWeight,
	InfoIxx,
	InfoIyy,
	InfoIzz,
	InfoWingSpan,
	InfoWingArea,
	InfoWingChord,

	GearGm,
	GearcmMax,
	GearDm,
	GearKm,
	GearGn,
	GearcnMax,
	GearDn,
	GearKn,

	DerivClda,
	DerivCldr,
	DerivClp,
	DerivCmq,
	DerivCnr,
	DerivCmAlpha,
	DerivCmFactor,

	PowerplantFuel,
	PowerplantThrust,
	PowerplantABThrust,
	PowerplantLag,
	PowerplantSpFuelConsump,
	PowerplantSpABFuelConsump
	};

struct cell info_dialog_items[] = {
	{ InfoName,     "Aircraft Name",
	  "Unknown", 10, NULL, NULL
	},
	{ InfoEmptyWeight,      "Empty Weight [lbs]",
	  "10000", 10, NULL, NULL
	},
	{ InfoIxx,      "Ixx [slug*ft^2]",
	  "5000", 10, NULL, NULL
	},
	{ InfoIyy,      "Iyy [slug*ft^2]",
	  "50000", 10, NULL, NULL
	},
	{ InfoIzz,      "Izz [slug*ft^2]",
	  "50000", 10, NULL, NULL
	},
	{ InfoWingSpan, "WingSpan [ft]",
	  "50000", 10, NULL, NULL
	},
	{ InfoWingArea, "Wing Area [ft]",
	  "50000", 10, NULL, NULL
	},
	{ InfoWingChord, "Wing Chord [ft]",
	  "50000", 10, NULL, NULL
	},
	{ -1, NULL, NULL, 0, NULL, NULL }
	};

struct cell gear_dialog_items[] = {
	{
	  GearGm,       "Main Gear Assembly Length (Gm) [ft]",
	  "2.0", 10, NULL, NULL
	},
	{
	  GearcmMax,    "Main Gear Max Extension (cmMax) [ft]",
	  "1.0", 10, NULL, NULL
	},
	{
	  GearDm,       "Main Gear Damping Factor (Dm) [lb/ft/sec]",
	  "2.0", 10, NULL, NULL
	},
	{
	  GearKm, "Main Gear Spring Factor (Km) [*]",
	  "0.0", 10, NULL, NULL
	},
	{
	  GearGn, "Nose Gear Assembly Length (Gn) [ft]",
	  "2.0", 10, NULL, NULL
	},
	{
	  GearcnMax, "Nose Gear Max Extension (cnMax) [ft]",
	  "1.0", 10, NULL, NULL
	},
	{
	  GearDn, "Nose Gear Damping Factor (Dn) [lb/ft/sec]",
	  "2.0", 10, NULL, NULL
	},
	{
	  GearKn, "Nose Gear Spring Factor (Kn) [*]",
	  "0.0", 10, NULL, NULL
	},
	{ -1, NULL, NULL, 0, NULL, NULL }
};
	
struct cell deriv_dialog_items[] = {
	{
	  DerivClda,    "Roll moment from aileron offset",
	  "0.03", 10, NULL, NULL
	},
	{
	  DerivCldr,    "Roll moment from rudder offset",
	  "0.003", 10, NULL, NULL
	},
	{
	  DerivClp,     "Roll damping",
	  "-0.3", 10, NULL, NULL
	},
	{
	  DerivCmq,     "Pitch damping",
	  "-8.0", 10, NULL, NULL
	},
	{
	  DerivCnr,     "Yaw damping",
	  "-2.0", 10, NULL, NULL
	},
	{
	  DerivCmAlpha, "Pitch due to angle of attack",
	  "-0.3", 10, NULL, NULL
	},
	{
	  DerivClda,    "Pitch due to angle of attack [stalled]",
	  "-0.2", 10, NULL, NULL
	},
	{ -1, NULL, NULL, 0, NULL, NULL }
	};

struct cell pwr_dialog_items[] = {
	{
	  PowerplantFuel,       "Internal Fuel Capacity [lbs]",
	  "1000", 10, NULL, NULL
	},
	{
	  PowerplantThrust,     "Military Thrust [lbs]",
	  "10000", 10, NULL, NULL
	},
	{
	  PowerplantABThrust,   "Afterburner Thrust [lbs]",
	  "10000", 10, NULL, NULL
	},
	{
	  PowerplantLag,        "Engine response lag [negative value]",
	  "-3.0", 10, NULL, NULL
	},
	{
	  PowerplantSpFuelConsump,
	  "Specific fuel consumption, afterburner off [lb fuel/lb T * hr]",
	  "0.7", 10, NULL, NULL
	},
	{
	  PowerplantSpABFuelConsump,
	  "Specific fuel consumption, afterburner on [lb fuel/lb T * hr]",
	  "2.50", 10, NULL, NULL
	},
	{ -1, NULL, NULL, 0, NULL, NULL }
	};





static Widget
CreatePanelDialog (char *name, Widget parent, struct cell *p, int count, int calc_fn, int cancel)
{
	Arg     args[10];
	char    s1[32], s2[32];
	static ActionAreaButton action_items[] = {
		{ "Calculate",  MenuCB, (XtPointer) NULL },
		{ "Dismiss",    MenuCB, (XtPointer) NULL },
		{ "Help",       NULL,   NULL    },
	};
	Widget  dialog, form;
	int     i, n;
	XmString s;
	
	count --;
	
	/*
	 * Nasty cast here to assign int param to a 32 or 64 bits pointer
	 * preventing gcc -Wall complains :-)
	 */
	action_items[0].client_data = (XtPointer) shared_IntToPtr(calc_fn);
	action_items[1].client_data = (XtPointer) shared_IntToPtr(cancel);

	dialog = CreateExtendedFormDialog (name, parent, &form, action_items,
		XtNumber (action_items), 0);

	for (i=0; i<count; ++i) {

		sprintf (s1, "label_%d", i+1);
		sprintf (s2, "field_%d", i+1);

		s = XmStringCreateSimple (p[i].prompt);

		n = 0;
		XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION);  n++;
		XtSetArg (args[n], XmNrightPosition, 70); n++;
		XtSetArg (args[n], XmNlabelString, s); n++;
		if (i == 0) {
		    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM);  n++;
		}
		else {
		    XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET);  n++;
		    XtSetArg (args[n], XmNtopWidget, p[i-1].field);  n++;
		}

		p[i].label = XtCreateManagedWidget (s1, xmLabelGadgetClass,
			form, args, n);

		n = 0;
		XtSetArg (args[n], XmNcolumns, p[i].field_width);  n++;
		XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION);  n++;
		XtSetArg (args[n], XmNleftPosition, 70); n++;
		if (i == 0) {
		    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM);  n++;
		}
		else {
		    XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET);  n++;
		    XtSetArg (args[n], XmNtopWidget, p[i-1].field);  n++;
		}

		p[i].field = XtCreateManagedWidget (s2, xmTextFieldWidgetClass,
			form, args, n);

		XmTextFieldSetString (p[i].field,  p[i].initial_value);

	}

	XtManageChild (form);
	return dialog;
}


Widget
CreateInfoDialog(Widget parent)
{
	return CreatePanelDialog("info_dialog", parent, info_dialog_items,
		XtNumber(info_dialog_items), MENU_INFO_CALCULATE,
		MENU_INFO_CANCEL);
}

Widget
CreateGearDialog(Widget parent)
{
	return CreatePanelDialog("gear_dialog", parent, gear_dialog_items,
		XtNumber(gear_dialog_items), MENU_GEAR_CALCULATE,
		MENU_GEAR_CANCEL);
}

Widget
CreateDerivDialog(Widget parent)
{
	return CreatePanelDialog("stability_dialog", parent, deriv_dialog_items,
		XtNumber(deriv_dialog_items), MENU_DERIV_CALCULATE,
		MENU_DERIV_CANCEL);
}

Widget
CreatePowerplantDialog(Widget parent)
{
	return CreatePanelDialog("powerplant_dialog", parent, pwr_dialog_items,
		XtNumber(pwr_dialog_items), MENU_PWR_CALCULATE,
		MENU_PWR_CANCEL);
}

void
InitializeDialogs(craftType *c)
{
	SetCellValueString (info_dialog_items, InfoName, craft_name);
	SetCellValueDouble (info_dialog_items, InfoEmptyWeight,
		c->emptyWeight);
	SetCellValueDouble (info_dialog_items, InfoIxx, c->I.m[0][0]);
	SetCellValueDouble (info_dialog_items, InfoIyy, c->I.m[1][1]);
	SetCellValueDouble (info_dialog_items, InfoIzz, c->I.m[2][2]);
	SetCellValueDouble (info_dialog_items, InfoWingSpan, c->wings * 2.0);
	SetCellValueDouble (info_dialog_items, InfoWingArea, c->wingS);
	SetCellValueDouble (info_dialog_items, InfoWingChord, c->c);

	SetCellValueDouble (gear_dialog_items, GearGm, c->Gm);
	SetCellValueDouble (gear_dialog_items, GearcmMax, c->cmMax);
	SetCellValueDouble (gear_dialog_items, GearDm, c->Dm);
	SetCellValueDouble (gear_dialog_items, GearKm, c->Km);
	SetCellValueDouble (gear_dialog_items, GearGn, c->Gn);
	SetCellValueDouble (gear_dialog_items, GearcnMax, c->cnMax);
	SetCellValueDouble (gear_dialog_items, GearDn, c->Dn);
	SetCellValueDouble (gear_dialog_items, GearKn, c->Kn);

	SetCellValueDouble (pwr_dialog_items, PowerplantFuel,
		c->maxFuel);
	SetCellValueDouble (pwr_dialog_items, PowerplantThrust,
		c->maxThrust);
	SetCellValueDouble (pwr_dialog_items, PowerplantABThrust,
		c->maxABThrust);
	SetCellValueDouble (pwr_dialog_items, PowerplantLag,
		c->engineLag);
	SetCellValueDouble (pwr_dialog_items, PowerplantSpFuelConsump,
		c->spFuelConsump);
	SetCellValueDouble (pwr_dialog_items, PowerplantSpABFuelConsump,
		c->spABFuelConsump);
}

void
GetCraftInfo(craftType *c)
{
	GetCellValueString (info_dialog_items, InfoName, craft_name);
	GetCellValueDouble (info_dialog_items, InfoEmptyWeight,
		&c->emptyWeight);
	GetCellValueDouble (info_dialog_items, InfoIxx, &c->I.m[0][0]);
	GetCellValueDouble (info_dialog_items, InfoIyy, &c->I.m[1][1]);
	GetCellValueDouble (info_dialog_items, InfoIzz, &c->I.m[2][2]);
	GetCellValueDouble (info_dialog_items, InfoWingSpan, &c->wings);
	c->wings /= 2.0;
	GetCellValueDouble (info_dialog_items, InfoWingArea, &c->wingS);
	GetCellValueDouble (info_dialog_items, InfoWingChord, &c->c);

	GetCellValueDouble (gear_dialog_items, GearGm, &c->Gm);
	GetCellValueDouble (gear_dialog_items, GearcmMax, &c->cmMax);
	GetCellValueDouble (gear_dialog_items, GearDm, &c->Dm);
	GetCellValueDouble (gear_dialog_items, GearKm, &c->Km);
	GetCellValueDouble (gear_dialog_items, GearGn, &c->Gn);
	GetCellValueDouble (gear_dialog_items, GearcnMax, &c->cnMax);
	GetCellValueDouble (gear_dialog_items, GearDn, &c->Dn);
	GetCellValueDouble (gear_dialog_items, GearKn, &c->Kn);

	GetCellValueDouble (pwr_dialog_items, PowerplantFuel,
		&c->maxFuel);
	GetCellValueDouble (pwr_dialog_items, PowerplantThrust,
		&c->maxThrust);
	GetCellValueDouble (pwr_dialog_items, PowerplantABThrust,
		&c->maxABThrust);
	GetCellValueDouble (pwr_dialog_items, PowerplantLag,
		&c->engineLag);
	GetCellValueDouble (pwr_dialog_items, PowerplantSpFuelConsump,
		&c->spFuelConsump);
	GetCellValueDouble (pwr_dialog_items, PowerplantSpABFuelConsump,
		&c->spABFuelConsump);
	
	c->viewPoint =  marker_list[MARKER_HEAD].location.point;

}

void
GearCalculate(craftType *c)
{
	struct balance_data b;
			
	b.weight = c->emptyWeight + c->maxFuel;
	b.rm = marker_list[MARKER_MAIN_GEAR].location.point;
	b.rn = marker_list[MARKER_NOSE_GEAR].location.point;
	b.cm = GetCellValueDouble (gear_dialog_items, GearcmMax, &c->cmMax);
	b.cm *= 0.6;
	b.cn = GetCellValueDouble (gear_dialog_items, GearcnMax, &c->cnMax);
	b.cn *= 0.6;
	b.Gm = GetCellValueDouble (gear_dialog_items, GearGm, &c->Gm);
	b.Gn = GetCellValueDouble (gear_dialog_items, GearGn, &c->Gn);

#ifdef notdef
	printf ("Input:\n");
	printf ("nose   contact = %lf  %lf  %lf\n", b.rn.x, b.rn.y, b.rn.z);
	printf ("main's contact = %lf  %lf  %lf\n", b.rm.x, b.rm.y, b.rm.z);
	printf ("Weight = %lf\n", b.weight);
#endif  

	balance (&b);

#ifdef notdef
	printf ("\nOutput:\n");
	printf ("rm = %lf,  %lf,  %lf\n", b.rm.x, b.rm.y, b.rm.z);
	printf ("rn = %lf,  %lf,  %lf\n", b.rn.x, b.rn.y, b.rn.z);
	printf ("Km = %lf\n", b.Km);
	printf ("Kn = %lf\n", b.Kn);
	printf ("Grounding point (z) = %lf\n", b.Gpz);
	printf ("\n\"inventory\" form:\n\n");
	printf ("\tRm\t\t{%lg,  %lg,  %lg}\n", b.rm.x, b.rm.y, b.rm.z);
	printf ("\tRn\t\t{%lg,  %lg,  %lg}\n", b.rn.x, b.rn.y, b.rn.z);
	printf ("\tKm\t\t%lg\n", b.Km);
	printf ("\tKn\t\t%lg\n", b.Kn);
	printf ("\tGm\t\t%lg\n", b.Gm);
	printf ("\tGn\t\t%lg\n", b.Gn);
	printf ("\tCmMax\t\t%lg\n", c->cmMax);
	printf ("\tCnMax\t\t%lg\n", c->cnMax);
	printf ("\tGroundingPoint\t{0.0, 0.0, %lg}\n", b.Gpz);
#endif

	c->rm = b.rm;
	c->rn = b.rn;
	c->Km = b.Km;   
	c->Kn = b.Kn;
	//c->groundingPoint.x = c->groundingPoint.y = 0.0;
	//c->groundingPoint.z = b.Gpz;
	SetCellValueDouble (gear_dialog_items, GearKm, c->Km);
	SetCellValueDouble (gear_dialog_items, GearKn, c->Kn);
}
	
	
