/* XRACER (C) 1999-2000 Richard W.M. Jones <rich@annexia.org> and other AUTHORS
 *
 * 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.
 *
 * $Id: main.c,v 1.24 2000/03/19 23:48:47 rich Exp $
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <libintl.h>
#define _(String) gettext(String)

#if HAVE_LOCALE_H
#include <locale.h>
#endif

#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#if HAVE_GETOPT_H
#include <getopt.h>
#endif

#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include <GL/glut.h>

#include "xracer.h"
#include "xracer-mode.h"
#include "xracer-text.h"
#include "xracer-log.h"
#include "xracer-track.h"
#include "xracer-player.h"
#include "xracer-game.h"
#include "xracer-ws.h"
#include "xracer-panel.h"
#include "xracer-lcd.h"
#include "xracer-powerup.h"
#include "xracer-joystick.h"
#include "xracer-menu.h"
#include "xracer-sound.h"
#include "xracer-arch.h"
#include "xracer-file-browser.h"
#include "xracer-video.h"

int xrWidth = 640;
int xrHeight = 480;
int xrFullScreen = 0;
double xrCurrentTime = 0;
double xrTimeInterval = 0;
int xrMipmapping = 1;

/* Good ol' ZX Spectrum colours. */
const GLfloat xrColour[8][4] = {
  {0,0,0,1},                    /* black */
  {0,0,1,1},                    /* blue */
  {0,1,0,1},                    /* green */
  {0,1,1,1},                    /* cyan = blue+green */
  {1,0,0,1},                    /* red */
  {1,0,1,1},                    /* magenta = red+blue */
  {1,1,0,1},                    /* yellow = red+green */
  {1,1,1,1}                     /* white */
};
const GLfloat *xrColourBlack = xrColour[0];
const GLfloat *xrColourBlue = xrColour[1];
const GLfloat *xrColourGreen = xrColour[2];
const GLfloat *xrColourCyan = xrColour[3];
const GLfloat *xrColourRed = xrColour[4];
const GLfloat *xrColourMagenta = xrColour[5];
const GLfloat *xrColourYellow = xrColour[6];
const GLfloat *xrColourWhite = xrColour[7];

static void program_initialization (void);
static void program_shutdown (void);

int
main (int argc, char *argv[])
{
  const char *home = 0;
  static int verbose = 0;
  int c;

#if HAVE_GETOPT_LONG
  int option_index = 0;
  struct option long_options[] =
  {
    { "fullscreen",       no_argument,       0, 'f' },
    { "help",             no_argument,       0, 'h' },
    { "joystick",         required_argument, 0, 'j' },
    { "nomipmap",         no_argument,       0, 'L' },
    { "size",             required_argument, 0, 's' },
    { "version",          no_argument,       0, 'V' },
    { "verbose",          no_argument,       0, 'v' },
    { 0, 0, 0, 0 }
  };
#endif

#define OPTION_STRING "hj:Ls:Vv"

#if HAVE_LOCALE_H
  /* Set the current locale. You should be able to select language-specific
   * version of XRacer by correctly setting the ``LANG'' variable in the
   * environment. (Not all languages are supported of course - look for
   * *.po files in the ``po/'' directory to see which languages are supported.)
   */
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
#endif

  /* Read the command line arguments. */
  while ((c =
#if HAVE_GETOPT_LONG
	  getopt_long (argc, argv, OPTION_STRING, long_options, &option_index)
#elif HAVE_GETOPT
	  getopt (argc, argv, OPTION_STRING)
#else
#error "requires getopt or getopt_long calls"
#endif
	  ) != -1)
    {
      switch (c)
	{
	case 'f':
	  xrFullScreen = 1;
	  break;

	case 'h':
	default:
	  fprintf (stderr,
	 _("XRacer (C) 1999-2000 Richard W.M. Jones <rich@annexia.org> and other AUTHORS.\n"
	   "Package name: %s  Version: %s\n"
	   "\n"
	   "This program is free software; you can redistribute it and/or\n"
	   "modify it under the terms of the GNU General Public License\n"
	   "as published by the Free Software Foundation; either version 2\n"
	   "of the License, or (at your option) any later version.\n"
	   "\n"
	   "This program is distributed in the hope that it will be useful,\n"
	   "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
	   "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
	   "GNU General Public License for more details.\n"
	   "\n"
	   "You should have received a copy of the GNU General Public License\n"
	   "along with this program; if not, write to the Free Software\n"
	   "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n"
	   "\n"
	   "Usage:\n"
	   "  xracer [OPTIONS]\n"
	   "Options:\n"
	   "  -f, --fullscreen               Fullscreen mode.\n"
	   "  -h, --help                     Display this help and exit.\n"
	   "  -j, --joystick DEVICE          Set joystick device name.\n"
	   "  -L, --nomipmap                 Disable mipmapping.\n"
	   "  -s, --size WIDTHxHEIGHT        Set the screen size.\n"
	   "  -v, --verbose                  Print verbose messages.\n"
	   "  -V, --version                  Print version number and exit.\n"
	   ),
		   PACKAGE, VERSION);
	  exit (1);

	case 'j':
	  xrJoystickDevice = optarg;
	  break;

	case 'L':
	  xrMipmapping = 0;
	  break;

	case 's':
	  if (sscanf (optarg, "%dx%d", &xrWidth, &xrHeight) != 2)
	    {
	      fprintf (stderr,
		       _("xracer: don't understand the size argument %s (expecting WIDTHxHEIGHT)\n"),
		       optarg);
	      exit (1);
	    }
	  break;

	case 'v':
	  ++verbose;
	  break;

	case 'V':
	  printf ("%s %s\n", PACKAGE, VERSION);
	  exit (EXIT_SUCCESS);
	  break;
	}
    } /* while */

  /* Not that XRacer would ever crash :-) */
#ifdef HAVE_SYNC
  sync ();
#endif

  /* Change the debugging threshold according to whether the
   * verbose flag was set or not.
   */
  if (verbose)
    {
      xrLogSetStderrThreshold (LOG_DEBUG);
      xrLogSetDisplayThreshold (LOG_DEBUG);
    }
  else
    {
      xrLogSetStderrThreshold (LOG_INFO);
      xrLogSetDisplayThreshold (LOG_INFO);
    }

  /* Check XRACER_HOME environment variable is set. If it's set,
   * change to that directory. If not, change to package data
   * directory. If that wasn't defined at compile time, stay in
   * the current directory.
   */
  home = getenv ("XRACER_HOME");
  if (home) xrLog (LOG_DEBUG, "XRACER_HOME=%s", home);

#if defined(XRACER_HOME)
  if (home == 0)
    home = XRACER_HOME;
#endif

  xrLog (LOG_DEBUG, "Trying home directory: %s", home);

  if (home && chdir (home) == -1)
    {
      xrLogPerror (home);
      exit (1);
    }

#ifdef HAVE_ACCESS
  if (access (".xracer-data-directory", R_OK) == -1)
    {
      fprintf (stderr,
    _("xracer: Cannot find the package data directory. If the data files are\n"
      "installed in a non-standard place, then try setting the XRACER_HOME\n"
      "environment variable and running the program again.\n"));
      exit (1);
    }
#endif

  /* Perform program-level initialization. */
  program_initialization ();

  /* Ensure that things are cleaned up at shutdown. */
  atexit (program_shutdown);

  /* Switch into the first mode. */
  if (xrHaveIntroSequence)
    {
      xrEnterMode (&xrIntroMode, 0);
    }
  else
    {
      xrEnterMenu (&xrStartMenu, 0);
    }

  /* Start GLUT main loop. */
  glutMainLoop ();

  /*NOTREACHED*/
  exit (0);
}

/* Put program-level initialization in this function. */
static void
program_initialization ()
{
  void *track;

  /* This disable the splash screen on 3DFX cards, and has no effect
   * for anyone else.
   */
  putenv ("FX_GLIDE_NO_SPLASH=1");

  /* Recalculate current time. */
  xrArchRecalculateCurrentTime ();

  /* Initialize the PRNG. */
  xrLog (LOG_DEBUG, "Initializing PRNG");
#if HAVE_SRANDOM
  srandom ((int) (xrCurrentTime * 1000));
#elif HAVE_SRAND
  srand ((int) (xrCurrentTime * 1000));
#endif

  /* Force fullscreen mode for 3DFX cards, if requested on command line. */
  if (xrFullScreen) putenv ("MESA_GLX_FX=fullscreen");

  /* Initialize GLUT library. */
  xrLog (LOG_DEBUG, "Initializing GLUT");
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
  glutInitWindowSize (xrWidth, xrHeight);

  if (xrFullScreen)
    {
      glutInitWindowPosition (0, 0);
      glutEnterGameMode ();
    }
  else
    {
      static char title[128];

      snprintf (title, sizeof title,
		_("%s %s Copyright (C) 1999-2000 Richard W.M. Jones and other AUTHORS"),
		PACKAGE, VERSION);
      glutCreateWindow (title);
    }
  xrWsFixWindowClass ();

  glutSetKeyRepeat (GLUT_KEY_REPEAT_OFF);

  /* Various other GL initializations. */
  xrLog (LOG_DEBUG, "Initializing GL");

  /* This code taken from Mesa3.1/demos/glinfo.c. Useful for
   * debugging compatibility problems if people send me the
   * output of xracer -v.
   */
  xrLog (LOG_DEBUG, "GL_VERSION: %s", (char *) glGetString (GL_VERSION));
  xrLog (LOG_DEBUG, "GL_EXTENSIONS: %s", (char *) glGetString (GL_EXTENSIONS));
  xrLog (LOG_DEBUG, "GL_RENDERER: %s", (char *) glGetString (GL_RENDERER));
  xrLog (LOG_DEBUG, "GL_VENDOR: %s", (char *) glGetString (GL_VENDOR));
  xrLog (LOG_DEBUG, "GLU_VERSION: %s", (char *) gluGetString (GLU_VERSION));
  xrLog (LOG_DEBUG, "GLU_EXTENSIONS: %s", (char *) gluGetString (GLU_EXTENSIONS));
  xrLog (LOG_DEBUG, "GLUT_API_VERSION: %d", GLUT_API_VERSION);
#ifdef GLUT_XLIB_IMPLEMENTATION
  xrLog (LOG_DEBUG, "GLUT_XLIB_IMPLEMENTATION: %d", GLUT_XLIB_IMPLEMENTATION);
#endif

  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
  glHint (GL_POINT_SMOOTH_HINT, GL_NICEST);
  glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
  glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
  glHint (GL_FOG_HINT, GL_NICEST);
  glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

  glClearColor (0, 0, 0, 0);

  /* Initialize text drawing code. */
  xrLog (LOG_DEBUG, "Initializing fonts and text");
  xrTextInit ();

  /* Initialize file browser library. */
  xrLog (LOG_DEBUG, "Initializing file browser");
  xrFileBrowserInit ();

  /* Initialize mode library. */
  xrLog (LOG_DEBUG, "Initializing modes");
  xrModeInit ();

  /* Initialize log system. */
  xrLog (LOG_DEBUG, "Initializing logging");
  xrLogInit ();

#ifndef DISABLE_VIDEO
  /* Initialize video. */
  xrLog (LOG_DEBUG, "Initializing video sequences");
  xrVideoInit ();
#endif

  /* Initialize introductory video clip. */
  xrLog (LOG_DEBUG, "Initializing intro mode");
  xrIntroModeInit ();

  /* Initialize sound. */
  xrLog (LOG_DEBUG, "Initializing sound");
  xrSoundInit ();

  /* Initialize joystick code. */
  xrLog (LOG_DEBUG, "Initializing joystick");
  xrJoystickInit ();

  /* Initialize menus. */
  xrLog (LOG_DEBUG, "Initializing menus");
  xrMenuInit ();
  xrStartMenuInit ();
  xrQuitMenuInit ();
  xrConfigurationMenuInit ();
  xrKeyboardConfigurationMenuInit ();
  xrLoadTrackModeInit ();

  /* Initialize craft. */
  xrLog (LOG_DEBUG, "Initializing craft");
  xrCraftInit ();

  /* Initialize player code. */
  xrLog (LOG_DEBUG, "Initializing player code");
  xrPlayerInit ();

  /* Initialize powerup code. */
  xrLog (LOG_DEBUG, "Initializing powerups");
  xrPowerupInit ();

  /* Initialize LCD code. */
  xrLog (LOG_DEBUG, "Initializing LCDs");
  xrLCDInit ();

  /* Initialize panel code. */
  xrLog (LOG_DEBUG, "Initializing instrument panel");
  xrPanelInit ();

  /* Initialize game mode. */
  xrLog (LOG_DEBUG, "Initializing game mode");
  xrGameInit ();

  /* Load simple track. */
  xrLog (LOG_DEBUG, "Loading simple track");
  if ((track = xrTrackLoadByName ("simple2")) == 0)
    {
      xrLogPerror ("load track: simple2");
      exit (1);
    }
  xrTrackMakeCurrent (track);
}

/* Put program-level cleanup code in this function. */
static void
program_shutdown ()
{
  xrLog (LOG_DEBUG, "Shutting down sound process");
  xrSoundCleanup ();

  if (xrFullScreen)
    {
      xrLog (LOG_DEBUG, "Leaving game mode");
      glutLeaveGameMode ();
    }

  xrLog (LOG_DEBUG, "Restoring keyboard repeat mode");
  glutSetKeyRepeat (GLUT_KEY_REPEAT_DEFAULT);
  xrWsFlushEventQueue ();
}
