/* xprep.cc 1.19 95/12/24 01:05:05 */


// xspacewarp by Greg Walker (gow@math.orst.edu)

// This is free software. Non-profit redistribution and/or modification
// is allowed and welcome.


// prepare the X windows stuff.


#include "xprep.hh"
#include <X11/Core.h>
#include <X11/Xmu/Converters.h>	// convert string to long int
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <iostream.h>
#include "common.hh"
#include "params.hh"
#include "globals.hh"
#include "math.h"

static void nomouse(void);
static void install_GCs(void);
static int read_dashes(const char *, char *);
static void check_values(void);


void xprep(int argc, char **argv)
{
  // Intrinsics do not provide a string-to-long resource converter

  XtAddConverter(XtRString, XtRLong, XmuCvtStringToLong, NULL, 0);

  XtVaGetApplicationResources(toplevel, &app_data, resources,
			      XtNumber(resources), NULL);

  check_values();		// look for invalid values in app_data

  XtAppAddActions(app_context, actions, XtNumber(actions));

  // all drawing will be done on this thing and on a pixmap

  widget = XtVaCreateManagedWidget("widget",
				   coreWidgetClass,
				   toplevel,
				   XtNwidth, GAMEW,
				   XtNheight, GAMEH,
				   NULL);

  // see if widget translation table is missing. install default
  // trans-table if necessary

  XtTranslations trans_table;
  XtVaGetValues(widget, XtNtranslations, &trans_table, NULL);
  if (!trans_table)		// app-default trans-table is missing
  {
    trans_table = XtParseTranslationTable(def_trans_table);
    XtOverrideTranslations(widget, trans_table);
  }

  // create window and map it

  XtRealizeWidget(toplevel);

  // if desired, install an invisible mouse cursor
  // that does not block view of xspacewarp.

  if (app_data.nomouse)
     nomouse();

  // pixmap to use as backing store

  pixmap = XCreatePixmap(DISPLAY, XtWindow(toplevel), GAMEW, GAMEH, DEPTH);

  // make graphics contexts

  install_GCs();
}


// Make the mouse pointer invisible when it enters the game window.

void nomouse(void)
{
  Pixmap pix;
  Cursor cursor;
  XColor bg;
  
  pix = XCreatePixmapFromBitmapData(DISPLAY, XtWindow(widget),
				    bitcursor_bits, bitcursor_width,
				    bitcursor_height, WHITE, BLACK, 1);
  bg.pixel = app_data.background;
  XQueryColor(DISPLAY, COLORMAP, &bg);
  cursor = XCreatePixmapCursor(DISPLAY, pix, None, &bg, &bg,
			       bitcursor_x_hot, bitcursor_y_hot);
  XDefineCursor(DISPLAY, WINDOW, cursor);
  XFreePixmap(DISPLAY, pix);
}


// Install the graphics contexts used in this game

static void install_GCs(void)
{
  Font font;
  XGCValues gcvalues;

  // create the default GC used for drawing text and borders

  if (!(font = XLoadFont(DISPLAY, "9x15")))
  {
    cerr << "xspacewarp: cannot load font 9x15." << endl;
    exit(1);
  }
  gcvalues.foreground = app_data.foreground;
  gcvalues.background = app_data.background;
  gcvalues.line_width = 1;
  gcvalues.font = font;
  def_GC = XCreateGC(DISPLAY, XtWindow(toplevel),
		     (GCForeground | GCBackground | GCLineWidth | GCFont),
		     &gcvalues);


  // create the default reverse video GC for erasing regions

  gcvalues.foreground = app_data.background;
  gcvalues.background = app_data.foreground;
  gcvalues.line_width = 1;
  gcvalues.font = font;
  defrv_GC = XCreateGC(DISPLAY, XtWindow(toplevel),
		       (GCForeground | GCBackground | GCLineWidth | GCFont),
		       &gcvalues);

  // GC to use for faser blasts

  gcvalues.line_width = FASERWTH;
  gcvalues.line_style = LineOnOffDash;
  gcvalues.foreground = app_data.faser_color;
  faserGC = XCreateGC(DISPLAY, XtWindow(toplevel),
	              (GCLineWidth | GCLineStyle | GCForeground),
		    &gcvalues);

  // get the dash pattern to use for the fasers

  char dash_list[FASERMAXDASH];
  int ndashes;
  if ((ndashes = read_dashes(app_data.faser_dash_list, dash_list)) < 1)
  {
    invalid("faserDashList");
    app_data.faser_dash_list = def_app_data.faser_dash_list;
    (void) read_dashes(app_data.faser_dash_list, dash_list);
  }
  XSetDashes(DISPLAY, faserGC, app_data.faser_dash_offset, dash_list, ndashes);
	     

  // reverse video GC to use for faser blasts

  gcvalues.line_width = FASERRVWTH; // extra width to clean up non-right angles
  gcvalues.line_style = LineSolid;
  gcvalues.foreground = app_data.background;
  faserGC_rv = XCreateGC(DISPLAY, XtWindow(toplevel),
		    (GCLineWidth | GCLineStyle | GCForeground),
		    &gcvalues);

  // GC to use for torpedo shots

  gcvalues.line_width = TORPWTH;
  gcvalues.line_style = LineSolid;
  gcvalues.foreground = app_data.torpedo_color;
  torpGC = XCreateGC(DISPLAY, XtWindow(toplevel),
		    (GCLineWidth | GCLineStyle | GCForeground),
		    &gcvalues);

  // reverse video GC to use for torpedo shots

  gcvalues.line_width = TORPRVWTH; // extra width to clean up non-right angles
  gcvalues.line_style = LineSolid;
  gcvalues.foreground = app_data.background;
  torpGC_rv = XCreateGC(DISPLAY, XtWindow(toplevel),
		    (GCLineWidth | GCLineStyle | GCForeground),
		    &gcvalues);

  // GC to use for explosions

  gcvalues.foreground = app_data.explosion_color;
  explodeGC = XCreateGC(DISPLAY, XtWindow(toplevel), GCForeground, &gcvalues);

  // GC to use for endever

  gcvalues.foreground = app_data.endever_color;
  gcvalues.font = font;
  endeverGC = XCreateGC(DISPLAY, XtWindow(toplevel),
			(GCForeground | GCFont), &gcvalues);
  
  // GC to use for bases

  gcvalues.foreground = app_data.base_color;
  gcvalues.font = font;
  baseGC = XCreateGC(DISPLAY, XtWindow(toplevel),
		     (GCForeground | GCFont), &gcvalues);

  // GC to use for jovians

  gcvalues.foreground = app_data.jovian_color;
  gcvalues.font = font;
  jovianGC = XCreateGC(DISPLAY, XtWindow(toplevel),
		       (GCForeground | GCFont), &gcvalues);

  // GC to use for stars

  gcvalues.foreground = app_data.star_color;
  gcvalues.font = font;
  starGC = XCreateGC(DISPLAY, XtWindow(toplevel),
		     (GCForeground | GCFont), &gcvalues);

  // GC to use for blackholes

  gcvalues.foreground = app_data.blackhole_color;
  gcvalues.font = font;
  blackholeGC = XCreateGC(DISPLAY, XtWindow(toplevel),
		     (GCForeground | GCFont), &gcvalues);
}
  

// This reads a sequence of chars (one byte integers)
// represented symbolically in in_str (eg, in_str = "10 5 4 13")
// and converts in_str to an array of chars ( [10, 5, 4, 13] in
// example above). read_dashes() reads a string (ie, an unbroken
// sequence on non white space) out of in_str (skipping over
// white space), scans the string for a char (one byte integer),
// stores the char in out_str, skips over white space, reads the
// next string out of in_str, etc.

static int read_dashes(const char *in_str, char *out_str)
{
  const int maxstrlen = 2;
  char dashvalue[maxstrlen+1];	// plus a null
  int ndashes = 0;
  int n;

  while (*in_str && ndashes < FASERMAXDASH)
  {
    while (*in_str && isspace(*in_str))	// skip white space
       in_str++;

    n = 0;
    while (*in_str && !isspace(*in_str)) // read non white space
    {
      if (n+1 > maxstrlen)
         return (0);
      dashvalue[n++] = *in_str++;
    }
    dashvalue[n] = 0;

    if (n > 0)			// number of digits in dash length
    {
      if (sscanf(dashvalue, "%d", &n) < 1) // not a number
         return (0);
      if (n < 1)		// dash lengths must be at least 1
         return (0);
      out_str[ndashes++] = (char) n;
    }
  }

  return (ndashes);
}


// Check validity of data read in from X resources.

static void check_values(void)
{
  if (app_data.rows < 1)
  {
    invalid("rows");
    app_data.rows = def_app_data.rows;
  }
  
  if (app_data.columns < 1)
  {
    invalid("columns");
    app_data.columns = def_app_data.columns;
  }
  
  if (app_data.min_average_base_pop < 1 ||
      app_data.min_variation_base_pop < 0 ||
      app_data.min_average_base_pop - app_data.min_variation_base_pop < 1 ||
      app_data.min_average_base_pop + app_data.min_variation_base_pop >
      UROWS*UCOLS)
  {
    invalid("minAverageBasePop or minVariationBasePop");
    app_data.min_average_base_pop = def_app_data.min_average_base_pop;
    app_data.min_variation_base_pop = def_app_data.min_variation_base_pop;
  }

  if (app_data.max_average_base_pop < 1 ||
      app_data.max_variation_base_pop < 0 ||
      app_data.max_average_base_pop - app_data.max_variation_base_pop < 1 ||
      app_data.max_average_base_pop + app_data.max_variation_base_pop >
      UROWS*UCOLS)
  {
    invalid("maxAverageBasePop or maxVariationBasePop");
    app_data.max_average_base_pop = def_app_data.max_average_base_pop;
    app_data.max_variation_base_pop = def_app_data.max_variation_base_pop;
  }

  if (app_data.max_jovians_per_sector < 1)
  {
    invalid("maxJoviansPerSector");
    app_data.max_jovians_per_sector = def_app_data.max_jovians_per_sector;
  }

  if (app_data.min_average_jovian_pop < 1 ||
      app_data.min_variation_jovian_pop < 0 ||
      app_data.min_average_jovian_pop - app_data.min_variation_jovian_pop < 1 ||
      app_data.min_average_jovian_pop + app_data.min_variation_jovian_pop >
      app_data.max_jovians_per_sector*UROWS*UCOLS)
  {
    invalid("minAverageJovianPop or minVariationJovianPop");
    app_data.min_average_jovian_pop = def_app_data.min_average_jovian_pop;
    app_data.min_variation_jovian_pop = def_app_data.min_variation_jovian_pop;
  }

  if (app_data.max_average_jovian_pop < 1 ||
      app_data.max_variation_jovian_pop < 0 ||
      app_data.max_average_jovian_pop - app_data.max_variation_jovian_pop < 1 ||
      app_data.max_average_jovian_pop + app_data.max_variation_jovian_pop >
      app_data.max_jovians_per_sector*UROWS*UCOLS)
  {
    invalid("maxAverageJovianPop or maxVariationJovianPop");
    app_data.max_average_jovian_pop = def_app_data.max_average_jovian_pop;
    app_data.max_variation_jovian_pop = def_app_data.max_variation_jovian_pop;
  }

  if (app_data.max_stars_per_sector < 1)
  {
    invalid("maxStarsPerSector");
    app_data.max_stars_per_sector = def_app_data.max_stars_per_sector;
  }

  if (app_data.min_average_star_pop < 1 ||
      app_data.min_variation_star_pop < 0 ||
      app_data.min_average_star_pop - app_data.min_variation_star_pop < 1 ||
      app_data.min_average_star_pop + app_data.min_variation_star_pop >
      app_data.max_stars_per_sector*UROWS*UCOLS)
  {
    invalid("minAverageStarPop or minVariationStarPop");
    app_data.min_average_star_pop = def_app_data.min_average_star_pop;
    app_data.min_variation_star_pop = def_app_data.min_variation_star_pop;
  }

  if (app_data.max_average_star_pop < 1 ||
      app_data.max_variation_star_pop < 0 ||
      app_data.max_average_star_pop - app_data.max_variation_star_pop < 1 ||
      app_data.max_average_star_pop + app_data.max_variation_star_pop >
      app_data.max_stars_per_sector*UROWS*UCOLS)
  {
    invalid("maxAverageStarPop or maxVariationStarPop");
    app_data.max_average_star_pop = def_app_data.max_average_star_pop;
    app_data.max_variation_star_pop = def_app_data.max_variation_star_pop;
  }

  if (app_data.min_average_blackhole_pop < 1 ||
      app_data.min_variation_blackhole_pop < 0 ||
      app_data.min_average_blackhole_pop - app_data.min_variation_blackhole_pop
      < 1 ||
      app_data.min_average_blackhole_pop + app_data.min_variation_blackhole_pop
      > UROWS*UCOLS)
  {
    invalid("minAverageBlackholePop or minVariationBlackholePop");
    app_data.min_average_blackhole_pop = def_app_data.min_average_blackhole_pop;
    app_data.min_variation_blackhole_pop =
      def_app_data.min_variation_blackhole_pop;
  }

  if (app_data.max_average_blackhole_pop < 1 ||
      app_data.max_variation_blackhole_pop < 0 ||
      app_data.max_average_blackhole_pop - app_data.max_variation_blackhole_pop
      < 1 ||
      app_data.max_average_blackhole_pop + app_data.max_variation_blackhole_pop
      > UROWS*UCOLS)
  {
    invalid("maxAverageBlackholePop or maxVariationBlackholePop");
    app_data.max_average_blackhole_pop = def_app_data.max_average_blackhole_pop;
    app_data.max_variation_blackhole_pop =
      def_app_data.max_variation_blackhole_pop;
  }

  if (app_data.faser_width < 1)
  {
    invalid("faserWidth");
    app_data.faser_width = def_app_data.faser_width;
  }

  if (app_data.faser_dash_offset < 0)
  {
    invalid("faserDashOffset");
    app_data.faser_dash_offset = def_app_data.faser_dash_offset;
  }

  if (app_data.faser_speed < 1)
  {
    invalid("faserSpeed");
    app_data.faser_speed = def_app_data.faser_speed;
  }

  if (app_data.torpedo_width < 1)
  {
    invalid("torpedoWidth");
    app_data.torpedo_width = def_app_data.torpedo_width;
  }

  if (app_data.torpedo_length < 1)
  {
    invalid("torpedoLength");
    app_data.torpedo_length = def_app_data.torpedo_length;
  }

  if (app_data.torpedo_speed < 1)
  {
    invalid("torpedoSpeed");
    app_data.torpedo_speed = def_app_data.torpedo_speed;
  }

  if (app_data.explosion_speed < 1)
  {
    invalid("explosionSpeed");
    app_data.explosion_speed = def_app_data.explosion_speed;
  }

  if (app_data.explosion_radius_small < 1)
  {
    invalid("explosionRadiusSmall");
    app_data.explosion_radius_small = def_app_data.explosion_radius_small;
  }

  if (app_data.explosion_radius_big < 1)
  {
    invalid("explosionRadiusBig");
    app_data.explosion_radius_big = def_app_data.explosion_radius_big;
  }

  if (app_data.endever_thrust_speed < 1)
  {
    invalid("endeverThrustSpeed");
    app_data.endever_thrust_speed = def_app_data.endever_thrust_speed;
  }

  if (app_data.ship_energize_speed < 1)
  {
    invalid("shipEnergizeSpeed");
    app_data.ship_energize_speed = def_app_data.ship_energize_speed;
  }

  if (app_data.jovian_action_speed < 1)
  {
    invalid("jovianActionSpeed.");
    app_data.jovian_action_speed = def_app_data.jovian_action_speed;
  }
    
  if (strlen(app_data.self_destruct_code) != 3)
  {
    invalid("selfDestructCode");
    app_data.self_destruct_code = def_app_data.self_destruct_code;
  }

  if (app_data.max_torpedoes < 0)
  {
    invalid("maxTorpedoes");
    app_data.max_torpedoes = def_app_data.max_torpedoes;
  }

  if (app_data.mask_probability < 0 || app_data.mask_probability > 100)
  {
    invalid("maskProbability");
    app_data.mask_probability = def_app_data.mask_probability;
  }

  if (app_data.min_jovian_distance < 0 ||
      app_data.min_jovian_distance > sqrt((double)SECTDIAGSQ)/(double)4)
  {
    invalid("minJovianDistance");
    app_data.min_jovian_distance = def_app_data.min_jovian_distance;
  }

  if (app_data.endever_min_faser_energy < 0 ||
      app_data.endever_min_faser_energy > 100)
  {
    invalid("endeverMinFaserEnergy");
    app_data.endever_min_faser_energy = def_app_data.endever_min_faser_energy;
  }

  if (app_data.endever_min_warp_energy < 0 ||
      app_data.endever_min_warp_energy > 100)
  {
    invalid("endeverMinWarpEnergy");
    app_data.endever_min_warp_energy = def_app_data.endever_min_warp_energy;
  }

  if (app_data.endever_min_thrust_energy < 0 ||
      app_data.endever_min_thrust_energy > 100)
  {
    invalid("endeverMinThrustEnergy");
    app_data.endever_min_thrust_energy = def_app_data.endever_min_thrust_energy;
  }

  if (app_data.jovian_min_faser_energy < 0 ||
      app_data.jovian_min_faser_energy > 100)
  {
    invalid("jovianMinFaserEnergy");
    app_data.jovian_min_faser_energy = def_app_data.jovian_min_faser_energy;
  }

  if (app_data.jovian_min_warp_energy < 0 ||
      app_data.jovian_min_warp_energy > 100)
  {
    invalid("jovianMinWarpEnergy");
    app_data.jovian_min_warp_energy = def_app_data.jovian_min_warp_energy;
  }

  if (app_data.jovian_min_thrust_energy < 0 ||
      app_data.jovian_min_thrust_energy > 100)
  {
    invalid("jovianMinThrustEnergy");
    app_data.jovian_min_thrust_energy = def_app_data.jovian_min_thrust_energy;
  }

  if (app_data.jovian_thrust_retreat_energy < 0 ||
      app_data.jovian_thrust_retreat_energy > 100)
  {
    invalid("jovianThrustRetreatEnergy");
    app_data.jovian_thrust_retreat_energy =
       def_app_data.jovian_thrust_retreat_energy;
  }

  if (app_data.jovian_warp_retreat_energy < 0 ||
      app_data.jovian_warp_retreat_energy > 100)
  {
    invalid("jovianWarpRetreatEnergy");
    app_data.jovian_warp_retreat_energy =
       def_app_data.jovian_warp_retreat_energy;
  }
  
  if (app_data.jovian_faser_retreat_energy < 0 ||
      app_data.jovian_faser_retreat_energy > 100)
  {
    invalid("jovianFaserRetreatEnergy");
    app_data.jovian_faser_retreat_energy =
       def_app_data.jovian_faser_retreat_energy;
  }

  if (app_data.jovian_shield_retreat_energy < 0 ||
      app_data.jovian_shield_retreat_energy > 100)
  {
    invalid("jovianShieldRetreatEnergy");
    app_data.jovian_shield_retreat_energy =
       def_app_data.jovian_shield_retreat_energy;
  }

  if (app_data.jovian_retreat_probability < 0 ||
      app_data.jovian_retreat_probability > 100)
  {
    invalid("jovianRetreatProbability");
    app_data.jovian_retreat_probability =
       def_app_data.jovian_retreat_probability;
  }

  if (app_data.jovian_retreat_speed <= 0)
  {
    invalid("jovianRetreatSpeed");
    app_data.jovian_retreat_speed =
       def_app_data.jovian_retreat_speed;
  }

  if (app_data.jovian_min_fight_frequency <= 0)
  {
    invalid("jovianMinFightFrequency");
    app_data.jovian_min_fight_frequency =
      def_app_data.jovian_min_fight_frequency;
  }

  if (app_data.jovian_max_fight_frequency <= 0)
  {
    invalid("jovianMaxFightFrequency");
    app_data.jovian_max_fight_frequency =
      def_app_data.jovian_max_fight_frequency;
  }

  if (app_data.jovian_min_raid_frequency <= 0)
  {
    invalid("jovianMinRaidFrequency");
    app_data.jovian_min_raid_frequency =
      def_app_data.jovian_min_raid_frequency;
  }

  if (app_data.jovian_max_raid_frequency <= 0)
  {
    invalid("jovianMaxRaidFrequency");
    app_data.jovian_max_raid_frequency =
      def_app_data.jovian_max_raid_frequency;
  }

  if (app_data.jovian_min_leap_frequency <= 0)
  {
    invalid("jovianMinLeapFrequency");
    app_data.jovian_min_leap_frequency =
      def_app_data.jovian_min_leap_frequency;
  }

  if (app_data.jovian_max_leap_frequency <= 0)
  {
    invalid("jovianMaxLeapFrequency");
    app_data.jovian_max_leap_frequency =
      app_data.jovian_max_leap_frequency;
  }
}







// end
