/* setup.c,v 1.4 1995/04/08 19:51:31 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 "libcommon/common.h"
#include "libcommon/sampling.h"
#include "rayshade.h"
#include "defaults.h"
#include "libsurf/surface.h"
#include "libsurf/atmosphere.h"
#include "liblight/light.h"
#include "liblight/infinite.h"
#include "libobj/list.h"
#include "libtext/texture.h"
#include "stats.h"
#include "viewing.h"
#include "picture.h"
#include "zbuf.h"

#ifdef NSPARALLEL
#include "nspar.h"
#include <signal.h>
#include <sys/wait.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif

#ifdef	ultrix
static RETSIGTYPE reaper _PROTO((int));
#else
static RETSIGTYPE reaper _PROTO((void));
#endif
#endif

#ifdef MULTIMAX
#include <parallel.h>
#define SHARED_BYTES	23	/* 2^23 bytes of shared memory */
#endif

extern GeomList *Defstack;
extern SurfList *CurSurf;
extern Medium TopMedium;
extern Light *Lights;
extern FILE *yyin;

/*
 * Set default parameters
 */
void
RSSetup()
{
#ifdef MULTIMAX
  unsigned int bytes;
  
  /*
   * Initialize shared memory stuff.
   */
  bytes = 1 << SHARED_BYTES;
  if (share_malloc_init(bytes) == -1) {
    RLerror(RL_PANIC, "Cannot share_malloc %d bytes.\n",bytes);
  } else
    fprintf(fstats,"Malloced %d bytes of shared memory.\n",
	    bytes);
#endif
  
  Camera.hfov = HFOV;
  Camera.vfov = UNSET;
  Camera.pos.x = EYEX;
  Camera.pos.y = EYEY;
  Camera.pos.z = EYEZ;
  Camera.pos_orig = Camera.pos;
  Camera.pos_trans = (Trans *)NULL;
  Camera.lookp.x = LOOKX;
  Camera.lookp.y = LOOKY;
  Camera.lookp.z = LOOKZ;
  Camera.lookp_orig = Camera.lookp;
  Camera.lookp_trans = (Trans *)NULL;
  Camera.up.x = UPX;
  Camera.up.y = UPY;
  Camera.up.z = UPZ;
  Camera.up_orig = Camera.up;
  Camera.up_trans = (Trans *)NULL;
  Camera.focaldist = UNSET;
  Camera.aperture = 0.;
  
  Screen.xres = Screen.yres = UNSET;
  
  Options.cpp = TRUE;
  Options.maxdepth = MAXDEPTH;
  Options.report_freq = REPORTFREQ;
  Options.jitter = TRUE;
  Options.samples = UNSET;
  Options.gaussian = GAUSSIAN;
  Options.filterwidth = UNSET;
  Options.contrast.r = UNSET;
  Options.ambient.r = Options.ambient.g = Options.ambient.b = 1.0;
  Options.cutoff.r = UNSET;
  Options.cache = TRUE;
  Options.shadowtransp = TRUE;
  Options.crop[LOW][X] = Options.crop[LOW][Y] = 0.;
  Options.crop[HIGH][X] = Options.crop[HIGH][Y] = 1.;
  Stats.fstats = stderr;
  Options.pictfile = stdout;
#ifdef URT
  Options.alpha = TRUE;
  Options.exp_output = FALSE;
#endif
  Options.gamma = GAMMA;
  Options.eyesep = UNSET;
#ifdef LINDA
  Options.workers = WORKERS;
#endif
  
  Options.zbufprint = FALSE;
  Options.zbufname = NULL;
  
  Options.totalframes = 1;
  Options.startframe = 0;
  Options.starttime = 0.;
  Options.framelength = 1.;
  Options.shutterspeed = 0.;
  
#ifdef NSPARALLEL
  Options.mode = MODE_STANDALONE;
  Options.tcpport = -1;
  Options.tcpfd = -1;
#endif
  
  TopMedium.index = DEFAULT_INDEX;
  TopMedium.statten = 1.0;
  NoiseInit();			/* Initialize values for Noise() */
  
  /*
   * Top of object definition stack points to the World object.
   * The World object is always a list.
   */
  Defstack = GeomStackPush(GeomListCreate(), (GeomList *)NULL);
  Defstack->obj->name = strsave("World");
  /* Initialize surface stack */
  CurSurf = SurfPush((Surface *)NULL, (SurfList *)NULL);
}

/*
 * cleanup()
 *
 * Initialize options/variables not set on command line or in input file.
 * Perform sanity checks on widow dimension, maxdepth, etc.
 */
void
RSCleanup()
{
#ifdef AMIGA
  /* probably need to do this unconditionally */
  if (yyin){
    close_yyin();
  }
#endif
  yyin = (FILE *)NULL;	/* mark that we're done reading input */
  
  if (Options.samples == UNSET)
    Options.samples = DEFSAMPLES;
  
  if (Options.filterwidth == UNSET) {
    if (Options.gaussian)
      Options.filterwidth = FILTERWIDTH;
    else
      /* Default box filter width of 1.0 */
      Options.filterwidth = 1.0;
  }
  
  Options.endframe = Options.startframe + Options.totalframes -1;
  
  OpenStatsFile();
  
  ViewingSetup();
  
  if (Options.cutoff.r == UNSET)
    Options.cutoff.r = Options.cutoff.g =
      Options.cutoff.b = DEFCUTOFF;
  
  /*
   * Set contrast.
   */
  if (Options.contrast.r == UNSET) {
    Options.contrast.r = DEFREDCONT;
    Options.contrast.g = DEFGREENCONT;
    Options.contrast.b = DEFBLUECONT;
  }
  
  /*
   * Image gamma is inverse of display gamma.
   */
  if (fabs(Options.gamma) > EPSILON)
    Options.gamma = 1. / Options.gamma;
  else
    Options.gamma = FAR_AWAY;
  
  if (Options.maxdepth < 0)
    Options.maxdepth = 0;
  
  
  LightSetup();
  if (Options.zbufprint)
    ZbufSetup();
}

void
RSStartFrame(frame)
     int frame;
{
  /*
   * Set the frame start time
   */
  Options.framenum = frame;
  Options.framestart = Options.starttime +
    Options.framenum*Options.framelength;
  SamplingSetTime(Options.framestart, Options.shutterspeed,
		  Options.framenum);
  /*
   * Set up viewing parameters.
   */
  RSViewing();
  
  /*
   * Initialize world
   */
  WorldSetup();
}

/*
 * Initialize non-time-varying goodies.
 */
void
RSInitialize(argc, argv)
     int argc;
     char *argv[];
{
#ifdef NSPARALLEL
  int newfd;
  int pid = 0;
  struct sockaddr_in sin;
  int sinlen;
#endif

  /*
   * Initialize variables, etc.
   */
  RSSetup();

  /*
   * Parse options from command line.
   */
  RSOptionsSet(argc, argv);

  /*
   * If we are running in a parallel environment, we need to initialize all 
   * that stuff here, before we begin reading the input file if we are a 
   * server, since the file we use will end up coming from our client.
   *
   * If we are a client, go ahead and parse the input file to be certain 
   * it works.  If we can parse the input file correctly, start contacting 
   * the remote machines.
   */
#ifdef NSPARALLEL
  if (Options.mode == MODE_SERVER) {
    Options.tcpfd = tcp_open(&(Options.tcpport), Options.mode);
    if (Options.tcpfd < 0) {
      perror("tcp_open()");
      exit(1);
    }

    signal(SIGXCPU, SIG_IGN);  /* damn cpu time limits... */

    signal(SIGCHLD, reaper);
    
    if (listen(Options.tcpfd, 4) < 0) {
      perror("listen");
      exit(1);
    }
      
    do {
      fprintf(Stats.fstats, "Waiting for connection (fd = %d)\n",
	      Options.tcpfd);
      
      do {
	sinlen = sizeof(sin);
	newfd = accept(Options.tcpfd, (struct sockaddr *)&sin, &sinlen);
	
	if (newfd < 0 && errno != EINTR) {
	  perror("accept");
	  exit(1);
	}
      } while (newfd < 0);
      
      fprintf(Stats.fstats, "connection: fd = %d, address = %s\n",
	      newfd, inet_ntoa(sin.sin_addr));
      
      /*
       * Now that we are all set up with a new socket and we have a work
       * request (or soon will), create a new process to actually 
       * talk to the client.
       */
      pid = fork();
      
      if (pid < 0) {
	perror("fork");
	exit(1);
      }
      
      /*
       * The parent closes the child's new socket
       */
      if (pid > 0) {
	close(newfd);
      }
      
      /*
       * The child closes the parent's socket, and stores the socket it
       * will be using in Options.tcpfd.  Also, ignore SIGCHLD now.
       */
      if (pid == 0) {
	close(Options.tcpfd);
	Options.tcpfd = newfd;
	signal(SIGCHLD, SIG_IGN);
	setpriority(PRIO_PROCESS, 0, SERVER_PRIO);
      }
      
    } while (pid != 0);
  }
#endif

  /*
   * Process input file.
   */
  if (Options.verbose) {
    VersionPrint();
    fprintf(Stats.fstats,"Reading input file...\n");
    (void)fflush(Stats.fstats);
  }
  RSReadInputFile();

  /*
   * Set variables that weren't set on command line
   * or in input file.
   */
  RSCleanup();

  /*
   * Set sampling options.
   */
  SamplingSetOptions(Options.samples, Options.gaussian,
		     Options.filterwidth);
}

#ifdef AMIGA
close_yyin()
{
  if (!yyin) return;
  
#if defined(CPPSTDIN) && defined(HAVE_POPEN)
  if (Options.cpp)
    pclose(yyin);
  else
#endif
    if (yyin != stdin)
      fclose(yyin);
}
#endif

#ifdef NSPARALLEL
#ifdef	ultrix
static RETSIGTYPE reaper(sig) /* of lost souls */
	int sig;
#else
static RETSIGTYPE reaper() /* of lost souls */
#endif
{
  int status;
  pid_t pid;

  while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0)
    fprintf(stderr, "reap pid %d\n", pid);
}
#endif
