/* $Id: main.c,v 1.4 1998/09/27 04:47:08 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 <gh.h>			/* for GNU Guile */

#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 */
#include "random.h"		/* faked SCM primitives */


/* Symbolic constants */

#define MAP_XSIZE 40
#define MAP_YSIZE 20

#define DEFAULT_ENERGY 1000
#define DEFAULT_SHEILDS 100

#define VERSION   "GNU Robots, version 0.90"
#define COPYRIGHT "Copyright (C) 1998 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_SHEILDS;


/* The program starts here */

int
main (int argc, char *argv[])
{
  int opt;
  int flag;

  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'},
    {"sheilds", 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", COPYRIGHT);
	  fprintf (stderr, "%s\n", VERSION);
	  exit (0);
	  break;

	case 'h':
	  /* Display help, then quit. */
	  usage ();
	  exit (0);
	  break;

	case 'f':
	  /* Set map file */

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

	case 's':
	  /* Set sheilds */

	  robot_shields = (long) atol (optarg);
	  break;

	case 'e':
	  /* Set energy */

	  robot_energy = (long) atol (optarg);
	  break;

	default:
	  /* invalid option */
	  usage ();
	  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 ("loading Guile .. please wait\n");

  gh_enter (2, main_argv, main_prog);
  exit (0);			/* never gets here */
}

/* 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-sheilds", robot_get_sheilds);
  gh_new_procedure0_0 ("robot-get-energy", robot_get_energy);
  gh_new_procedure0_0 ("robot-get-score", robot_get_score);

  /* Scheme primitives not there in Guile 1.2 */

  gh_new_procedure1_0 ("random", scm_random);
  gh_new_procedure0_0 ("randomize", scm_randomize);

  /* 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 ("Loading map..\n");
  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 ("Starting robot..\n");
  hook_init ();
  draw_map (map, nrows, ncols);

  /* execute a Scheme file */

#ifndef NO_DEBUG_MSGS
  printf ("--------------------------------------------------------\n");
#endif /* NO_DEBUG_MSGS */

  gh_eval_file (robot_program);

#ifndef NO_DEBUG_MSGS
  printf ("--------------------------------------------------------\n");
#endif /* NO_DEBUG_MSGS */

  /* done */

  printf ("Done.\n");
  exit_nicely ();
}

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 ("Sheilds: %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);
}

void
usage (void)
{
  printf ("%s\n\n", COPYRIGHT);

  printf ("Usage: robots [OPTION]... [FILE]\n");
  printf ("GNU Robots - game/diversion where you construct a program for a\n");
  printf ("little robot, then watch him explore a world.\n\n");

  printf ("  -f, --map-file=FILE    load this map file\n");
  printf ("  -s, --sheilds=N        set initial sheilds 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 - Returns a true value (1) if filename is a readable file.
   Returns a false value (0) if it is not. */

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

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

      return (0);
    }

  /* Success */

  fclose (stream);
  return (1);
}
