#ifndef lint
static char *rcsid="$Header: /mit/hst/src/event/RCS/Event.c,v 2.4 91/01/10 17:18:12 tldavis Exp $";
#endif
/* 
 * Event.c: Copyright 1990 Timothy L. Davis
 *
 * These functions deal with interaction between the simulation,
 * the user interface, and the operating system.
 * The main top-level loop DispatchDataAndEvents() is also here.
 *
 * To use zephyr windowgrams to report errors, define the symbol "ZEPHYR".
 * To use a pipe to store simulation data, define the symbol "USEPIPE".
 *
 * $Log:	Event.c,v $
 * Revision 2.4  91/01/10  17:18:12  tldavis
 * Fixed problem with POLLING.  If there is nothing to do,
 * the simulator will now BLOCK in XtNextEvent() instead of constantly
 * checking XPending() and checking if more simulation data is needed.
 * (This bug was in DispatchDataAndEvents().)
 * ,
 * 
 * Revision 2.3  90/08/28  14:36:15  tldavis
 * Fixed error file posting.
 * 
 * Revision 2.1  90/08/18  18:32:20  tldavis
 * General cleanup.
 * 
 */

#include <X11/Intrinsic.h>
#include <fcntl.h>
#include <stdio.h>
#ifdef RSAIX
extern void *malloc(), *realloc();
#else
#ifdef __STDC__
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#endif
#include "../sim/CVDefs.h"
#include "../data/Parameter.h"
#include "Event.h"

extern char *CVLib; /* from Main.c */

/* samn includes */
#include <sys/types.h>
#include <sys/time.h>
/* end samn includes */

#ifdef NOTDEF
#define ZEPHYR /* Use zwrite for Project Athena error notification. */
#endif
extern int simulate();
extern Display *dpy;
extern Widget paramWidget;    /* from ../cv/Interface.c */
extern caddr_t paramWinData; /* from ../cv/Interface.c */
extern void ShowParameters();/* from ../cv/Interface.c */

/*
 * record of window and update/data procs
 */
typedef struct {
  Widget widget;
  void (*modProc)();
  void (*dataProc)();
} DataWidgetRec;


static int FPERROR = 0; 
/*PUBLIC*/ /*ARGSUSED*/
#ifdef SVR4
void fpe_in_simulate(int foo)
#else
int fpe_in_simulate(int foo)
#endif
{ 
  PostErrorString("Floating Point Error in simulate.c");
  FPERROR = 1; /* Not an extern variable, so OK for Sun-4 -O3 optimization */
#ifndef SVR4
  return(0);
#endif
}

/*PUBLIC*/ 
void PostErrorString(s)
     char *s;
{  
#ifdef ZEPHYR
  char ss[256];
  sprintf(ss, "zwrite -q $USER -m %s\n",s);
  system(ss);
#else
  XtWarning(s);
#endif
}

/*PUBLIC*/ 
void PostErrorFile(s)
     char *s;
{  
#ifdef ZEPHYR
  char ss[256];
  sprintf(ss,"zwrite -q $USER < %s",s);
  system(ss);
#else
  int c;
  FILE *file;
  char buf[2048], *p, *end;

  XtWarning("SIMULATOR ERROR:\n");
  if ((file = fopen(s,"r")) != NULL) {
    p = buf;
    end = &buf[2047];
    while ((c=getc(file))!=EOF && p < end) *p++ = (char) c;
    fclose(file);
    *p++ = '\n';
    XtWarning(buf);
  }
#endif
 }

static double x[10];
/*PUBLIC*/ int Stopped;

static DataWidgetRec *DWlist = (DataWidgetRec *)0, *dwLast;
static int DWlen = 0, DWactive = 0;

/*
 * Register a widget to receive simulation data from DispatchData()
 * Takes widget window, parameter modification handler modProc(), and
 * data reception handler dataProc():
 *
 * void (*modProc)(Widget widget,double x[], int  place,int type, double value);
 * void (*dataProc)(Widget widget, double[] pressures);
 */
/*PUBLIC*/ void AddDataWidget(widget, modProc, dataProc)
Widget widget;
void (*modProc)(), (*dataProc)();
{
  int l = DWlen;
  DWactive++;
  if (l) {
    while (l--)
      if (DWlist[l].widget == (Widget) 0) {/*0 means unused record (deleted) */
	DWlist[l].widget = widget;
	DWlist[l].modProc = modProc;
	DWlist[l].dataProc = dataProc;
	return;
    }
    DWlist = (DataWidgetRec *)realloc((char *)DWlist,
				      (2+DWlen)*sizeof(DataWidgetRec));
  } else {
    DWlist = (DataWidgetRec *)malloc((2+DWlen)*sizeof(DataWidgetRec));
  }
  DWlist[DWlen].widget = widget;
  DWlist[DWlen].modProc = modProc;
  DWlist[DWlen].dataProc = dataProc;
  DWlist[++DWlen].widget = (Widget)0; /* end of list */
  dwLast = &DWlist[DWlen];
}

/*
 * Remove widget from the list of those receiving data packets
 */
/*PUBLIC*/ void DeleteDataWidget(widget)
Widget widget;
{
  int l = DWlen;
  while (l--)
    if (DWlist[l].widget && 
	(DWlist[l].widget == widget || XtParent(DWlist[l].widget) == widget)) 
    {
      DWlist[l].widget = (Widget)0; /* delete this entry */
      DWactive--;
    }
}

void UpdateParamWindow() 
{
  if (paramWidget) ShowParameters((Widget)0, paramWinData, (caddr_t)0);
}

/*
 * Get data from pipe fd, and broadcast it to all widgets which have requested
 * a simulation data stream (used in DispatchDataAndEvents).
 */
static double currentTime = 0.0; /* the current time for Reset() */

#ifdef USEPIPE
static void DispatchData(fd, dataWidget)
int fd;
DataWidgetRec *dataWidget; 
{
  char dataType;
  double pressures[10];
  fd_set readfds;
  struct timeval timeout;
  DataWidgetRec *dw;
    
  if (!DWactive) return;
  if (DWactive < 0 || DWlen < 0)  fprintf(stderr,"Bad DataPlot list!\n");
  FD_ZERO(&readfds);
  FD_SET(fd, &readfds);
  timerclear(&timeout);
  while (select(FD_SETSIZE, &readfds,(fd_set *)0,(fd_set *)0,&timeout) != 0) {
    read(fd, &dataType, sizeof(dataType));
    switch (dataType) {
    case DATA:
      if (read(fd,pressures,sizeof(pressures[0])*10)==sizeof(pressures[0])*10){
	currentTime = pressures[6];
	for (dw = dataWidget; dw != dwLast;  dw++)
	  if (dw->widget) (*dw->dataProc)(dw->widget, pressures);
      }
      break;
    }
  }
}
#else

#define SIMBUFLENGTH 110
static double SimBuffer[SIMBUFLENGTH]; /* Storage for simulation data between
					* data production and plotting.
					*/
static int SimBufHead = 0, SimBufTail = 0;

static void DispatchData(dataWidget)
DataWidgetRec *dataWidget; 
{
  double pressures[10];
  DataWidgetRec *dw;
  int i;
    
  if (!DWactive) return;
  if (DWactive < 0 || DWlen < 0)  fprintf(stderr,"Bad DataPlot list!\n");
  while (SimBufHead != SimBufTail) {
    for (i=0; i<10; i++) {
      pressures[i] = SimBuffer[SimBufHead++];
      if (SimBufHead >= SIMBUFLENGTH) SimBufHead -= SIMBUFLENGTH;
      if (SimBufHead == SimBufTail) 
	i=100;
    }
    if (i < 100) { /* Got a complete set of pressures to dispatch */
      currentTime = pressures[6];
      for (dw = dataWidget; dw != dwLast;  dw++)
	if (dw->widget) (*dw->dataProc)(dw->widget, pressures);
    }
  }
}
#endif

static Boolean Flushing=False; /* Whether to flush simulation data or use it*/

/*PUBLIC*/ void SimDataEnq(pressures)
double *pressures;
{
  if (Flushing==True || FPERROR) return;
  else 
#ifndef USEPIPE
    {
      register int i;
      for (i = 0; i < 10 ; i++) {
	SimBuffer[SimBufTail++] = pressures[i];
	if (SimBufTail >= SIMBUFLENGTH) SimBufTail -= SIMBUFLENGTH;
	if (SimBufTail == SimBufHead) {
	  PostErrorString("Simulation Buffer Overflow!");
	  SimBufTail--;
	}
      }
    }
#else
    {
      int c, len;
      char *start;
      static char header=DATA;
      while (write(SimToPlotPipe[1], &header, sizeof(char)) < sizeof(char)) ;
      start = (char *) pressures; 
      len=sizeof(pressures[0])*10;
      while ((c=write(SimToPlotPipe[1], start, len)) != len) 
	{ len -= c; start += c; }
    }
#endif
}
 
/*************************************************************************/
/*
 *
 * The following help the simulator to communicate with the plot process.
 *
 */

#ifdef USEPIPE
static int SimToPlotPipe[2]; /* pipe from simulator -> plotting process */
#endif
/*
 * This is the main top-level control loop.  Process all X events, then
 * get the next data from the simulator process if there are any active
 * plots.  If no active plots exist, the loop will block in XtNextEvent()
 * processing only user interactions, without polling, to save CPU time
 * for other possible processes.
 */
/*PUBLIC*/ void DispatchDataAndEvents() /* never returns */
{
  XEvent ev;
  double old_x[10];
  int failures = 0, i;

#ifdef USEPIPE
  if (fd) SimToPlotPipe[0] = fd;
#endif
  for (;;) {
    while (XPending(dpy) || !DWactive || Stopped)  
      /* Allow to block only if we have no reason to calculate data */
      { XNextEvent(dpy, &ev); XtDispatchEvent(&ev); }

#ifdef USEPIPE
    DispatchData(SimToPlotPipe[0], DWlist);
#else
    DispatchData(DWlist);
#endif
    /* Compute the next bit of simulation data, taking care to 
     * restart on the event of a simulation failure.
     */
    x[6] = currentTime; 
    Flushing = False;
    if (simulate(x, 10)) {
      for (i = 0; i < 10; i++) old_x[i] = x[i];
      failures = 0;
      currentTime = x[6];
    } else if (++failures > 50 || FPERROR) {
      /* hope the user resets a parameter, but if not */
      char path[1024];
      sprintf(path,"%s/convergence.failure", CVLib);
      PostErrorFile(path);
      Stopped = 1; /* quit simultion process; user must re-start */
      failures = 0;
      FPERROR = 0;
      for (i = 0; i<10; i++) x[i] = old_x[i];
    }
  }
}

/*PUBLIC*/ void SimFlush() /* Clears data queue. */
{
#ifdef USEPIPE
  DispatchData(SimToPlotPipe[0], DWlist);
#else
  DispatchData(DWlist);
#endif
}

/*
 * PUBLIC routines handling communication with the SIMULATOR through
 * Unix pipes or a manually-handled buffer.
 *
 */
/*PUBLIC*/ void SendToSim(place, type, value) /*send param change to sim.proc*/
int place, type;
double value;
{
  setval(x, place, type, value);
}

/*PUBLIC*/ void MakeSimulator(parameters, starttime) 
SIMULATION *parameters;
double starttime;
{
  static int i; 
#ifdef USEPIPE
  static int devnull =-1;
  if (pipe(SimToPlotPipe) != 0) {
    fprintf(stderr, "Pipe creation failed.\n");
  }
  if (devnull < 0) devnull = open("/dev/null",O_WRONLY, 0);
#endif
  initeqnvars(parameters);
  setupCoeffs();
  btconst();
  estimate(parameters, x); /* sets x[9] to 0, start a new beat. */
  Flushing = True;
  for (i=0; i < 4; i++)
    simulate(x, TBLSEGS);
  Flushing = False;
  Stopped = 0;
  x[6] = currentTime = starttime;
}

/* send signal to pause simulation */
/*PUBLIC*/ void StopSimulator()
{
  Stopped = 1;
}

/* resume simulation where we left off, no steady-state finding. */
/*PUBLIC*/ int ResumeSimulator()
{
  Stopped = 0;
#ifdef USEPIPE
  return(SimToPlotPipe[0]);
#else
  return(1);
#endif
}

/* Reset simulator with a new data state.  Find new steady state. */
/*PUBLIC*/
void ResetSimulator(parameters)
SIMULATION *parameters;
{
#ifdef USEPIPE
  close(SimToPlotPipe[0]);/* discard the data in the pipe. */
  close(SimToPlotPipe[1]);
#endif
  MakeSimulator(parameters, currentTime); /* Continue from current time */
}
