/*
 * $Id: output.c,v 2.10 89/11/11 19:09:29 tynor Exp $
 *----------------------------------------------------------------------------
 *	FPLAN - Flight Planner
 *	Steve Tynor
 *	tynor@prism.gatech.edu
 *
 *	This program is in the public domain. Permission to copy,
 * distribute, modify this program is hearby given as long as this header
 * remains. If you redistribute this program after modifying it, please
 * document your changes so that I do not take the blame (or credit) for
 * those changes.  If you fix bugs or add features, please send me a
 * patch so that I can keep the 'official' version up-to-date.
 *
 *	Bug reports are welcome and I'll make an attempt to fix those
 * that are reported.
 *
 *	USE AT YOUR OWN RISK! I assume no responsibility for any
 * errors in this program, its database or documentation. I will make an
 * effort to fix bugs, but if you crash and burn because, for example,
 * fuel estimates in this program were inaccurate, it's your own fault
 * for trusting somebody else's code! Remember, as PIC, it's _your_
 * responsibility to do complete preflight planning. Use this program as
 * a flight planning aid, but verify its results before using them.
 *----------------------------------------------------------------------------
 */

static char rcsid[] = "$Id: output.c,v 2.10 89/11/11 19:09:29 tynor Exp $";

#include <math.h>
#include <stdio.h>
#include "mystring.h"
#include "wp_info.h"

extern double decimal_2_degrees_mins();

typedef enum {
   END_OF_FORMAT,	/* we count on ((int)END_OF_FORMAT == 0) */
   CITY, NAME, COMMENT, ELEV, FREQ, TYPE, DESIG, 
   TC, MC, MH, WIND, DIST_LEG, DIST, DIST_REMAIN,
   ETA_LEG, ETA, ATA, FUEL_AMT, FUEL_RATE, FUEL_LEG, ALT,
   TAS, EGS, AGS, VOR_DESIG, VOR_FREQ, VOR_RADIAL, LAT_LONG, VOR_DME,
   IGNORE, EMPTY, FORWARD_ARROW, BACKWARD_ARROW, 
   SEPARATOR, BAR, CROSS, EOL, END_OF_VOR,
   } FORMAT_KIND;

char *headers[] = 
{
   "", "CITY", "CHECKPOINT", "COMMENT", "ELEV", "FREQ", "TYPE", "DESIG", 
   "TC", "MC", "MH", "WIND", "DIST", "DIS", "REM",
   "ETE", "ETE", "ATE", "FUEL", "RATE", "FUEL", "ALT",
   "TAS", "EGS", "AGS", "VOR", "FREQ", "RAD", "LAT/LONG", "DME", "", "",
};

typedef enum {R, L} JUSTIFICATION;

typedef enum {U,S} UNSHIFTED_OR_SHIFTED;

typedef struct {
   FORMAT_KIND	 	kind;
   int		 	field_width;
   JUSTIFICATION 	justification;
   int		 	arg;	/* which_vor or num_decimal_points */
   UNSHIFTED_OR_SHIFTED	shifted;
} FORMAT_SPEC_CELL;

/*
 * default format (for narrow output (e.g. to terminals) the VORs are not 
 * output)
 *
 *                                                            VOR1    VOR2
 *+=========//==============+===+=====+                            +=======+
 *|NAME     //    TYPE DESIG|   | FUEL|                            |    VOR|
 *|CITY     //      ALT FREQ|DIS|  ETE|======+===+====+====+=======|   FREQ|
 *|COMMENT  //      LAT/LONG|REM|  ATE|    MC|TAS|DIST| ALT|    VOR|RAD DME|
 *+=========//==============+===+=====>  WIND|EGS| ETE|RATE|   FREQ<-------+
 *|         //              |   |     |    MH|AGS| ATE|FUEL|RAD DME|       |
 *|         //              |   |     |======+===+====+====+=======|       |
 *|         //              |   |     |      |   |    |    |       |       |
 *+---------//--------------+---+----->      |   |    |    |       <-------+
 *|         //              |   |     |      |   |    |    |       |       |
 *|         //              |   |     |------+---+----+----+-------|       |
 */

#define MAX_NUM_FIELDS 170

FORMAT_SPEC_CELL default_format_wide [] = 
{
   /* line 0 */
   /* WAYPOINT INFO */
   {CROSS},{SEPARATOR,44,L},{CROSS},
   {SEPARATOR,3}, {CROSS}, {SEPARATOR,5},{FORWARD_ARROW},

   /* LEG INFO */
   {WIND,6,R,0,S},{BAR,1,R,0,S},{EGS,3,R,0,S},{BAR,1,R,0,S},
   {ETA_LEG,4,R,0,S},{BAR,1,R,0,S},{FUEL_RATE,5,R,1,S},{BAR,1,R,0,S},
   {END_OF_VOR,1,R,0,U},{VOR_FREQ,8,R,0,S},{BACKWARD_ARROW,1,R,0,U},
   /* WAYPOINT INFO */
   {END_OF_VOR,1,R,1,U},{SEPARATOR,8,R,1,U},{CROSS},
   {END_OF_VOR,1,R,2,U},{SEPARATOR,8,R,2,U},{CROSS},
   {END_OF_VOR,1,R,3,U},{SEPARATOR,8,R,3,U},{CROSS},
   {END_OF_VOR,1,R,4,U},{SEPARATOR,8,R,4,U},{CROSS},
   {END_OF_VOR,1,R,5,U},{SEPARATOR,8,R,5,U},{CROSS},
   {END_OF_VOR,1,R,6,U},{SEPARATOR,8,R,6,U},{CROSS},
   {END_OF_VOR,1,R,7,U},{SEPARATOR,8,R,7,U},{CROSS},
   {EOL}, 

   /* line 1 */
   /* WAYPOINT INFO */
   {BAR},{NAME,32,L},{ELEV,5},{EMPTY,1},{DESIG,6},{BAR},
   {EMPTY,3}, {BAR}, {FUEL_AMT,5,R,1},{BAR},
   /* LEG INFO */
   {MH,6,R,0,S},{BAR,1,R,0,S},{AGS,3,R,0,S},{BAR,1,R,0,S},
   {ATA,4,R,0,S},{BAR,1,R,0,S},{FUEL_LEG,5,R,1,S},{BAR,1,R,0,S},
   {VOR_RADIAL,4,L,0,S},
   {VOR_DME,4,R,0,S},{BAR,1,R,0,U},
   /* WAYPOINT INFO */
   {VOR_DESIG,8,R,1,U},{BAR},
   {VOR_DESIG,8,R,2,U},{BAR},
   {VOR_DESIG,8,R,3,U},{BAR},
   {VOR_DESIG,8,R,4,U},{BAR},
   {VOR_DESIG,8,R,5,U},{BAR},
   {VOR_DESIG,8,R,6,U},{BAR},
   {VOR_DESIG,8,R,7,U},{BAR},
   {EOL}, 

   /* line 2 */
   /* WAYPOINT INFO */
   {BAR},{CITY,29,L},{TYPE,8},{EMPTY,1},{FREQ,6},{BAR},
   {DIST,3}, {BAR},{ETA,5},{BAR},
   /* LEG INFO */
   {SEPARATOR,6,R,0,U},{CROSS,1,R,0,U},{SEPARATOR,3,R,0,U},{CROSS,1,R,0,U},
   {SEPARATOR,4,R,0,U},{CROSS,1,R,0,U},{SEPARATOR,5,R,0,U},{CROSS,1,R,0,U},
   {END_OF_VOR,1,R,0,U},{SEPARATOR,8,R,0,U},{BAR,1,R,0,U},
   /* WAYPOINT INFO */
   {VOR_FREQ,8,R,1,U},{BAR},
   {VOR_FREQ,8,R,2,U},{BAR},
   {VOR_FREQ,8,R,3,U},{BAR},
   {VOR_FREQ,8,R,4,U},{BAR},
   {VOR_FREQ,8,R,5,U},{BAR},
   {VOR_FREQ,8,R,6,U},{BAR},
   {VOR_FREQ,8,R,7,U},{BAR},
   {EOL}, 

   /* line 3 */
   /* WAYPOINT INFO */
   {BAR},{COMMENT,26,L},{LAT_LONG,18},{BAR},
   {DIST_REMAIN,3}, {BAR},{ATA,5},{BAR},
   /* LEG INFO */
   {MC,6,R,0,S},{BAR,1,R,0,S},{TAS,3,R,0,S},{BAR,1,R,0,S},
   {DIST_LEG,4,R,0,S},{BAR,1,R,0,S},{ALT,5,R,0,S},{BAR,1,R,0,S},
   {VOR_DESIG,8,R,0,S},{BAR,1,R,0,U},
   /* WAYPOINT INFO */
   {VOR_RADIAL,4,L,1,U}, {VOR_DME,4,R,1,U},{BAR},
   {VOR_RADIAL,4,L,2,U}, {VOR_DME,4,R,2,U},{BAR},
   {VOR_RADIAL,4,L,3,U}, {VOR_DME,4,R,3,U},{BAR},
   {VOR_RADIAL,4,L,4,U}, {VOR_DME,4,R,4,U},{BAR},
   {VOR_RADIAL,4,L,5,U}, {VOR_DME,4,R,5,U},{BAR},
   {VOR_RADIAL,4,L,6,U}, {VOR_DME,4,R,6,U},{BAR},
   {VOR_RADIAL,4,L,7,U}, {VOR_DME,4,R,7,U},{BAR},
   {EOL}, 
   {END_OF_FORMAT}
};

#ifdef OLD_FORMAT
/*
 * Default wide column format:
 *
+-------------//--------------+------+---+----+-----+-----+-----+-----+
|NAME         //    TYPE DESIG|    MC|TAS| LEG|  LEG|  ALT|  VOR|  VOR|
|CITY         //      ALT FREQ|  WIND|EGS|DIST|  ETA| RATE| FREQ| FREQ|
|COMMENT      //      LAT/LONG|    MH|AGS| REM|  ATA| FUEL|  RAD|  RAD|
+-------------//--------------+------+---+----+-----+-----+-----+-----+
 *
 * narrow column format is the same - without the last 2 fields
 */

FORMAT_SPEC_CELL default_format_wide [MAX_NUM_FIELDS] = 
{
   {BAR},{NAME,33,L},{TYPE,11},{EMPTY,1},{DESIG,6},{BAR},
   {MC,6},{BAR},{TAS,3},{BAR},
   {DIST_LEG,4},{BAR},{ETA_LEG,5},{BAR},{ALT,5},{BAR},
   {VOR_DESIG,6,R,0},{BAR},{VOR_DESIG,6,R,1},{BAR},
   {EOL}, 

   {BAR},{CITY,33,L},{ELEV,11},{EMPTY,1},{FREQ,6,R,1},{BAR},
   {WIND,6},{BAR},{EGS,3},{BAR},
   {DIST,4},{BAR},{ETA,5},{BAR},{FUEL_RATE,5,R,1},{BAR},
   {VOR_FREQ,6,R,0},{BAR},{VOR_FREQ,6,R,1},{BAR},
   {EOL},

   {BAR},{COMMENT,33,L},{LAT_LONG,18},
   {BAR},{MH,6},{BAR},{AGS,3},{BAR},
   {DIST_REMAIN,4},{BAR},{ATA,5},{BAR},{FUEL_AMT,5,R,1},{BAR},
   {VOR_RADIAL,6,R,0},{BAR},{VOR_RADIAL,6,R,1},{BAR},
   {EOL},

   {END_OF_FORMAT}
};
#endif

FORMAT_SPEC_CELL output_format [MAX_NUM_FIELDS];

int line_num;

enum {NAUTICAL, STATUTE} output_units = NAUTICAL;
/*
 * the only things affected by the output_format are:
 * speeds (TAS, GS) and distance (DIST, DIST_LEG, DIST_REMAIN)
 * All are internally stored as knots and km.
 */
#define NM_OR_MI(a) ((output_units == NAUTICAL) ? (a) : (a) * MI_PER_NM)

#define EPSON_CROSS		197
#define EPSON_VERTICAL_BAR	179
#define EPSON_HORIZONTAL_BAR	196
#define EPSON_FORWARD_ARROW	180
#define EPSON_BACKWARD_ARROW	195
static BOOLEAN use_epson_box_chars = FALSE;

/*----------------------------------------------------------------------------*/
void set_output_units (nautical)
     int nautical;
{
   if (nautical)
      output_units = NAUTICAL;
   else
      output_units = STATUTE;
}

/*----------------------------------------------------------------------------*/
void set_format (max_nav, epson)
     int max_nav;
     BOOLEAN epson;
{
   int i;
   FORMAT_SPEC_CELL *spec = default_format_wide;

   use_epson_box_chars = epson;
   for (i = 0; i < MAX_NUM_FIELDS; i++) {
      if (((spec[i].kind == END_OF_VOR) ||
	   (spec[i].kind == VOR_DESIG) ||
	   (spec[i].kind == VOR_FREQ) ||
	   (spec[i].kind == VOR_DME) ||
	   (spec[i].kind == VOR_RADIAL))
	  && 
	  (max_nav < spec[i].arg)) {
	 for (; (spec[i].kind != EOL) && (i < MAX_NUM_FIELDS); i++) {
	    output_format[i].kind = IGNORE;
	 }
	 i--;
      } else {
	 output_format[i] = spec[i];
      }
   }
#if 0
   output_format[i] = spec[i];
#endif
}

/*----------------------------------------------------------------------------*/
static void put_time (out_fp, field_width, hours)
     FILE   *out_fp;
     int    field_width;
     double hours;
{
   char   buffer[30];
   int    h       = (int) hours;
   double minutes = hours * 60.0;

   if (h > 0) {
      sprintf (buffer, "%d:%02.0f", h, minutes - h * 60.0);
      fprintf (out_fp, "%*s", field_width, buffer);
   } else {
      fprintf (out_fp, "%*.0f", field_width, minutes);
   }
}
/*----------------------------------------------------------------------------*/
static void put_string (out_fp, fw, str)
FILE *out_fp;
int fw;
char *str;
{
   /*
    * force a string to fit in the specified field width. if we have to 
    * truncate, then indicate the truncation with a '>' in the last position
    */

   char buffer[100];

   strncpy (buffer, str, ABS (fw));
   if (strlen (str) > ABS(fw))
      buffer[ABS (fw)-1] = '>';
   buffer[ABS (fw)] = '\0';
#ifdef MSDOS
   /* 
    * both the Microsoft and Zortech compilers generate incorrect output for
    * negative field widths.
    */
   {
      int i;
      int xtra = ABS(fw) - strlen (buffer);
      
      if (fw > 0)
	 for (i = 0; i < xtra; i++)
	    fprintf (out_fp, " ");
      fprintf (out_fp, "%s", buffer);
      if (fw < 0)
	 for (i = 0; i < xtra; i++)
	    fprintf (out_fp, " ");
   }
#else
   fprintf (out_fp, "%*s", fw, buffer);
#endif
}

/*----------------------------------------------------------------------------*/
static void put_lat_long (out_fp, fw, latitude, longitude)
     FILE   *out_fp;
     int    fw;
     double latitude, longitude;
{
   char buffer [100];
   sprintf (buffer, "[%c%3.3lf,%c%3.3lf]", 
	    (latitude > 0.0) ? 'N' : 'S',
	    fabs ((double) decimal_2_degrees_mins (latitude)),
	    (longitude > 0.0) ? 'W' : 'E',
	    fabs ((double) decimal_2_degrees_mins (longitude)));
   fprintf (out_fp, "%*s", fw, buffer);
}
/*----------------------------------------------------------------------------*/
static void put_db_mode (out_fp, fw, mode)
     FILE 	   *out_fp;
     int           fw;
     WAYPOINT_MODE mode;
{
   char *str_val;

   switch (mode) {
   case WP_AIRPORT:
      str_val = "Airport";
      break;
   case WP_VOR:
      str_val = "VOR";
      break;
   case WP_NAMED_INTERSECTION:
   case WP_INTERSECTION:
      str_val = "Intersection";
      break;
   case WP_INCREMENTAL:
      str_val = "Incremental";
      break;
   case WP_LAT_LONG:
      str_val = "Lat/Long";
      break;
   case WP_NDB:
      str_val = "NDB";
      break;
   case WP_DME:
      str_val = "VOR/DME";
      break;
   case WP_TAC:
      str_val = "TACAN";
      break;
   case WP_ILS:
      str_val = "ILS";
      break;
   case WP_WPT:
      str_val = "Waypoint";
      break;
   case WP_LOM:
      str_val = "Outer Mkr";
      break;
   case WP_LMM:
      str_val = "Middle Mkr";
      break;
   default:
      break;
   }
   put_string (out_fp, fw, str_val);
}
/*----------------------------------------------------------------------------*/
static void put_header_cell (out_fp, cell, force)
     FILE *out_fp;
     FORMAT_SPEC_CELL cell;
     BOOLEAN force;
{
   int fw = ((cell.justification == R) ? cell.field_width :
	     -1 * cell.field_width);


   if ((cell.kind == IGNORE) || (cell.kind == END_OF_VOR))
      return;

   if (cell.kind == EOL) {
      line_num++;
      fprintf (out_fp, "\n");
      return;
   }

   if ((!force) &&(cell.shifted == S) && ((line_num == 0) || (line_num == 1))) {
      put_string (out_fp, cell.field_width, "");
      return;
   }

   switch (cell.kind) {
   case BAR:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_VERTICAL_BAR : '|');
      break;
   case FORWARD_ARROW:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_FORWARD_ARROW : '+');
      break;
   case BACKWARD_ARROW:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_BACKWARD_ARROW : '+');
      break;
   case CROSS:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_CROSS : '+');
      break;
   case SEPARATOR:
      { 
	 int i;
	 for (i = 0; i < cell.field_width; i++)
	    fprintf (out_fp, "%c", 
		     use_epson_box_chars ? EPSON_HORIZONTAL_BAR : '-');
      }
      break;
   default:
      put_string (out_fp, fw, headers[(int) cell.kind]);
   }
}


/*----------------------------------------------------------------------------*/
static void put_info_cell (out_fp, cell, prev_wp, curr_wp)
     FILE             *out_fp;
     FORMAT_SPEC_CELL cell;
     WAYPOINT_INFO    *prev_wp, *curr_wp;
{ 
   WAYPOINT_INFO *wp;

   int fw = ((cell.justification == R) ? cell.field_width :
	     -1 * cell.field_width);
   char   *str_val;
   double d_val;
   char buffer[80];
   BOOLEAN valid;
   
   if ((cell.kind == IGNORE) || (cell.kind == END_OF_VOR))
      return;
   if ((cell.shifted == S) &&
	((line_num == 0) || (line_num == 1))) {
      if (! prev_wp) {
	 put_header_cell (out_fp, cell, TRUE);
	 return;
      } else
	 wp = prev_wp;
   } else
      wp = curr_wp;

   if ((cell.shifted == S) && (wp->wp_kind == WP_TO)) {
      put_string (out_fp, cell.field_width, "");
      return;
   }

   switch (cell.kind) {
   case EOL:
      line_num++;
      fprintf (out_fp, "\n");
      break;
   case BAR:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_VERTICAL_BAR : '|');
      break;
   case CROSS:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_CROSS : '+');
      break;
   case FORWARD_ARROW:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_FORWARD_ARROW : '>');
      break;
   case BACKWARD_ARROW:
      fprintf (out_fp, "%c", use_epson_box_chars ? EPSON_BACKWARD_ARROW : '<');
      break;
   case SEPARATOR:
      { 
	 int i;
	 for (i = 0; i < cell.field_width; i++)
	    fprintf (out_fp, "%c", 
		     use_epson_box_chars ? EPSON_HORIZONTAL_BAR : '-');
      }
      break;
      /*
       * strings
       */
   case NAME:
   case CITY:
   case COMMENT:
   case DESIG:
   case VOR_DESIG:
      switch (cell.kind) {
      case CITY:
	 valid = TRUE;
	 str_val = wp->db->city;
	 break;
      case NAME:
	 valid = TRUE;
	 str_val = wp->db->name;
	 break;
      case COMMENT:
	 valid = TRUE;
	 str_val = wp->db->comment;
	 break;
      case DESIG:
	 valid = TRUE;
	 str_val = wp->db->desig;
	 break;
      case VOR_DESIG:
	 valid   = wp->vor_fix [cell.arg].valid;
	 if (valid)
	    str_val = wp->vor_fix [cell.arg].db->desig;
	 break;
      default:
	 valid   = FALSE;
	 str_val = NULL;
	 break;
      }
      if (! str_val)
	 valid = FALSE;
      if (valid) {
	 put_string (out_fp, fw, str_val);
      } else
	 fprintf (out_fp, "%*s", fw, "");
      break;
      /*
       * headings
       */
   case MH:
   case MC:
   case TC:
      switch (cell.kind) {
      case MH:
	 valid = wp->mh.valid;
	 d_val = wp->mh.value;
	 break;
      case MC:
	 valid = wp->mc.valid;
	 d_val = wp->mc.value;
	 break;
      case TC:
	 valid = wp->tc.valid;
	 d_val = wp->tc.value;
	 break;
      default:
	 break;
      }
      if (valid) {
	 sprintf (buffer, "%03.*f", cell.arg, d_val);
	 fprintf (out_fp, "%*s", fw, buffer);
      } else {
	 fprintf (out_fp, "%*s", fw, "");
      }
      break;
      /*
       * numbers
       */
   case FREQ:
   case VOR_FREQ:
      switch (cell.kind) {
      case VOR_FREQ:
	 valid = wp->vor_fix [cell.arg].valid;
	 if (valid) 
	    d_val = wp->vor_fix [cell.arg].db->freq.value;
	 break;
      case FREQ:
	 valid = wp->db->freq.valid;
	 d_val = wp->db->freq.value;
	 break;
      default:
	 valid = FALSE;
	 break;
      }
      if (valid) {
	 fprintf (out_fp, "%*.2f", fw, d_val);
      } else
	 fprintf (out_fp, "%*s", fw, "");
      break;
   case ELEV:
   case TAS:
   case EGS:
   case FUEL_AMT:
   case FUEL_LEG:
   case FUEL_RATE:
   case DIST_LEG:
   case DIST:
   case DIST_REMAIN:
      switch (cell.kind) {
      case ELEV:
	 valid = wp->db->altitude.valid;
	 d_val = wp->db->altitude.value;
	 break;
      case TAS:
	 valid = wp->tas.valid;
	 d_val = NM_OR_MI (wp->tas.value); 
	 break;
      case EGS:
	 valid = wp->egs.valid;
	 d_val = NM_OR_MI (wp->egs.value);
	 break;
      case FUEL_AMT:
	 valid = wp->fuel_amt.valid;
	 d_val = wp->fuel_amt.value;
	 break;
      case FUEL_LEG:
	 valid = wp->fuel_leg.valid;
	 d_val = wp->fuel_leg.value;
	 break;
      case FUEL_RATE:
	 valid = wp->fuel_rate.valid;
	 d_val = wp->fuel_rate.value;
	 break;
      case DIST_LEG:
	 valid = wp->dist_leg.valid;
	 d_val = NM_OR_MI (wp->dist_leg.value);
	 break;
      case DIST:
	 valid = wp->dist.valid;
	 d_val = NM_OR_MI (wp->dist.value);
	 break;
      case DIST_REMAIN:
	 valid = wp->dist_remain.valid;
	 d_val = NM_OR_MI (wp->dist_remain.value);
	 break;
      default:
	 valid = FALSE;
	 d_val = 0.0;
	 break;
      }
      if (valid) {
	 fprintf (out_fp, "%*.*f", fw, cell.arg, d_val);
      } else
	 fprintf (out_fp, "%*s", fw, "");
      break;
      /*
       * times:
       */
   case ETA:
   case ETA_LEG:
      valid = (cell.kind == ETA) ? wp->eta.valid : wp->eta_leg.valid;
      d_val = (cell.kind == ETA) ? wp->eta.value : wp->eta_leg.value;
      if (valid)
	 put_time (out_fp, fw, d_val);
      else
	 fprintf (out_fp, "%*s", fw, "");
      break;
   case VOR_DME:
      if (wp->vor_fix[cell.arg].valid)
	 fprintf (out_fp, "%*.0lf", fw, wp->vor_fix[cell.arg].distance);
      else
	 fprintf (out_fp, "%*s", fw, "");
      break;
   case VOR_RADIAL:
      if (wp->vor_fix[cell.arg].valid) {
	 sprintf (buffer, "%03.0f%c", wp->vor_fix[cell.arg].heading, 
		  (wp->vor_fix[cell.arg].from_to == FROM ? 'F' : 'T') );
	 fprintf (out_fp, "%*s", fw, buffer);
      } else {
	 fprintf (out_fp, "%*s", fw, "");
      }
      break;
   case LAT_LONG:
      put_lat_long (out_fp, fw, wp->db->latitude, wp->db->longitude);
      break;
   case ALT:
      if (! wp->altitude.valid) {
	 fprintf (out_fp, "%*s", fw, "");
#if 0
      } else if (wp->altitude.value < 1000.0) {
	 sprintf (buffer, "FL%g", wp->altitude.value);
	 fprintf (out_fp, "%*s", fw, buffer);
#endif
      } else {
	 fprintf (out_fp, "%*.0f", fw, wp->altitude.value);
      }
      break;
   case WIND:
      if (wp->wind_direction.valid && wp->wind_speed.valid) {
	 sprintf (buffer, "%03.0f@%02.0f", wp->wind_direction.value, 
		  wp->wind_speed.value);
	 fprintf (out_fp, "%*s", fw, buffer);
      } else {
	 fprintf (out_fp, "%*s", fw, "");
      }
      break;
   case TYPE:
      put_db_mode (out_fp, fw, wp->db->mode);
      break;
   case EMPTY:
   case ATA:
   case AGS:
   default:
      fprintf (out_fp, "%*s", fw, "");
      break;
   }    
}

/*----------------------------------------------------------------------------*/
static void put_info_cells (out_fp, cells, prev_wp, wp)
     FILE             *out_fp;
     FORMAT_SPEC_CELL cells[];
     WAYPOINT_INFO    *prev_wp, *wp;
{
   int i;

   line_num = 0;
   for (i = 0; (cells[i].kind != END_OF_FORMAT); i++) 
      put_info_cell (out_fp, cells[i], prev_wp, wp);
}

/*----------------------------------------------------------------------------*/
static void put_header_cells (out_fp, cells, first_line_only)
     FILE             *out_fp;
     FORMAT_SPEC_CELL cells[];
     BOOLEAN first_line_only;
{
   int i;

   line_num = 0;
   for (i = 0; (cells[i].kind != END_OF_FORMAT); i++) {
      if (first_line_only && line_num)
	 return;
      else
	 put_header_cell (out_fp, cells[i], FALSE);
   }
}

/*----------------------------------------------------------------------------*/
void print_plan ()
{
   int i;

   for (i = 0; i < num_waypoints; i++) {
      if (waypoints[i].wp_kind == WP_FROM) {
	 if (i)
	    fprintf (stdout, "\f");
	 printf ("\nSpeeds in %s; Distances in %s miles\n",
		 (output_units == NAUTICAL) ? "knots" : "mph (wind in knots)",
		 (output_units == NAUTICAL) ? "nautical" : "statute");
	 put_header_cells (stdout, output_format, FALSE);
      }
      put_info_cells (stdout, output_format,
		      (waypoints[i].wp_kind == WP_FROM) ? 0 : &waypoints[i-1],
		      &waypoints[i]);
      if (waypoints[i].wp_kind == WP_TO)
	 put_header_cells (stdout, output_format, TRUE);
   }
}


/*----------------------------------------------------------------------------*/
put_db (out_fp, db)
     FILE          *out_fp;
     DATABASE_INFO *db;
{
   fprintf (out_fp, "______________________________________________________________________________\n");
   fprintf (out_fp, "%s\t", db->desig);
   put_db_mode (out_fp, -12, db->mode);
   if (db->name)
      fprintf (out_fp, "\nNAME   : %s\n", db->name);
   if (db->city)
      fprintf (out_fp, "CITY   : %s\n", db->city);
   if (db->comment)
      fprintf (out_fp, "COMMENT: %s\n", db->comment);
   fprintf (out_fp, "LAT/LONG: ");
   put_lat_long (out_fp, 1, db->latitude, db->longitude);
   fprintf (out_fp, " MAG VARIATION: %g ", db->mag_variation);
   if (db->freq.valid)
      fprintf (out_fp, "FREQ: %7.2f ", db->freq.value);
   if (db->altitude.valid)
      fprintf (out_fp, "ALTITUDE: %g", db->altitude.value);
   fprintf (out_fp, "\n");
}
