/********************************************/
/*    Copyright 1997 MetaCard Corporation   */
/*    This source code may be used as a     */
/*    template for building external        */
/*    processes for use with Rev or MC.     */
/*    All Other Rights Reserved             */
/********************************************/
/*
  MetaCard template for external commands and functions
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef X11
#include <sys/time.h>
#include <values.h>

#ifdef SELECT
#ifndef LINUX
/* some systems don't have select.h and/or stream.h.  If yours doesn't
   delete the offending #include */
#include <sys/select.h>
#include <sys/stream.h>
#endif
#else
#include <poll.h>
#endif
#include <X11/Xlib.h>
#elif defined WIN32
#include <time.h>
#include <windows.h>
#elif defined MACOS
#include <Quickdraw.h>
#include <QDOffscreen.h>
#endif
#include "XCmdGlue.h"
#include "external.h"

/*
 * This global table has one entry per XCMD/XFCN.
 * The first entry is the name of the handler
 * The second entry is the type (XCOMMAND or XFUNCTION)
 * The third entry is a space for the atom (used by MetaCard)
 * The fourth entry is the name of the 'C' function to call
 * The fifth entry is a callback called if the user aborts
 * Note that the last entry in the table is a NULL entry
 * which is used to measure the size of the table.
 */

Xternal Xtable[] = {
{"xgetvar", XFUNCTION, 0, XGetVar, XCabort},
{"xsetvar", XCOMMAND, 0, XSetVar, XCabort},
{"xsetarray", XCOMMAND, 0, XSetArray, XCabort},
{"xgetarray", XFUNCTION, 0, XGetArray, XCabort},
{"convolve", XCOMMAND, 0, XConvolve, XCabort},
{"sprintf", XCOMMAND, 0, XCsprintf, XCabort},
{"xatan2", XFUNCTION, 0, XFatan2, XCabort},
{"callbacktests", XCOMMAND, 0, XCcallbacktests, XCabort},
{"life_init", XFUNCTION, 0, XFlife_init, XCabort},
{"life_create", XCOMMAND, 0, XClife_create, XCabort},
{"life_delay", XCOMMAND, 0, XClife_delay, XCabort},
{"life_cease", XCOMMAND, 0, XClife_cease, XCabort},
{"", XNONE, 0, NULL, NULL}
};

/*
 * This string identifies this external to the engine
 */
char Xname[] = "Template Xternals";

/*
 * local variables
 */
#define GRID_SIZE 40
#define CELL_SIZE 8
static char g1[GRID_SIZE + 2][GRID_SIZE + 2];
static char g2[GRID_SIZE + 2][GRID_SIZE + 2];
static char (*oldgrid)[GRID_SIZE + 2];
static char (*newgrid)[GRID_SIZE + 2];
#if defined X11
static GC gc;
static Pixmap pm;
static unsigned long livecolor;
static unsigned long deadcolor;
#elif defined WIN32
static HBITMAP bm;
static HBITMAP oldbitmap;
static HDC memhdc;
static HBRUSH livebrush; //handle to red color brush
static HBRUSH deadbrush; //handle to gray color brush
static HBRUSH oldbrush;  //old brush
#elif defined MACOS
static GWorldPtr gw;
static RGBColor livecolor;
static RGBColor deadcolor;
static Rect rect;
static CGrafPtr oldport;
static GDHandle olddevice;
#endif


#ifdef __STDC__
void XGetVar(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XGetVar(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
	int retvalue;
	char *key = NULL;
	char *retstr = NULL;
	*pass = False;
	*error = False;
	if (nargs == 2){
		MCstring val = {NULL,0};
			key = args[1];
		GetVariableEx(args[0],key,&val,&retvalue);
		if (val.sptr != NULL){
			retstr = malloc(val.length+1);
			retstr[val.length] = '\0';
			memcpy(retstr,val.sptr,val.length);
		}
	}
	if (retstr == NULL)
	 retstr = calloc(1, 1);
	*retstring = retstr;
}

#ifdef __STDC__
void XSetVar(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XSetVar(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
	 {
	int retvalue;
	char *key = NULL;
	char *retstr = NULL;
	*pass = False;
	*error = False;
	if (nargs == 3){
		MCstring tval;
		tval.sptr = args[2];
		tval.length = strlen(args[2]);
				key = args[1];
			SetVariableEx(args[0],key,&tval,&retvalue);
	}
	if (retstr == NULL)
		retstr = calloc(1, 1);
	*retstring = retstr;
}

#ifdef __STDC__
void XSetArray(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XSetArray(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
  int retvalue;
  char *retstr = NULL;
  char **keys = NULL;
  int nelements = 0;
  *pass = False;
  *error = False;
  if (nargs >= 2)
    {
      char *tptr = args[1];
      char *lastptr = NULL;
      char *keyptr = NULL;
      MCstring *tstrings;
      Bool usekeys = False;
      int count = 1;
      if (nargs == 3 && (strncmp(args[2],"TRUE",4) == 0))
	usekeys = True;
      do{
	if (*tptr == '\n')
	  count++;
      }while(*++tptr);
      if (usekeys){
	keys = (char **)malloc(sizeof(char *)*count);
	memset(keys,0,sizeof(char *)*count);
      }
      tstrings = (MCstring *)malloc(sizeof(MCstring)*count);
      tptr = args[1];
      lastptr = tptr;
      while (1){
	if (*tptr == '\n' || *tptr == '\0'){
	  if (usekeys == True){
	    keyptr = lastptr;
	    while(++keyptr < tptr){
	      if (*keyptr == ','){
		*keyptr = '\0';
		keys[nelements] = lastptr;
		lastptr = keyptr+1;
		break;
	      }
	    }
	  }
	  tstrings[nelements].sptr = lastptr;
	  tstrings[nelements].length = tptr - lastptr;
	  lastptr = tptr+1;
	  nelements++;
	  if (*tptr == '\0')
	    break;
	}
	tptr++;
      }
      SetArray(args[0],nelements,tstrings,keys,&retvalue);
      free(tstrings);
      if (usekeys == True)
	free(keys);
    }
  if (retstr == NULL)
    retstr = calloc(1, 1);
  *retstring = retstr;
}

#ifdef __STDC__
void XGetArray(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XGetArray(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
  int retvalue;
  char *retstr = NULL;
  MCstring *values = NULL; 
  char **keys = NULL;
  int nelements = 0;
  char *tptr = NULL;
  int size = 0;
  int i;
  *pass = False;
  *error = False;
  GetArray(args[0],&nelements,values,keys,&retvalue);
  if (nelements != 0){
    values = malloc(sizeof(MCstring) * nelements);
    keys = malloc(sizeof(char *) * nelements);
    GetArray(args[0],&nelements,values,keys,&retvalue);
    for (i = 0; i < nelements; i++){
      MCstring *s = &values[i];
      char *key = keys[i];
      int klength = strlen(key);
      tptr = realloc(tptr, size + s->length + 1 + klength + 1);
      memcpy(&tptr[size],key,klength);
      size += klength;
      tptr[size++] = '\t';
      memcpy(&tptr[size],s->sptr,s->length);
      size += s->length;
      tptr[size++] = '\n';
    }
    tptr[--size] = '\0';
    retstr = tptr;
    free(values);
    free(keys);
  }
  if (retstr == NULL)
    retstr = calloc(1, 1);
  *retstring = retstr;
}

#define RGBAtoFOUR(r, g, b, a) ((int)(((a) << 24) | ((r) << 16)\
         | ((g) << 8) | (b)))
#define FOURtoB(rgb) ((rgb) & 0xff)
#define FOURtoG(rgb) (((rgb) >> 8) & 0xff)
#define FOURtoR(rgb) (((rgb) >> 16) & 0xff)
#define FOURtoA(rgb) ((rgb) >> 24) 

#ifdef __STDC__
	int get255(int n)
#else
	int get255(n)
	int n;
#endif
{
	if (n > 254) return 255;
	if (n < 0) return 0;
	return n;
}

	 
#ifdef __STDC__
	 void XConvolve(char *args[], int nargs, char **retstring,
		 Bool *pass, Bool *error)
#else
		 void XConvolve(args, nargs, retstring, pass, error)
		 char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
	 {
		 int retvalue;
		 *pass = False;
		 *error = False;
		 if (nargs == 6){
			 MCstring image;
			 char emp[] = "";
			 GetVariableEx(args[0],emp,&image,&retvalue); //get image
			 if (image.sptr != NULL){
				 MCstring matrixelement;
				 char *convimage;
				 int convmatrix[3][3];
				 int width,height, bytesperline, scale,i,j;
				 char keybuffer[8];
				 Bool success = True;
				 width = atoi(args[1]);  //argument 2 contains width of image
				 height = atoi(args[2]); //argument 3 contains height of image
				 scale = atoi(args[3]); //get scale for convolution operation
				 bytesperline = image.length/height; //need this info for convolution filter
				 convimage = malloc(image.length); //allocate space for convoluted image (same size as original)
				 for (i = 0; i < 3; i++){ //populate matrix with elements from MC array
					 for (j = 0; j < 3; j++) {
						 sprintf(keybuffer ,"%d,%d", i+1, j+1); //create key (our MC array starts at 1)
						 GetVariableEx(args[4], keybuffer, &matrixelement,&retvalue); //get element from array
						 if (matrixelement.sptr == NULL) { //if not found throw an error
							 *retstring = istrdup("element not found in matrix");
							 success = False;
							 break;
						 }
						 else
							 convmatrix[i][j] = atoi(matrixelement.sptr);
					 }
				 }
				 if (success == True) {
					 MCstring newimage;
					 int x,y;
					 for (y = 1; y < height-2; y++) {
						 int *O = (int *)&convimage[y * bytesperline];
						 int *T = (int *)&image.sptr[(y-1) * bytesperline];
						 int *C = (int *)&image.sptr[(y) * bytesperline];
						 int *B = (int *)&image.sptr[(y+1) * bytesperline];
						 for (x = 1; x < width - 2;x++) {
							 int red = get255 ((
								 (FOURtoR(T[x-1])*convmatrix[0][0]) + (FOURtoR(T[x])*convmatrix[0][1]) + (FOURtoR(T[x+1])*convmatrix[0][2])
								 + (FOURtoR(C[x-1])*convmatrix[1][0]) + (FOURtoR(C[x])*convmatrix[1][1]) + (FOURtoR(C[x+1])*convmatrix[1][2])
								 + (FOURtoR(B[x-1])*convmatrix[2][0]) + (FOURtoR(B[x])*convmatrix[2][1]) + (FOURtoR(B[x+1])*convmatrix[2][2]))/scale);
							 int green = get255 ((
								 (FOURtoG(T[x-1])*convmatrix[0][0]) + (FOURtoG(T[x])*convmatrix[0][1]) + (FOURtoG(T[x+1])*convmatrix[0][2])
								 + (FOURtoG(C[x-1])*convmatrix[1][0]) + (FOURtoG(C[x])*convmatrix[1][1]) + (FOURtoG(C[x+1])*convmatrix[1][2])
								 + (FOURtoG(B[x-1])*convmatrix[2][0]) + (FOURtoG(B[x])*convmatrix[2][1]) + (FOURtoG(B[x+1])*convmatrix[2][2]))/scale);
							 int blue = get255 ((
								 (FOURtoB(T[x-1])*convmatrix[0][0]) + (FOURtoB(T[x])*convmatrix[0][1]) + (FOURtoB(T[x+1])*convmatrix[0][2])
								 + (FOURtoB(C[x-1])*convmatrix[1][0]) + (FOURtoB(C[x])*convmatrix[1][1]) + (FOURtoB(C[x+1])*convmatrix[1][2])
								 + (FOURtoB(B[x-1])*convmatrix[2][0]) + (FOURtoB(B[x])*convmatrix[2][1]) + (FOURtoB(B[x+1])*convmatrix[2][2]))/scale);
							 O[x] = RGBAtoFOUR(red,green,blue,0x00);
						 }
					 }
					 newimage.sptr = convimage; 
					 newimage.length = image.length;
					 SetVariableEx(args[5],"",&newimage,&retvalue); //set value of variable to resulting image
					 *retstring = calloc(1, 1);
				 }
				 free(convimage);
			 }
		 }
		 else
			 *retstring = istrdup("must pass 6 arguments");
	 }

/*
 * Call the c library sprintf with a format and argument string.
 * A more complete routine would include checks on the the validity
 * of the arguments and allow more than one argument to be passed.
 * This routine should actually be an XFCN, since it returns a value.
 */
#ifdef __STDC__
void XCsprintf(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XCsprintf(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
  fprintf(stderr, "XCsprintf called with nargs %d, arg1 is %s\n",
	  nargs, args[0]);
  *pass = False;
  if (nargs != 2) {
    /* only set error to true when a syntax error occurs, as it
       causes the script to stop execution at this point */
    *error = True;
    *retstring = istrdup("printf: must pass 2 arguments");
  }
  else {
    *error = False;
    *retstring = malloc(strlen(args[0]) + strlen(args[1]) + 1);
    sprintf(*retstring, args[0], args[1]);
  }
}

/*
 * Call the c library atan2 with the required two arguments.
 * A more complete routine would include setting up a matherr
 * routine and checking errno for DOMAIN errors.
 */
#ifdef __STDC__
void XFatan2(char *args[], int nargs, char **retstring,
	     Bool *pass, Bool *error)
#else
void XFatan2(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
  fprintf(stderr, "XCatan2 called with nargs %d, arg1 is %s\n",
	  nargs, args[0]);
  *pass = False;
  if (nargs != 2) {
    /* only set error to true when a syntax error occurs, as it
       causes the script to stop execution at this point */
    *error = True;
    *retstring = istrdup("atan2: must pass 2 arguments");
  }
  else {
    *error = False;
    *retstring = malloc(16);
    sprintf(*retstring, "%G", atan2(atof(args[0]), atof(args[1])));
  }
}

/*
 * Call back into MetaCard and get or set the contents of the
 * objects specified in the argument list.
 */
#ifdef __STDC__
void XCcallbacktests(char *args[], int nargs, char **retstring,
		     Bool *pass, Bool *error)
#else
void XCcallbacktests(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
  int retvalue;
  char buffer[256];
  char *mcstring;
#ifdef MACOS
  unsigned long seconds;
#endif

/* activate a card script handler */
  sprintf(buffer, "doSomething %s", args[0]);
  SendCardMessage(buffer, &retvalue);

/* tell the engine to execute a command */
#ifndef MACOS
  sprintf(buffer, "put \"Message time %d\" into field %s",
	  (int)time(NULL), args[1]);
#else  //MAC platform
  GetDateTime(&seconds);
  sprintf(buffer, "put \"Message time %d\" into field %s",
	  (int)seconds, args[1]);
#endif	 
  SendMCMessage(buffer, &retvalue);

/* get a global, tell MC to add 1 to the result, then set the global */
  mcstring = GetGlobal(args[2], &retvalue);
  sprintf(buffer, "%s + 1", mcstring);
  free(mcstring);
  mcstring = EvalExpr(buffer, &retvalue);
  SetGlobal(args[2], mcstring, &retvalue);
  free(mcstring);

/* The first parameter to field routines should be:
   "true" to look for the field on the card,
   "false" to look only on the background,
   "" to look on both the card and the background
   or a group name to look for the field in that group. */

  mcstring = GetFieldByName("", args[3], &retvalue);
  SetFieldByName("", args[6], mcstring, &retvalue);
  free(mcstring);

  mcstring = GetFieldByNum("", atoi(args[4]), &retvalue);
  SetFieldByNum("", atoi(args[7]), mcstring, &retvalue);
  free(mcstring);
  
  mcstring = GetFieldById("", atoi(args[5]), &retvalue);
  SetFieldById("", atoi(args[8]), mcstring, &retvalue);
  free(mcstring);

/* get an expression from a local variable, evaluate the expression
   in the local context, and put the value back into the local variable */
  mcstring = GetVariable(args[9], &retvalue);
  strcpy(buffer, mcstring);
  free(mcstring);
  mcstring = EvalExpr(buffer, &retvalue);
  SetVariable(args[9], mcstring, &retvalue);
  free(mcstring);

  *pass = False;
  *error = False;
  *retstring = istrdup("callbacktest done");
}

/*
 * Do one generation of the life simulation, coloring the squares
 * for each of the live cells.  This function is called at on the
 * idle event, the rate of which is controlled by the X_set_idle_rate
 * function, which is called in the XClife_delay function.
 */
void life_generation()
{
  int retvalue; 
  int x, y;

#ifdef X11
  if (pm == 0)
    return;
#elif defined WIN32
  if (bm == NULL)
    return;
#elif defined MACOS
  if (gw == NULL)
    return;
#endif
  if (oldgrid == g1) {
    oldgrid = g2;
    newgrid = g1;
  }
  else {
    oldgrid = g1;
    newgrid = g2;
  }
  for (x = 1 ; x <= GRID_SIZE ; x++) {
    for (y = 1 ; y <= GRID_SIZE ; y++) {
      int neighbors = oldgrid[x - 1][y - 1] + oldgrid[x][y - 1]
	+ oldgrid[x + 1][y - 1] + oldgrid[x - 1][y] + oldgrid[x + 1][y]
	+ oldgrid[x - 1][y + 1] + oldgrid[x][y + 1] + oldgrid[x + 1][y + 1];
      if (neighbors == 2)
	newgrid[x][y] = oldgrid[x][y];
      else {
	if (neighbors == 3)
	  newgrid[x][y] = 1;
	else
	  newgrid[x][y] = 0;
#ifdef X11
	if (newgrid[x][y] != oldgrid[x][y]) {
	  if (newgrid[x][y])
	    XSetForeground(MCdpy, gc, livecolor);
	  else
	    XSetForeground(MCdpy, gc, deadcolor);
	  XFillRectangle(MCdpy, pm, gc, (x - 1) * CELL_SIZE + 1,
			 (y - 1) * CELL_SIZE + 1,
			 CELL_SIZE - 2, CELL_SIZE - 2);
	}
#elif defined WIN32
        SelectObject(memhdc, bm);
        if (newgrid[x][y] != oldgrid[x][y]) {
	  if (newgrid[x][y]) {
	    SelectObject(memhdc,livebrush);
            SelectObject(memhdc, GetStockObject(NULL_PEN));
	  }
	  else {
	    SelectObject(memhdc, deadbrush);
            SelectObject(memhdc, GetStockObject(NULL_PEN));
	  }
	  Rectangle(memhdc, (x - 1) * CELL_SIZE + 1, (y - 1) * CELL_SIZE + 1,
		    (x -1) * CELL_SIZE + 1 + (CELL_SIZE - 2), 
		    (y - 1) * CELL_SIZE + 1 + (CELL_SIZE - 2));
	}
        SelectObject(memhdc, oldbrush);
	SelectObject(memhdc, oldbitmap);
#elif defined MACOS
        GetGWorld(&oldport, &olddevice);
  		SetGWorld(gw, NULL);
        if (newgrid[x][y] != oldgrid[x][y]) {
	  if (newgrid[x][y])
            RGBForeColor(&livecolor);
          else
            RGBForeColor(&deadcolor);
          SetRect(&rect, (x - 1) * CELL_SIZE + 1, (y - 1) * CELL_SIZE + 1,
                   (x -1) * CELL_SIZE + 1 + (CELL_SIZE - 2), 
		   (y - 1) * CELL_SIZE + 1 + (CELL_SIZE - 2));
          PaintRect(&rect); 
          SetGWorld(oldport, olddevice);
        }
#endif
      }
    }
  }
  ShowImageByNum("", 1, &retvalue);
}

/*
 * Create pixmap and clear it, then set imagePixmapId property.
 * Get GC and colors used for drawing cells.  Put some live cells
 * in at random.
 */
#ifdef __STDC__
void XFlife_init(char *args[], int nargs, char **retstring,
		 Bool *pass, Bool *error)
#else
void XFlife_init(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
  int x, y;
  X_set_idle_handler(life_generation);
  for (x = 1 ; x <= GRID_SIZE ; x++)
    for (y = 1 ; y <= GRID_SIZE ; y++)
      if (rand() > RAND_MAX / 2)
	g1[x][y] = 1;
  *pass = False;
  *error = False;
  *retstring = malloc(16);
#ifdef X11
  {
    XColor c;
    XWindowAttributes xwa;
    int size = GRID_SIZE * CELL_SIZE;
    XGetWindowAttributes(MCdpy, MCwin, &xwa);
    pm = XCreatePixmap(MCdpy, MCwin, size, size, xwa.depth);
    gc = DefaultGCOfScreen(xwa.screen);
    XAllocNamedColor(MCdpy, xwa.colormap, "red", &c, &c);
    livecolor = c.pixel;
    XAllocNamedColor(MCdpy, xwa.colormap, "gray", &c, &c);
    deadcolor = c.pixel;
    XSetForeground(MCdpy, gc, deadcolor);
    XFillRectangle(MCdpy, pm, gc, 0, 0, GRID_SIZE * CELL_SIZE,
		   GRID_SIZE * CELL_SIZE);
    sprintf(*retstring, "%ld", pm);
  }
#elif defined WIN32
  {
    int size = GRID_SIZE * CELL_SIZE;
    HDC winhdc = GetDC(HWND_DESKTOP);
    memhdc = CreateCompatibleDC(winhdc);
    if ((bm = CreateCompatibleBitmap(winhdc, size, size)) == NULL) //compatible to screen
      return;
    ReleaseDC(NULL, winhdc);
    livebrush = CreateSolidBrush(RGB(255, 0, 0));
    deadbrush = CreateSolidBrush(RGB(128, 128, 128)); // Create a gray brush.
   // deadbrush = CreateSolidBrush(RGB(0, 255, 0)); //green
    oldbitmap = SelectObject(memhdc, bm);      //select bitmap into memory DC
    oldbrush = SelectObject(memhdc, deadbrush); 
    if (!Rectangle(memhdc, 0, 0, size, size))
      return;
    SelectObject(memhdc, oldbitmap);
    SelectObject(memhdc, oldbrush);
    sprintf(*retstring, "%ld", bm);
  }
#elif defined MACOS
  {
  
    QDErr err;
    short devdepth;
    GDHandle hgraf;
    PixMapHandle hpixmap;
    SetRect(&rect, 0, 0, GRID_SIZE * CELL_SIZE, GRID_SIZE * CELL_SIZE);
    hgraf = GetMainDevice();  //get main screen graphic device handle
    HLock((Handle)hgraf);
    hpixmap = ((GDPtr)*hgraf)->gdPMap;
    HLock((Handle)hpixmap);
    devdepth = ((PixMapPtr)*hpixmap)->pixelSize;//get main screen device depth
    HUnlock((Handle)hpixmap);
    HUnlock((Handle)hgraf);
    err = NewGWorld(&gw, devdepth, &rect, NULL, NULL, 0);
    if ((gw == NULL) || (err != noErr))
      return;
    GetGWorld(&oldport, &olddevice);
    SetGWorld(gw, NULL);
    deadcolor.red = deadcolor.green = deadcolor.blue = 32768; // = 128 color gray
    livecolor.red = 65535;        // = 256 color red
    livecolor.green = livecolor.blue = 0;
    RGBForeColor(&deadcolor);
    PaintRect(&rect);
    SetGWorld(oldport, olddevice);
    sprintf(*retstring, "%ld", gw); //return the GWorld created to the calling program
  }
#endif
}

/*
 * Set the cell at the x,y coordinate specified to the "live" state.
 */
#ifdef __STDC__
void XClife_create(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XClife_create(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
 int x, y;
  x = atoi(args[0]) / CELL_SIZE + 1;
  y = atoi(args[1]) / CELL_SIZE + 1;
  newgrid[x][y] = 1;

#if defined X11
  XSetForeground(MCdpy, gc, livecolor);
  XFillRectangle(MCdpy, pm, gc, (x - 1) * CELL_SIZE + 1,
		 (y - 1) * CELL_SIZE + 1,
		 CELL_SIZE - 2, CELL_SIZE - 2);
#elif defined WIN32
  SelectObject(memhdc,livebrush);
  SelectObject(memhdc, GetStockObject(NULL_PEN));
  SelectObject (memhdc, bm);
  Rectangle(memhdc, (x - 1) * CELL_SIZE + 1, (y - 1) * CELL_SIZE + 1,
		(x - 1) * CELL_SIZE + 1 + (CELL_SIZE - 2), 
		(y - 1) * CELL_SIZE + 1 + (CELL_SIZE - 2));
  SelectObject(memhdc, oldbrush);
  SelectObject(memhdc, oldbitmap);
#elif defined MACOS
  GetGWorld(&oldport, &olddevice);
  SetGWorld(gw, NULL);
  RGBForeColor(&livecolor);
  SetRect(&rect, (x - 1) * CELL_SIZE + 1, (y - 1) * CELL_SIZE + 1, 
          (x - 1) * CELL_SIZE + 1 + (CELL_SIZE - 2), (y - 1) * CELL_SIZE + 1 + (CELL_SIZE - 2));
  PaintRect(&rect);
  SetGWorld(oldport, olddevice);
#endif
  *pass = False;
  *error = False;
  *retstring = calloc(1, 1);
}

/*
 * Set the delay between generations
 */
#ifdef __STDC__
void XClife_delay(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XClife_delay(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{
  X_set_idle_rate(atoi(args[0]));
  *pass = False;
  *error = False;
  *retstring = calloc(1, 1);
}

/*
 * Stop life
 */
#ifdef __STDC__
void XClife_cease(char *args[], int nargs, char **retstring,
	       Bool *pass, Bool *error)
#else
void XClife_cease(args, nargs, retstring, pass, error)
     char *args[];
     int nargs;
     char **retstring;
     Bool *pass;
     Bool *error;
#endif
{

#ifdef X11
  XWindowAttributes xwa;
  XGetWindowAttributes(MCdpy, MCwin, &xwa);
  XSync(MCdpy, False);                /* make sure all drawing is done */
  XFreeColors(MCdpy, xwa.colormap, &livecolor, 1, 0);
  XFreeColors(MCdpy, xwa.colormap, &deadcolor, 1, 0);
  XFreePixmap(MCdpy, pm);
  pm = 0;
#elif defined WIN32
  DeleteObject(bm);
  bm = NULL;
  DeleteObject(livebrush);
  DeleteObject(deadbrush);
  DeleteDC(memhdc);
#elif defined MACOS
  if (gw != NULL){
    DisposeGWorld(gw);
   	gw = NULL;
   }
#endif
  *pass = False;
  *error = False;
  *retstring = calloc(1, 1);
}

void XCabort()
{
  fprintf(stderr, "external abort");
}

#ifdef X11
#ifdef __STDC__
void main(int argc, char *argv[], char *envp[])
#else
void main(argc, argv, envp)
     int argc;
     char *argv[];
     char *envp[];
#endif
{
  int retval;
  fprintf(stderr, "external startup\n");
  if (!X_init(argc, argv, envp, Xtable, Xname))
    exit(-1);
  X_main_loop();
  retval = X_close();
  fprintf(stderr, "external exit\n");
  exit(retval);
}
#endif
