/* $Id: main.c,v 1.5 2000/05/28 21:00:40 jhall Exp $ */

/* GNU Robots game engine.  This is the main() program, using GNU
   Guile as my backend to handle the language. */

/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */

/*
   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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include <stdio.h>
#include <unistd.h>		/* for getopt */

#include <getopt.h>		/* for GNU getopt_long */
#include <guile/gh.h>		/* GNU Guile high */

#include "api.h"		/* robot API, as Scheme functions */
#include "drawmap.h"		/* draws a game map */
#include "hooks.h"		/* user interface hooks */
#include "loadmap.h"		/* loads a game map */
#include "main.h"		/* for this source file */

#if 0				/* scm_random is included in guile 1.3 */
#include "random.h"		/* faked SCM primitives */
#endif

/* Symbolic constants */

#define MAP_XSIZE 40
#define MAP_YSIZE 20

#define DEFAULT_ENERGY 1000
#define DEFAULT_SHIELDS 100

#define VERSION   "GNU Robots, version 1.0"
#define COPYRIGHT "Copyright (C) 1998,1999,2000 Jim Hall <jhall1@isd.net>"


/* Globals (share with api.c) */

int **map;
int nrows = MAP_YSIZE;
int ncols = MAP_XSIZE;

int robot_x = 1;		/* the robot position, etc. */
int robot_y = 1;
int robot_dir = 0;

long robot_score = 0;		/* the robot score, etc. */
long robot_energy = DEFAULT_ENERGY;
long robot_shields = DEFAULT_SHIELDS;


/************************************************************************
 * main()                                                               *
 * The program starts here!                                             *
 ************************************************************************/

int
main (int argc, char *argv[])
{
  int opt;			/* the option read from getopt */

  int flag;			/* flag passed back from getopt - NOT
				   USED */

  char *main_argv[3] = { "GNU Robots",
    "test.scm",
    "mapfile.map"
  };

  struct option long_opts[] = {
    {"version", 0, NULL, 'V'},
    {"help", 0, NULL, 'h'},
    {"map-file", 1, NULL, 'f'},
    {"shields", 1, NULL, 's'},
    {"energy", 1, NULL, 'e'},
    {NULL, 0, NULL, 0}
  };

  /* Check command line */

  while ((opt = getopt_long (argc, argv, "Vhf:s:e:", long_opts, &flag)) !=
	 EOF)
    {
      switch (opt)
	{
	case 'V':

	  /* Display version, then quit */

	  fprintf (stderr, "%s\n", VERSION);
	  fprintf (stderr, "%s\n", COPYRIGHT);
	  exit (0);

	  break;

	case 'h':

	  /* Display help, then quit. */

	  usage (argv[0]);
	  exit (0);

	  break;

	case 'f':

	  /* Set map file */

	  main_argv[2] = optarg;	/* pointer assignment */

	  break;

	case 's':

	  /* Set shields */

	  robot_shields = (long) atol (optarg);

	  break;

	case 'e':

	  /* Set energy */

	  robot_energy = (long) atol (optarg);

	  break;

	default:

	  /* invalid option */

	  usage (argv[0]);
	  exit (1);

	  break;
	}			/* switch */
    }				/* while */

  /* Extra arg is the Scheme file */

  if (optind < argc)
    {
      /* Set Scheme file */

      main_argv[1] = argv[optind];	/* pointer assignment */
    }

  /* Check that files exist */

  if (!is_file (main_argv[1]))
    {
      fprintf (stderr, "%s: %s: Scheme file does not exist\n",
	       argv[0], main_argv[1]);

      exit (1);
    }

  if (!is_file (main_argv[2]))
    {
      fprintf (stderr, "%s: %s: game map file does not exist\n",
	       argv[0], main_argv[2]);

      exit (1);
    }

  /* Start Guile environment.  Does not exit */

  printf ("%s\n", VERSION);
  printf ("%s\n", COPYRIGHT);
  printf ("GNU Robots comes with ABSOLUTELY NO WARRANTY\n");
  printf ("This is free software, and you are welcome to redistribute it\n");
  printf ("under certain conditions; see the file `COPYING' for details.\n");
  printf ("Loading Guile ... Please wait\n");

  gh_enter (2, main_argv, main_prog);

  exit (0);			/* never gets here, but keeps compiler
				   happy */
}

/************************************************************************
 * main_prog()                                                          *
 * the main program code that is executed after Guile starts up.  Pass  *
 * the Scheme program as argv[1] and the map file as argv[2].  The      *
 * program name is still argv[0].                                       *
 ************************************************************************/

void
main_prog (int argc, char *argv[])
{
  int j;
  char *robot_program = argv[1];
  char *map_file = argv[2];

  /* gh_startup();              no longer needed in guile 1.2? */

  /* define some new builtins (hooks) so that they are available in
     Scheme. */

  gh_new_procedure1_0 ("robot-turn", robot_turn);
  gh_new_procedure1_0 ("robot-move", robot_move);
  gh_new_procedure1_0 ("robot-smell", robot_smell);
  gh_new_procedure1_0 ("robot-feel", robot_feel);
  gh_new_procedure1_0 ("robot-look", robot_look);
  gh_new_procedure0_0 ("robot-grab", robot_grab);
  gh_new_procedure0_0 ("robot-zap", robot_zap);

  gh_new_procedure0_0 ("robot-get-shields", robot_get_shields);
  gh_new_procedure0_0 ("robot-get-energy", robot_get_energy);
  gh_new_procedure0_0 ("robot-get-score", robot_get_score);

#if 0                           /* disabled code */
  /* scm_random is included in guile 1.3 */

  /* Scheme primitives not there in Guile 1.2 */

  gh_new_procedure1_0 ("random", scm_random);
  gh_new_procedure0_0 ("randomize", scm_randomize);
#endif                          /* disabled code */

  /* Redefine some existing Scheme primitives */

  gh_new_procedure0_0 ("stop", robot_stop);
  gh_new_procedure0_0 ("quit", robot_stop);

  /* Create the map array, and load the map */

  map = malloc (nrows * sizeof (int **));

  for (j = 0; j < nrows; j++)
    {
      map[j] = malloc (ncols * sizeof (int));
    }

  printf ("Map file: %s\n", map_file);

  load_map (map_file, map, nrows, ncols);
  cleanup_map (map, nrows, ncols);

  /* ensure the robot is placed properly */

  robot_x = 1;
  robot_y = 1;
  robot_dir = 1;
  map[robot_y][robot_x] = ROBOT;

  /* draw the map */

  printf ("Robot program: %s\n", robot_program);
  hook_init ();
  draw_map (map, nrows, ncols);

  /* execute a Scheme file */

  gh_eval_file (robot_program);

  /* done */

  exit_nicely ();
}

/************************************************************************
 * exit_nicely()                                                        *
 * A function that allows the program to exit nicely, after freeing all *
 * memory pointers, etc.                                                *
 ************************************************************************/

void
exit_nicely (void)
{
  int j;

  /* Stop the UI */

  hook_close ();

  /* Free memory */

  for (j = 0; j < nrows; j++)
    {
      free (map[j]);
    }
  free (map);

  /* Show statistics */

  printf ("\n-----------------------STATISTICS-----------------------\n");
  printf ("Shields: %ld\n", (robot_shields < 0 ? 0 : robot_shields));
  printf ("Energy: %ld\n", (robot_energy < 0 ? 0 : robot_energy));
  printf ("Score: %ld\n", robot_score);

  /* Show results, if any */

  if (robot_shields < 1)
    {
      printf ("** Robot took too much damage, and died.\n");
    }

  else if (robot_energy < 1)
    {
      printf ("** Robot ran out of energy.\n");
    }

  /* Quit program */

  exit (0);
}

/************************************************************************
 * usage()                                                              *
 * A function that prints the usage of GNU Robots to the user.  Assume  *
 * text mode for this function.  We have not initialized X Windows or   *
 * curses yet.                                                          *
 ************************************************************************/

void
usage (const char *argv0)
{
  printf ("%s\n", VERSION);
  printf ("%s\n", COPYRIGHT);
  printf
    ("Game/diversion where you construct a program for a little robot\n");
  printf ("then set him loose and watch him explore a world on his own.\n\n");

  printf ("Usage: %s [OPTION]... [FILE]\n", argv0);
  printf ("  -f, --map-file=FILE    Load map file\n");
  printf ("  -s, --shields=N        Set initial shields to N\n");
  printf ("  -e, --energy=N         Set initial energy to N\n");
  printf ("  -V, --version          Output version information and exit\n");
  printf ("  -h, --help             Display this help and exit\n");
}

/************************************************************************
 * is_file()                                                            *
 * Checks if a file is a readable file.  We will use this function as   *
 * part of a sanity check, before we get anywhere near having to open   *
 * files.  This will save on error checking later on, when we may have  *
 * already initialized another environment (Curses, X Windows, ...)     *
 ************************************************************************/

int
is_file (const char *filename)
{
  FILE *stream;

  stream = fopen (filename, "r");
  if (stream == NULL)
    {
      /* Failed */

      return (0);
    }

  /* Success */

  fclose (stream);
  return (1);
}
