/* $Id:$ */

/* 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 VERSION   "GNU Robots, version 0.80"
#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;
int robot_score = 0;
int robot_energy = 100;
int robot_shields = 10;


/* 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", 0, NULL, 'f'},
			       {NULL, 0, NULL, 0}};

  /* Check command line */

  while ((opt = getopt_long (argc, argv, "Vhf:", 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;

    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);

  /* 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: %d\n", (robot_shields < 0 ? 0: robot_shields));
  printf ("Energy: %d\n", (robot_energy < 0 ? 0: robot_energy));
  printf ("Score: %d\n", robot_score);
  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 ("  -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);
}
