/* options.c,v 1.1.1.1 1995/02/27 07:38:45 explorer Exp */

/*
 * Copyright (C) 1989, 1991, Craig E. Kolb
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

#include "rayshade.h"
#include "options.h"
#include "stats.h"
#include "viewing.h"

RSOptions Options;

typedef struct op_vec {
  char op_char;
  char *op_str;
  int op_nargs;
  char *op_help;
} op_vec;

static void usage _PROTO((void));
static char op_parse _PROTO((op_vec *, int, char *[]));

op_vec rayshade_options[] = {
 { 'A', "-first", 1, "frame\t\tFirst frame number to render" },
#ifdef URT
 { 'a', "-alpha", 0, "\t\tToggle writing of alpha channel" },
#endif
 { 'C', "-cutoff", 3, "r g b\t\tSet adaptive ray tree cutoff value" },
#ifdef URT
 { 'c', "-continue", 0, "\t\tContinue interrupted rendering" },
#endif
 { 'D', "-maxdepth", 1, "depth\t\tSet aximum ray tree depth" },
 { 'E', "-eyesep", 1, "dist\t\tSet eye seperation in stereo pairs" },
#ifdef URT
 { 'e', "-exp", 0, "\t\tWrite exponential RLE file" },
#endif
 { 'F', "-report", 0, "freq\t\tSet frequency of status report" },
 { 'f', "-flipnorm", 0, "\t\tFlip all triangle normals" },
 { 'G', "-gamma", 1, "gamma\t\tUse given gamma correction exponent" },
 { 'g', "-gaussian", 1, "\t\tUse Gaussian pixel filter" },
 { 'h', "-help", 0, "\t\tDisplay this message" },
 { 'j', "-jitter", 0, "\t\tToggle jittered sampling" },
#ifdef AMIGA
 { 'L', "-sas", 0, "\t\tUse SAS cpp rather than the GNU cpp" },
#endif
 { 'l', "-left", 0, "\t\tRender image for left eye view" },
#ifdef URT
 { 'm', "-samplemap", 0, "\t\tOutput sample map in alpha channel" },
#endif
 { 'N', "-frames", 1, "number\t\tTotal number of frames to render" },
 { 'n', "-noshadow", 0, "\t\tDo not render shadows" },
 { 'O', "-outfile", 0, "filename\tSet output file name" },
 { 'o', "-shadowtransp", 0, "\tToggle opacity effect on shadowing" },
 { 'P', "-cpparg", 1, "\t\tOptions to pass to C pre-processor" },
 { 'p', "-preview", 0, "\t\tPreview quality rendering" },
 { 'q', "-quiet", 0, "\t\tRun quietly" },
 { 'R', "-res", 2, "xres ytes\tRender at given resolution" },
 { 'r', "-right", 0, "\t\tRender image for right eye view" },
 { 'S', "-samples", 1, "samples\t\tMax density of samples^2 samples" },
 { 's', "-cache", 0, "\t\tDon't cache shadowing information" },
 { 'T', "-contrast", 3, "r g b\t\tSet contrast threshold (0. - 1.)" },
 { 'u', "-nocpp", 0, "\t\tDo not run input file through cpp" },
 { 'v', "-verbose", 0, "\t\tVerbose output" },
 { 'V', "-vfile", 1, "filename\tWrite verbose output to filename" },
 { 'W', "-window", 4, "x x y y\t\tRender subwindow" },
 { 'X', "-crop", 4, "l r b t\t\tCrop window" },
#ifdef NSPARALLEL
 { 'Y', "-server", 1, "port\t\tPort to listen on" },
 { 'y', "-client", 1, "filename\tfile with list of hostnames and ports" },
#endif
 { 'z', "-zbuf", 1, "filename\tWrite Z bufer output to filename" },
 { '\0', NULL, 0, NULL },
};

void
RSOptionsSet(argc, argv)
     int argc;
     char *argv[];
{
  Options.progname = strsave(argv[0]);
  Options.inputname = (char *)NULL;
#ifdef AMIGA
  Options.SAScpp = 0;
#endif
  
  while (--argc) {
    argv++;
    if (argv[0][0] != '-') {
      /*
       * Must be the input file name.
       * If already given, complain and then exit.
       */
      if (Options.inputname != (char *)NULL) {
	usage();	
	exit(1);
      }
      Options.inputname = strsave(argv[0]);
      continue;
    }
    /* else */
    switch(op_parse(rayshade_options, argc, argv)) {
    case 'A':
      /*
       * First frame number
       */
      Options.startframe = atoi(argv[1]);
      argv++; argc--;
      break;
#ifdef URT
    case 'a':
      Options.alpha = !Options.alpha;
      break;
#endif
    case 'C':
      Options.cutoff.r = atof(argv[1]);
      Options.cutoff.g = atof(argv[2]);
      Options.cutoff.b = atof(argv[3]);
      Options.cutoff_set = TRUE;
      argv += 3; argc -= 3;
      break;
#ifdef URT
    case 'c':
      Options.appending = TRUE;
      break;
#endif
    case 'D':
      Options.maxdepth = atoi(argv[1]);
      Options.maxdepth_set = TRUE;
      argv++; argc--;
      break;
    case 'E':
      Options.eyesep = atof(argv[1]);
      Options.eyesep_set = TRUE;
      argc--; argv++;
      break;
#ifdef URT
    case 'e':
      Options.exp_output = TRUE;
      break;
#endif
    case 'F':
      Options.report_freq = atoi(argv[1]);
      if (Options.report_freq < 1)
	Options.report_freq = 1;
      Options.freq_set = TRUE;
      argv++; argc--;
      break;
    case 'f':
      Options.flipnorm = !Options.flipnorm;
      break;
    case 'G':
      Options.gamma = atof(argv[1]);
      argv++; argc--;
      break;
    case 'g':
      Options.gaussian = !Options.gaussian;
      break;
    case 'h':
      usage();
      exit(0);
      break;
    case 'j':
      Options.jitter = !Options.jitter;
      Options.jitter_set = TRUE;
      break;
#ifdef AMIGA
    case 'L':
      Options.SAScpp = 1;
      break;
#endif
    case 'l':
      Options.stereo = LEFT;
      break;
#ifdef URT
    case 'm':
      Options.samplemap = !Options.samplemap;
      break;
#endif
    case 'N':
      Options.totalframes = atoi(argv[1]);
      Options.totalframes_set = TRUE;
      argv++; argc--;
      break;
    case 'n':
      Options.no_shadows = !Options.no_shadows;
      break;
    case 'O':
      Options.imgname = strsave(argv[1]);
      argv++;
      argc--;
      break;
    case 'o':
      Options.shadowtransp = !Options.shadowtransp;
      break;
    case 'P':
      Options.cppargs = argv[1];
      argv++; argc--;
      break;
    case 'p':
      /*
       * Preview-quality rendering
       * no shadows
       * max depth of 0
       * 1 jittered sample/pixel
       */
      Options.no_shadows = TRUE;
      Options.maxdepth = 0;
      Options.maxdepth_set = TRUE;
      Options.jitter = TRUE;
      Options.jitter_set = TRUE;
      Options.samples = 1;
      Options.samples_set = TRUE;
      break;
    case 'q':
      Options.quiet = TRUE;
      break;
    case 'R':
      Screen.xres = atoi(argv[1]);
      Screen.yres = atoi(argv[2]);
      Options.resolution_set = TRUE;
      argv += 2;
      argc -= 2;
      break;
    case 'r':
      Options.stereo = RIGHT;
      break;
    case 'S':
      Options.samples = atoi(argv[1]);
      if (Options.samples < 1)
	Options.samples = 1;
      Options.samples_set = TRUE;
      argv++; argc--;
      break;
    case 's':
      Options.cache = !Options.cache;
      break;
    case 'T':
      Options.contrast.r = atof(argv[1]);
      Options.contrast.g = atof(argv[2]);
      Options.contrast.b = atof(argv[3]);
      Options.contrast_set = TRUE;
      argv += 3;
      argc -= 3;
      break;
    case 'u':
      Options.cpp = !Options.cpp;
      break;
    case 'v':
      Options.verbose = TRUE;
      break;
    case 'V':
      Options.verbose = TRUE;
      if (argv[1][0] == '-') {
	/*
	 * User probably blew it, and it's difficult to remove a file
	 * that begins with '-'...
	 */
	usage();
	exit(2);
      }
      Options.statsname = strsave(argv[1]);
      OpenStatsFile();
      argv++; argc--;
      break;
    case 'W':
      Options.window[LOW][X] = atoi(argv[1]);
      Options.window[HIGH][X] = atoi(argv[2]);
      Options.window[LOW][Y] = atoi(argv[3]);
      Options.window[HIGH][Y] = atoi(argv[4]);
      Options.window_set = TRUE;
      argv += 4; argc -= 4;
      break;
    case 'X':
      Options.crop[LOW][X] = atof(argv[1]);
      Options.crop[HIGH][X] = atof(argv[2]);
      Options.crop[LOW][Y] = atof(argv[3]);
      Options.crop[HIGH][Y] = atof(argv[4]);
      Options.crop_set = TRUE;
      argv += 4; argc -= 4;
      break;
#ifdef NSPARALLEL
    case 'Y':
      Options.mode = MODE_SERVER;
      Options.tcpport = atoi(argv[1]);
      Options.tcpfd = -1;  /* not oened yet */
      argv++; argc--;
      break;
    case 'y':
      Options.mode = MODE_CLIENT;
      Options.hostfile = strsave(argv[1]);
      argv++; argc--;
      break;
#endif
    case 'z':
      Options.zbufprint = TRUE;
      if (argv[1][0] == '-') {
	/*
	 * User probably blew it, and it's difficult to remove a file
	 * that begins with '-'...
	 */
	usage();
	exit(2);
      }
      Options.zbufname = strsave(argv[1]);
      argv++; argc--;
      break;
    default:
      RLerror(RL_PANIC,"Bad argument: %s\n",argv[0]);
    }
  }
}

void
RSOptionsList()
{
  if (Options.totalframes > 1) {
    fprintf(Stats.fstats,"Rendering frames %d through %d.\n",
	    Options.startframe, Options.endframe);
  } else {
    fprintf(Stats.fstats,"Rendering frame %d.\n", Options.startframe);
  }
  
  fprintf(Stats.fstats,"Screen resolution: %d x %d\n",
	  Screen.xres,Screen.yres);
  fprintf(Stats.fstats,"Image window: (%d - %d), (%d - %d).\n",
	  Screen.minx, Screen.maxx, Screen.miny, Screen.maxy);
  
  if (Options.jitter)
    fprintf(Stats.fstats,"Using jittered sampling, ");
  fprintf(Stats.fstats,"Max sampling rate %d %s/pixel.\n",
	  Options.samples*Options.samples,
	  Options.samples == 1 ? "sample" : "samples");
  
  fprintf(Stats.fstats,
	  "Maximum contrast: %g red, %g green, %g blue.\n",
	  Options.contrast.r, Options.contrast.g,
	  Options.contrast.b);
  fprintf(Stats.fstats,"Maximum ray depth: %d.  Cutoff thresh: %g %g %g.\n",
	  Options.maxdepth,
	  Options.cutoff.r, Options.cutoff.g, Options.cutoff.b);
  if (Options.stereo == LEFT)
    fprintf(Stats.fstats,"Rendering image for left eye.\n");
  else if (Options.stereo == RIGHT)
    fprintf(Stats.fstats,"Rendering image for right eye.\n");
  if (Options.no_shadows) {
    fprintf(Stats.fstats,"No shadows are rendered.\n");
  } else if (Options.shadowtransp) {
    fprintf(Stats.fstats,
	    "Object opacity affects depth of shadowing.\n");
  }
  if (!Options.cache)
    fprintf(Stats.fstats,"Shadow caching is disabled.\n");
  if (Options.totalframes != 1)
    fprintf(Stats.fstats,"Rendering %d frames.\n",
	    Options.totalframes);
  if (!Options.cpp)
    fprintf(Stats.fstats,"Input will not be run through cpp.\n");
#ifdef NSPARALLEL
  if (Options.mode == MODE_SERVER)
    fprintf(Stats.fstats, "Running as a server on TCP port %d\n",
	    Options.tcpport);
  if (Options.mode == MODE_CLIENT)
    fprintf(Stats.fstats, "Running as a client of hosts in file %s\n",
	    Options.hostfile);
#endif
}

static void
usage()
{
  struct op_vec *op;
  
  fprintf(stderr,"usage: %s [options] [filename]\n", Options.progname);
  fprintf(stderr,"Where options include:\n");
  
  op = rayshade_options;
  
  while(op->op_str != NULL) {
    fprintf(stderr, "\t-%c | %s\t%s\n", op->op_char, op->op_str, op->op_help);
    op++;
  }
}

static char
op_parse(op_vec *op, int argc, char **argv)
{
  while(op->op_str != NULL) {
    if (argv[0][2] == '\0' && argv[0][1] == op->op_char)
      return op->op_char;
    else if (!strcmp(op->op_str, argv[0]))
      return op->op_char;
    op++;
  }

  /* Didn't find anything.  Return a question mark. */
  return '?';
}
