/* ScianNetObjects.c
 * John R. Murray
 * June 1992
 * Stuff for transmitting and receiving objects over sockets.
 */

#include "Scian.h"
#include "ScianIDs.h"
#include "ScianTypes.h"
#include "ScianNetObjects.h"
#include "ScianSockets.h"
#include "ScianLists.h"
#include "ScianErrors.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianDatasets.h"
#include "ScianNames.h"
/* the following .h files are ONLY included for extern classes */
#include "ScianArrays.h"
#include "ScianTimers.h"
#include "ScianVisObjects.h"
/* this one's here for NewAtomicPalette function definition */
#include "ScianColors.h"

#ifndef DEBUGSOCKETS
#define DEBUGSOCKETS
#endif

/* if the main body of the object is smaller than this, an obj without the
 * extra bytes gets sent */
#define NO_SIZE_THRESHOLD 1024

static ObjPtr allPublished;
#ifdef SAVEDELETED
static ObjPtr allDeletedPublished;
#endif

#ifdef SOCKETS

unsigned int lastNetworkID = 0;

unsigned long GetNetworkID()
{
    return ++lastNetworkID;
}


void AssembleNetObjectVars (vnode, start)
VarsPtr vnode;
char **start;
{
    if (!vnode)
	return;

    if (!ThisWouldBeAReallyBadThing(vnode -> name))
    {

if (!vnode -> value)
{
    fprintf(stderr, "WARNING!! Sending var %ld with value NULLOBJ\n", vnode -> name);
}

#ifdef DEBUGSOCKETS
#ifdef DEBUG
	fprintf(stderr, "var %d\n", vnode -> name);
#endif
	sprintf(*start, "%d ", vnode -> name);
	*start += strlen(*start);
#else
	*((NameTyp *) *start) = HTON_NAMETYP(vnode -> name);
	*start += sizeof(NameTyp);
#endif
    }
    AssembleNetObjectVars (vnode -> left, start);
    AssembleNetObjectVars (vnode -> right, start);
}

char *GetNetTypeStr(obj, connection)
ObjPtr obj, connection;
{
    ObjPtr class;

    class = ClassOf(obj);

    if (class == iconDataset)
	return NET_ICONDATASET;
    else if (class == icon1DVector)
	return NET_ICON1DVECTOR;
    else if (class == icon2DVector)
        return NET_ICON2DVECTOR;
    else if (class == icon3DVector)
        return NET_ICON3DVECTOR;
    else if (class == icon4DVector)
        return NET_ICON4DVECTOR;
    else if (class == icon1DScalar)
        return NET_ICON1DSCALAR;
    else if (class == icon2DScalar)
        return NET_ICON2DSCALAR;
    else if (class == icon3DScalar)
        return NET_ICON3DSCALAR;
    else if (class == icon4DScalar)
        return NET_ICON4DSCALAR;
    else if (class == data3DScalar)
        return NET_DATA3DSCALAR;
    else if (class == data2DScalar)
        return NET_DATA2DSCALAR;
    else if (class == data1DVector)
        return NET_DATA1DVECTOR;
    else if (class == data3DUnstructSurface)
        return NET_DATA3DUNSTRUCT;
    else if (class == datasetClass)
	return NET_DATASET;
    else if (class == dataFormClass)
	return NET_DATAFORM;
    else if (class == stringClass)
	return NET_STRING;
    else if (class == objClass && IsInt(obj))
	return NET_INTEGER;
    else if (class == objClass && IsReal(obj))
	return NET_REAL;
    else if (class == objClass)
	return NET_PLAINOBJECT;
    else if (class == arrayClass)
#if MACHINE == IRIS4D
#ifdef FASTSOCKETS
	return NET_REALARRAYRAW;
#else
	if (GetPredicate(connection, FASTSOCKET))
	{
	    return NET_REALARRAYRAW;
	}
	else
	{
	    return NET_REALARRAYASCII;
	}
#endif
#else
	return NET_REALARRAYASCII;
#endif
    else if (class == objectArrayClass)
	return NET_OBJECTARRAY;
    else if (class == timedObjClass)
	return NET_TIMEDOBJECT;
    else if (class == paletteClass)
	return NET_PALETTE;
    else if (class == visIcon)
{
fprintf(stderr, "sending visIcon\n");
	return NET_VISICON;
}
    else
    {
	fprintf(stderr, "trying to transmit obj of unrecognized class\n");
	fprintf(stderr, "class = 0x%x\n", class);
	return NET_UNIMPLEMENTED;
    }
}

void TransmitObject(obj, connection, flag)
ObjPtr obj, connection;
Bool flag;
/* transmit obj across connection. if flag, send OBJ! message header, or not */
{
    char buf[1024];
    char *bufp;
    int sock;
    int networkID;
    ObjPtr networkIDPtr;
    NetFlagsTyp netFlags;
    FuncTyp method;
    FlagsTyp flagstmp;

    networkID = PublishObject(obj);

    sock = ((ConnectionPtr) connection) -> sock;

    if (sock < 0)
	return;

    bufp = buf;

    if (flag)
    {
	sprintf(bufp, "%s ", NO_MESG_OBJECT);
	bufp += strlen(bufp);
    }

    /* sending network type identifier */
    sprintf(bufp, "%s ", GetNetTypeStr(obj, connection));
    bufp += strlen(bufp);

    /* sending network ID number */
#ifdef DEBUG
fprintf(stderr, "xmitting netid %ld\n", networkID);
#endif
    sprintf(bufp, "%ld ", networkID);
    bufp += strlen(bufp);

    /* send object flags */
    flagstmp = obj -> flags;
    flagstmp = flagstmp & ~ISPUBLISHED;
    if (GetVar(obj, ADVERTISED))
    {
	flagstmp = flagstmp | ISREMOTEADVERTISED;
    }
    sprintf(bufp, "%ld ", flagstmp);
    bufp += strlen(bufp);

    /* send network flags */
    netFlags = ( !obj -> class ? NO_NULL_CLASS : 0 )
	       | ( !obj -> vars ? NO_NULL_VARS : 0 )
	       | ( !obj -> methods ? NO_NULL_METHODS : 0 )
	       | ( !obj -> depends ? NO_NULL_DEPENDS : 0 );

    sprintf(bufp, "%ld ", netFlags);
    bufp += strlen(bufp);

    /* send variables */
    AssembleNetObjectVars(obj -> vars, &bufp);
    sprintf(bufp, "%d ", NO_LAST_VAR);
    bufp += strlen(bufp);

#ifdef DEBUG
    fprintf(stderr, "%s", buf);
#endif

    if (bufp - buf > 1024)
    {
	fprintf(stderr, "TransmitObject: overran buffer!\n");
    }

    writen(sock, buf, bufp - buf);

    method = GetMethod(obj, TRANSMITEXTRA);
    if (method)
    {
/*BIG HAK!*/
#if MACHINE == RS6000
#else
	if (GetPredicate(connection, FASTSOCKET)
		&& (method == TransmitExtraStuffRealArrayAscii))
	{
#ifdef DEBUG
	    fprintf(stderr, "fast\n");
#endif
	    TransmitExtraStuffRealArrayRaw(obj, connection);
	}
	else
#endif
	{
	    (* method) (obj, connection);
	}
    }

    /* send line terminator */
    buf[0] = '\n';
    buf[1] = '\0';
#ifdef DEBUG
    fprintf(stderr, "%s", buf);
#endif

    writen(sock, buf, strlen(buf));

}

#define ROS_BEGIN		0	/* startup state */
#define ROS_READ_NET_TYPE	1	/* reading the type of this object */
#define ROS_READ_FLAGS		2	/* reading the object flags */
#define ROS_READ_NETFLAGS	3	/* reading the net flags */
#define ROS_READ_VARS		4	/* reading the vars */
#define ROS_ERROR		5	/* blammo */
#define ROS_DONE		6	/* I'm done */
#define ROS_READ_NETWORKID	7	/* reading the network identifier */

ObjPtr ReceiveObject(connection, remoteChCount)
ObjPtr connection;
unsigned long remoteChCount;
{
    int state;
    ObjPtr retVal;
    char objType, inChar;
    int someDigits;
    unsigned long ltmp;
    int networkID;
    int sock;
    char s[100];
    int netID = 0;
    int varNum = 0;
    int debug = 0;	/* setting this to 1 will fprintf the internal states */
    ObjPtr process;

    ltmp = 0; /* we'll have to do something else with ltmp when we make this reentrant */
    state = ROS_BEGIN;
    sock = ((ConnectionPtr) connection) -> sock;

    if (sock < 0)
    {
	ReportError("ReceiveObject", "Bad socket");
	return ObjFalse;
    }

    process = GetObjectVar("ReceiveObject", connection, OWNERPROCESS);
    if (!process)
    {
	ReportError("ReceiveObject", "connection has no OWNERPROCESS");
	return ObjFalse;
    }

    while (state != ROS_DONE)
    {
	switch(state)
	{
	    case ROS_BEGIN:
if (debug) fprintf(stderr, "state = BEGIN\n");
		state = ROS_READ_NET_TYPE;
		s[0] = '\0';
		break;
	    case ROS_READ_NET_TYPE:	/* initial entry assumes s == "" */
if (debug) fprintf(stderr, "state = READ_NET_TYPE\n");
		if (1 == read(sock, &inChar, 1))
		{
#if 0
		    if (inChar == '\n' || inChar == '\0')
		    {
			((ConnectionPtr) connection) -> gotCmdEnd = true;
			ltmp = 0;
			state = ROS_ERROR;
		    }
		    else
#endif
		    if (!isspace(inChar))
		    {
			if (strlen(s) <= 4)
			{
			    strncat(s, &inChar, 1);	/* append one char */
			}
			else
			{
			    state = ROS_ERROR; /* net type too long */
			}
			/* skip initial blanks */
		    }
		    else if (s[0] == '\0')	/* no characters yet */
		    {
			/* skip initial blanks */
		    }
		    else if (strlen(s) != 4)
		    {
			state = ROS_ERROR;
		    }
		    else	/* it's a space, and we've got 4 characters */
		    {
			long cmdNum;

			cmdNum = CMD(s);
			if (cmdNum == CMD(NET_PLAINOBJECT))
			{
			    retVal = NewObject(0, 0);
			}
			else if (cmdNum == CMD(NET_INTEGER))
			{
			    retVal = NewInt(0);
			}
			else if (cmdNum == CMD(NET_REAL))
			{
			    retVal = NewReal(0);
			}
			else if (cmdNum == CMD(NET_STRING))
			{
			    retVal = NewString("");
			}
#if MACHINE == IRIS4D
			else if (cmdNum == CMD(NET_REALARRAYRAW))
			{
			    retVal = NewRealArray(1, 1L);
			    SetMethod(retVal, TRANSMITEXTRA, TransmitExtraStuffRealArrayRaw);
			    SetMethod(retVal, RECEIVEEXTRA, ReceiveExtraStuffRealArrayRaw);
			}
#endif
			else if (cmdNum == CMD(NET_REALARRAYASCII))
			{
			    retVal = NewRealArray(1, 1L);
			}
			else if (cmdNum == CMD(NET_OBJECTARRAY))
			{
			    long dummy;
			    dummy = 1L;
			    retVal = NewArray(AT_OBJECT, 1, &dummy);
			}
			else if (cmdNum == CMD(NET_ICONDATASET))
			{
			    retVal = NewObject(iconDataset, 0);
			}
			else if (cmdNum == CMD(NET_ICON1DVECTOR))
			{
			    retVal = NewObject(icon1DVector, 0);
			}
			else if (cmdNum == CMD(NET_ICON2DVECTOR))
			{
			    retVal = NewObject(icon2DVector, 0);
			}
			else if (cmdNum == CMD(NET_ICON3DVECTOR))
			{
			    retVal = NewObject(icon3DVector, 0);
			}
			else if (cmdNum == CMD(NET_ICON4DVECTOR))
			{
			    retVal = NewObject(icon4DVector, 0);
			}
			else if (cmdNum == CMD(NET_ICON1DSCALAR))
			{
			    retVal = NewObject(icon1DScalar, 0);
			}
			else if (cmdNum == CMD(NET_ICON2DSCALAR))
			{
			    retVal = NewObject(icon2DScalar, 0);
			}
			else if (cmdNum == CMD(NET_ICON3DSCALAR))
			{
			    retVal = NewObject(icon3DScalar, 0);
			}
			else if (cmdNum == CMD(NET_ICON4DSCALAR))
			{
			    retVal = NewObject(icon4DScalar, 0);
			}
			else if (cmdNum == CMD(NET_DATA1DVECTOR))
			{
			    retVal = NewObject(data1DVector, 0);
			}
			else if (cmdNum == CMD(NET_DATA2DSCALAR))
			{
			    retVal = NewObject(data2DScalar, 0);
			}
			else if (cmdNum == CMD(NET_DATA3DSCALAR))
			{
			    retVal = NewObject(data3DScalar, 0);
			}
			else if (cmdNum == CMD(NET_DATA3DUNSTRUCT))
			{
			    retVal = NewObject(data3DUnstructSurface, 0);
			}
			else if (cmdNum == CMD(NET_DATASET))
			{
			    retVal = NewObject(datasetClass, 0);
			}
			else if (cmdNum == CMD(NET_DATAFORM))
			{
			    retVal = NewObject(dataFormClass, 0);
			}
			else if (cmdNum == CMD(NET_TIMEDOBJECT))
			{
			    retVal = NewObject(timedObjClass, 0);
			}
			else if (cmdNum == CMD(NET_PALETTE))
			{
			    retVal = NewAtomicPalette();
			}
			else if (cmdNum == CMD(NET_VISICON))
			{
			    retVal = NewObject(visIcon, 0);
			}
			else if (cmdNum == CMD(NET_UNIMPLEMENTED))
			{
			    fprintf(stderr, "remote process transmitted unimplemented net type\ntreating it as plain object\n");
			    retVal = NewObject(0, 0);
			}
			else
			{
			    fprintf(stderr, "unrecognized net type %4s\n", s);
			    return ObjFalse;
			}
			ltmp = 0;
			someDigits = false;
			state = ROS_READ_NETWORKID;
		    }
		}
		break;
	    case ROS_READ_NETWORKID:	/* initial entry assumes ltmp == 0 && someDigits == false */
if (debug) fprintf(stderr, "state = READ_NETWORKID\n");
#ifdef DEBUGSOCKETS
		if (1 == read(sock, &inChar, 1))
		{
		    if (isspace(inChar) && (inChar != '\n') && !someDigits)
		    {
			/* skipping blanks */
		    }
		    else if (inChar >= '0' && inChar <= '9')
		    {
			someDigits = true;
			ltmp *= 10;
			ltmp += inChar - '0';
		    }
		    else if (inChar == '\0' || inChar == '\n')
		    {
			((ConnectionPtr) connection) -> gotCmdEnd = true;
			state = ROS_ERROR;
		    }
		    else if (!someDigits)
		    {
			state = ROS_ERROR;
		    }
		    else
		    {
if (debug) fprintf(stderr, "setting NETWORKID to %d\n", ltmp);
			networkID = ltmp;
			someDigits = false;
			ltmp = 0;
			state = ROS_READ_FLAGS;
		    }
		}
#else
		Yabbadabba!
#endif
		break;
	    case ROS_READ_FLAGS:	/* 1st entry assumes ltmp == 0  & someDigits == false */
if (debug) fprintf(stderr, "state = READ_FLAGS\n");
#ifdef DEBUGSOCKETS
		if (1 == read(sock, &inChar, 1))
		{
		    if (isspace(inChar) && (inChar != '\n') && !someDigits)
		    {
			/* skipping blanks */
		    }
		    else if (inChar >= '0' && inChar <= '9')
		    {
			someDigits = true;
			ltmp *= 10;
			ltmp += inChar - '0';
		    }
		    else if (inChar == '\0' || inChar == '\n')
		    {
			((ConnectionPtr) connection) -> gotCmdEnd = true;
			state = ROS_ERROR;
		    }
		    else
		    {
			retVal -> flags = ltmp;
			ltmp = 0;
			someDigits = false;
			state = ROS_READ_NETFLAGS;
		    }
		}
#else
		Yabbadabba!
#endif
		break;
	    case ROS_READ_NETFLAGS:	/* initial entry assumes ltmp == 0 && someDigits == false */
if (debug) fprintf(stderr, "state = READ_NETFLAGS\n");
#ifdef DEBUGSOCKETS
		if (1 == read(sock, &inChar,1))
		{
		    if (isspace(inChar) && (inChar != '\n') && !someDigits)
		    {
			/* skipping blanks */
		    }
		    else if (inChar >= '0' && inChar <= '9')
		    {
			someDigits = true;
			ltmp *= 10;
			ltmp += inChar - '0';
		    }
		    else if (inChar == '\n' || inChar == '\0')
		    {
			((ConnectionPtr) connection) -> gotCmdEnd = true;
			state = ROS_ERROR;
		    }
		    else
		    {
			/* done reading netflags */
if (debug) fprintf(stderr, "object's netflags == %lx\n", ltmp);
			if (ltmp & NO_NULL_CLASS)
			{
			    /* ignored for now */
			}

			if (ltmp & NO_NULL_VARS)
			{
			    /* ignored for now */
			}

			if (ltmp & NO_NULL_METHODS)
			{
			    /* ignored */
			}

			if (ltmp & NO_NULL_DEPENDS)
			{
			    /* ignored */
			}

			ltmp = 0;
			someDigits = false;
			state = ROS_READ_VARS;
		    }
		}
#else
		Yabbadabba!
#endif
		break;
	    case ROS_READ_VARS:	/* initial entry assumes ltmp == 0 and someDigits == false */
/* if (debug) fprintf(stderr, "state = READ_VARS\n"); */
if (debug) fprintf(stderr, "state = READ_VARS... ");
		if (1 == read(sock, &inChar, 1))
		{
if (debug) fprintf(stderr, "char '%c'\n", inChar);
		    if (isspace(inChar) && (inChar != '\n') && !someDigits)
		    {
			/* skipping blanks */
		    }
		    else if (inChar >= '0' && inChar <= '9')
		    {
			someDigits = true;
			ltmp *= 10;
			ltmp += inChar - '0';
		    }
		    else if (inChar == '\n' || inChar == '\0')
		    {
			((ConnectionPtr) connection) -> gotCmdEnd = true;
			state = ROS_ERROR;
		    }
		    else if (ltmp != NO_LAST_VAR)
		    {
			VarsPtr vPtr;
			SetVar (retVal, ltmp, NULLOBJ);
			vPtr = GetVarNode(retVal, ltmp);
			vPtr -> process = process;
			vPtr -> remoteNetID = NETSTUBFLAG;
			vPtr -> remoteChCount = anotherDamnReferenceCount;
			ltmp = 0;
			someDigits = false;
		    }
		    else
		    {
			/* that may have been the end var marker */
			if (someDigits)
			{
			    if (SanityCheckObject(retVal))
			    {
				FuncTyp method;

				method = GetMethod(retVal, RECEIVEEXTRA);
				if (method)
				{
				    (* method) (retVal, connection);
				}
				state = ROS_DONE;
			    }
			    else
			    {
				ReportError("ReceiveObject", "An incoming network object failed Sanity Check");
				state = ROS_ERROR;
			    }
			}
			else
			{
			    state = ROS_ERROR;
			}
		    }
		}
		break;
	    case ROS_ERROR:	/* no assumptions on inital entry */
if (debug) fprintf(stderr, "state = ERROR\n");
		ReportError("ReceiveObject", "Error in receiving network object");
		return ObjFalse;
		break;
	    default:
		fprintf(stderr, "funny state in ReceiveObject!\n");
		break;
	}
    }

    SetVar(retVal, NETWORKID, NewInt(networkID));
    if (remoteChCount) SetVar(retVal, REMOTECHCOUNT, NewInt(remoteChCount));
    SetVar(retVal, OWNERCONNECTION, connection);
    retVal -> flags = retVal -> flags | ISREMOTE;
if (debug) PrintObject(retVal);
if (debug) PrintVarTree(retVal);
    NetworkIDIsWaiting(process, networkID, false);
    return retVal;
}

real ReceiveRealAscii(connection)
ObjPtr connection;
{
    real retVal;
    int sock;
    char inChar, s[100];
    int si;
    double time;
    Bool someDigits;

    si = 0;
    s[si] = '\0';
    someDigits = false;
    time = Clock();

    sock = ((ConnectionPtr) connection) -> sock;

    while (Clock() < time + JOHNSTIMEOUT && !((ConnectionPtr) connection) -> gotCmdEnd)
    {
	if (1 == read(sock, &inChar, 1))
	{
	    if (isspace(inChar) && (inChar != '\n') && !someDigits)
	    {
		/* skipping blanks */
	    }
	    else if (si >= 100 || (inChar >= '0' && inChar <= '9')
			|| inChar == '-' || inChar == 'e' || inChar == 'E'
			|| inChar == '+' || inChar == '-' || inChar == '.')
	    {
		someDigits = true;
		s[si++] = inChar;
		s[si] = '\0';
	    }
	    else
	    {
		int len;

		if (inChar == '\0' || inChar == '\n')
		{
		    ((ConnectionPtr) connection) -> gotCmdEnd = true;
		}

		/* make sure we read etwas that takes up the whole string */
		if (someDigits && (1 != sscanf(s, "%100g%n", &retVal, &len)
				   || len < strlen(s)))
		{
fprintf(stderr, "bad sscanf return. s = %s, len = %d, strlen(s) = %d.\n", s, len, strlen(s));
		    return -1;
		}
		return retVal;
	    }
	}
    }
    /* only got here if we timed out */
fprintf(stderr, "hack timeout in ReceiveRealAscii\n");
    return -1;
}

ObjPtr TransmitExtraStuffRealArrayRaw(obj, connection)
ObjPtr obj, connection;
{
    int sock;
    int rank;
    long *dimp;
    int i;
    long nels;
    real *elsPtr;

    sock = ((ConnectionPtr) connection) -> sock;

    rank = RANK(obj);
#ifdef DEBUG
    fprintf(stderr, "sending rank = %d.. ", rank);
#endif
    writen(sock, (char *) &rank, sizeof(rank));

#ifdef DEBUG
    fprintf(stderr, "sending dims");
#endif
    dimp = DIMS(obj);
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, *dimp);
#endif
	writen(sock, (char *) dimp, sizeof(*dimp));
	nels *= *dimp;
	++dimp;
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
    fprintf(stderr, "trying to write %ld * 4 bytes of data\n", nels);
#endif
    elsPtr = ELEMENTS(obj);
    writen(sock, (char *) elsPtr, sizeof(*elsPtr) * nels);
}

ObjPtr ReceiveExtraStuffRealArrayRaw(obj, connection)
ObjPtr obj, connection;
{
    int sock, rank, i;
    long *dimp, nels, oneDim;
    real *elsPtr;

    sock = ((ConnectionPtr) connection) -> sock;

    readn(sock, (char *) &rank, sizeof(rank));
/* fprintf(stderr, "rank = %ld\n",  */
#ifdef DEBUG
    fprintf(stderr, "got rank = %d\n", rank);
#endif
    RANK(obj) = rank;

#ifdef DEBUG
    fprintf(stderr, "getting dims -");
#endif
    dimp = malloc(rank * sizeof(long));
    DIMS(obj) = dimp;
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
	readn(sock, (char *) &oneDim, sizeof(long));
	nels *= oneDim;
	*dimp = oneDim;
	++dimp;
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, oneDim);
#endif
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
#endif

    elsPtr = (void *) malloc(nels * sizeof(real));
    if (!elsPtr)
    {
	OMErr();
	return ObjFalse;
    }
    ELEMENTS(obj) = elsPtr;

#ifdef DEBUG
    fprintf(stderr, "trying to read 4 * %d bytes\n", nels);
#endif
    readn(sock, (char *) elsPtr, sizeof(real) * nels);
#ifdef DEBUG
    fprintf(stderr, "...wow! did it work??\n");
#endif
}

ObjPtr TransmitExtraStuffRealArrayAscii(obj, connection)
ObjPtr obj, connection;
{
    int sock;
    int rank;
    long *dimp;
    long i;
    long nels;
    real *elsPtr;

    sock = ((ConnectionPtr) connection) -> sock;

    rank = RANK(obj);
#ifdef DEBUG
    fprintf(stderr, "sending rank = %d.. ", rank);
#endif
    sprintf(tempStr, "%d ", rank);
    writen(sock, tempStr, strlen(tempStr));

#ifdef DEBUG
    fprintf(stderr, "sending dims");
#endif
    dimp = DIMS(obj);
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, *dimp);
#endif
	sprintf(tempStr, "%ld ", *dimp);
	writen(sock, tempStr, strlen(tempStr));
	nels *= *dimp;
	++dimp;
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
    fprintf(stderr, "trying to write %ld ascii numbers\n", nels);
#endif
    elsPtr = ELEMENTS(obj);
    for (i = 0; i < nels; ++i)
    {
	sprintf(tempStr, "%g ", elsPtr[i]);
	writen(sock, tempStr, strlen(tempStr));
    }
}

ObjPtr ReceiveExtraStuffRealArrayAscii(obj, connection)
ObjPtr obj, connection;
{
    int sock, rank, i;
    long *dimp, nels, oneDim;
    real *elsPtr;

    sock = ((ConnectionPtr) connection) -> sock;

    rank = ReceiveRealAscii(connection);
#ifdef DEBUG
    fprintf(stderr, "got rank = %d\n", rank);
#endif
    RANK(obj) = rank;

#ifdef DEBUG
    fprintf(stderr, "getting dims -");
#endif
    dimp = malloc(rank * sizeof(long));
    DIMS(obj) = dimp;
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
	oneDim = ReceiveRealAscii(connection);
	nels *= oneDim;
	*dimp = oneDim;
	++dimp;
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, oneDim);
#endif
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
#endif

    elsPtr = (void *) malloc(nels * sizeof(real));
    if (!elsPtr)
    {
	OMErr();
	return ObjFalse;
    }

    ELEMENTS(obj) = elsPtr;
#ifdef DEBUG
    fprintf(stderr, "trying to read %ld elements..", nels);
#endif
    for (i = 0; i < nels; ++i)
    {
	elsPtr[i] = ReceiveRealAscii(connection);
    }
#ifdef DEBUG
    fprintf(stderr, "wow! did it work??\n");
#endif
    return ObjTrue;
}

ObjPtr TransmitExtraStuffObjectArraySpecial(obj, connection)
ObjPtr obj, connection;
{
    int sock;
    int rank;
    long *dimp;
    long i;
    long nels;
    ObjPtr *elsPtr;
    double startTime;

    startTime = Clock();

    sock = ((ConnectionPtr) connection) -> sock;

    rank = RANK(obj);
#ifdef DEBUG
    fprintf(stderr, "sending rank = %d.. ", rank);
#endif
    sprintf(tempStr, "%d ", rank);
    writen(sock, tempStr, strlen(tempStr));

#ifdef DEBUG
    fprintf(stderr, "sending dims");
#endif
    dimp = DIMS(obj);
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, *dimp);
#endif
	sprintf(tempStr, "%ld ", *dimp);
	writen(sock, tempStr, strlen(tempStr));
	nels *= *dimp;
	++dimp;
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
#endif

    elsPtr = ELEMENTS(obj);
    for (i = 0; i < nels; ++i)
    {
	TransmitObject(elsPtr[i], connection, false);
    }
    fprintf(stderr, "object array transmit time %10.4lf\n", Clock() - startTime);
}

ObjPtr ReceiveExtraStuffObjectArraySpecial(obj, connection)
ObjPtr obj, connection;
{
    int sock, rank, i;
    long *dimp, nels, oneDim;
    ObjPtr *elsPtr;
    double startTime;

    startTime = Clock();

    sock = ((ConnectionPtr) connection) -> sock;

    rank = ReceiveRealAscii(connection);
#ifdef DEBUG
    fprintf(stderr, "got rank = %d\n", rank);
#endif
    RANK(obj) = rank;

#ifdef DEBUG
    fprintf(stderr, "getting dims -");
#endif
    dimp = malloc(rank * sizeof(long));
    DIMS(obj) = dimp;
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
	oneDim = ReceiveRealAscii(connection);
	nels *= oneDim;
	*dimp = oneDim;
	++dimp;
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, oneDim);
#endif
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
#endif

    elsPtr = (void *) malloc(nels * sizeof(ObjPtr));
    if (!elsPtr)
    {
	OMErr();
	return ObjFalse;
    }

    ELEMENTS(obj) = elsPtr;
    for (i = 0; i < nels; ++i)
    {
	((ConnectionPtr) connection) -> gotCmdEnd = false;
	elsPtr[i] = ReceiveObject(connection, 0);
    }
fprintf(stderr, "object array receive time %10.4lf\n", Clock() - startTime);
    return ObjTrue;
}

#if 1
ObjPtr TransmitExtraStuffObjectArray(obj, connection)
ObjPtr obj, connection;
{
    TransmitExtraStuffObjectArraySpecial(obj, connection);
}
#else
ObjPtr TransmitExtraStuffObjectArray(obj, connection)
ObjPtr obj, connection;
{
    int sock;
    int rank;
    long *dimp;
    long i;
    long nels;
    real *elsPtr;

    sock = ((ConnectionPtr) connection) -> sock;

    rank = RANK(obj);
#ifdef DEBUG
    fprintf(stderr, "sending rank = %d.. ", rank);
#endif
    sprintf(tempStr, "%d ", rank);
    writen(sock, tempStr, strlen(tempStr));

#ifdef DEBUG
    fprintf(stderr, "sending dims");
#endif
    dimp = DIMS(obj);
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, *dimp);
#endif
	sprintf(tempStr, "%ld ", *dimp);
	writen(sock, tempStr, strlen(tempStr));
	nels *= *dimp;
	++dimp;
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
#endif
}
#endif

#if 1
ObjPtr ReceiveExtraStuffObjectArray(obj, connection)
ObjPtr obj, connection;
{
    ReceiveExtraStuffObjectArraySpecial(obj, connection);
}
#else
ObjPtr ReceiveExtraStuffObjectArray(obj, connection)
ObjPtr obj, connection;
{
    int sock, rank, i;
    long *dimp, nels, oneDim;
    ObjPtr *elsPtr;

    sock = ((ConnectionPtr) connection) -> sock;

    rank = ReceiveRealAscii(connection);
#ifdef DEBUG
    fprintf(stderr, "got rank = %d\n", rank);
#endif
    RANK(obj) = rank;

#ifdef DEBUG
    fprintf(stderr, "getting dims -");
#endif
    dimp = malloc(rank * sizeof(long));
    DIMS(obj) = dimp;
    nels = 1;
    for (i = 0; i < rank; ++i)
    {
	oneDim = ReceiveRealAscii(connection);
	nels *= oneDim;
	*dimp = oneDim;
	++dimp;
#ifdef DEBUG
	fprintf(stderr, " d%d=%ld", i, oneDim);
#endif
    }
#ifdef DEBUG
    fprintf(stderr, ". done with dims\n");
#endif

    elsPtr = (void *) malloc(nels * sizeof(ObjPtr));
    if (!elsPtr)
    {
	OMErr();
	return ObjFalse;
    }

    ELEMENTS(obj) = elsPtr;
    for (i = 0; i < nels; ++i)
    {
	elsPtr[i] = (ObjPtr) NETSTUBFLAG;
    }
    return ObjTrue;
}
#endif

ObjPtr TransmitExtraStuffString(obj, connection)
ObjPtr obj, connection;
{
    int sock;

    if (!obj)
	return ObjFalse;

    sock = ((ConnectionPtr) connection) -> sock;

    if (sock < 0)
	return ObjFalse;

    /* HAK! should figure out a way to avoid relying on the \0 */
    /* transmit everything, even the \0 on the end */
#ifdef DEBUG
    fprintf(stderr, "%s", ((SPtr) obj) -> stringPtr);
#endif
    writen(sock, ((SPtr) obj) -> stringPtr, 1 + strlen(((SPtr) obj) -> stringPtr));
    return ObjTrue;
}

ObjPtr ReceiveExtraStuffString(obj, connection)
ObjPtr obj, connection;
{
    double time;
    int sock;
    char inChar;
    int someDigits;

    /* HAK! figure out how to do this reentrantly */
    time = Clock();
    sock = ((ConnectionPtr) connection) -> sock;
    someDigits = false;

    if (obj)
    {
	char s[1000];
	int si;

	si = 0;
	s[si] = '\0';

	free(((SPtr) obj) -> stringPtr);	/* really! */
	((SPtr) obj) -> stringPtr = (char *) 0;

	while(Clock() < time + JOHNSTIMEOUT)
	{
	    if (1 == read(sock, &inChar, 1))
	    {
		if (inChar)
		{
		    someDigits = true;
		    s[si++] = inChar;
		    s[si] = '\0';
		}

		if (!inChar || si >= 999)
		{
		    if ( ! ((SPtr) obj) -> stringPtr)
		    {
			((SPtr) obj) -> stringPtr = malloc(1 + strlen(s));
			if (! ((SPtr) obj) -> stringPtr)
			{
			    OMErr();
			    return ObjFalse;
			}
			strcpy(((SPtr) obj) -> stringPtr, s);
		    }
		    else
		    {
			((SPtr) obj) -> stringPtr = realloc(((SPtr) obj) -> stringPtr, strlen(s));
			if (! ((SPtr) obj) -> stringPtr)
			{
			    OMErr();
			    return ObjFalse;
			}
			strcat(((SPtr) obj) -> stringPtr, s);
		    }
		    si = 0;
		    s[si] = '\0';
		}

		if (!inChar)
		{
		    return ObjTrue;
		}
	    }
	}
	/* only got here if we timed out */
fprintf(stderr, "hack timeout in ReceiveExtraStuffString");
	return ObjFalse;
    }
    return ObjTrue;
}

ObjPtr TransmitExtraStuffObject(obj, connection)
ObjPtr obj, connection;
{
    int sock;

    if (!obj)
	return ObjFalse;

    sock = ((ConnectionPtr) connection) -> sock;
    if (sock < 0)
	return ObjFalse;

    if (IsInt(obj))
    {
	char buf[50];

	sprintf(buf, "%d ", ((IPtr) obj) -> intpart);
#ifdef DEBUG
	fprintf(stderr, "%s", buf);
#endif
	writen(sock, buf, strlen(buf));
    }
    else if (IsReal(obj))
    {
	char buf[100];

	sprintf(buf, "%g ", ((RPtr) obj) -> realpart);
#ifdef DEBUG
	fprintf(stderr, "%s", buf);
#endif
	writen(sock, buf, strlen(buf));
    }
    return ObjTrue;
}

ObjPtr ReceiveExtraStuffObject(obj, connection)
ObjPtr obj, connection;
{
    int sock;
    char inChar;
    int someDigits;
    long int ltmp;
    double time;

    if (!obj)
	return ObjFalse;

    /* HAK! figure out how to do this reentrantly */
    time = Clock();

    ltmp = 0;
    someDigits = false;
    inChar = 'x';

    sock = ((ConnectionPtr) connection) -> sock;

    if (IsInt(obj))
    {
	while (Clock() < time + JOHNSTIMEOUT)
	{
	if (1 == read(sock, &inChar, 1))
	{
	    if (isspace(inChar) && (inChar != '\n') && !someDigits)
	    {
		/* skipping blanks */
	    }
	    else if (inChar >= '0' && inChar <= '9')
	    {
		someDigits = true;
		ltmp *= 10;
		ltmp += inChar - '0';
	    }
	    else if (inChar == '\0' || inChar == '\n')
	    {
		((ConnectionPtr) connection) -> gotCmdEnd = true;
		return ObjFalse;
	    }
	    else
	    {
		((IPtr) obj) -> intpart = ltmp;
		return ObjTrue;
	    }
	}
	}	
	/* only got here if we timed out */
fprintf(stderr, "hack timeout in ReceiveExtraStuffObject");
	return ObjFalse;
    }
    else if (IsReal(obj))
    {
	((RPtr) obj) -> realpart = ReceiveRealAscii(connection);
    }

    return ObjTrue;
}

#ifdef PROTO
ObjPtr TransmitExtraStuffPalette(ObjPtr obj, ObjPtr connection)
#else
ObjPtr TransmitExtraStuffPalette(obj, connection)
ObjPtr obj, connection;
#endif
{
    return ObjTrue;
}

#ifdef PROTO
ObjPtr ReceiveExtraStuffPalette(ObjPtr obj, ObjPtr connection)
#else
ObjPtr ReceiveExtraStuffPalette(obj, connection)
ObjPtr obj, connection;
#endif
{
    ObjPtr atomPal;

#if 0
    atomPal = NewAtomicPalette(); /* HAK */
    CopyPalette(obj, atomPal);
#endif
    return ObjTrue;
}

#endif /* ifdef SOCKETS */

int PublishObject(obj)
ObjPtr obj;
/* Marks an object as published. Returns the (new?) NetworkID of the object,
 * or 0 if it failed for some reason.
 */
{
    int netid;

    netid = 0;
#ifdef SOCKETS
    if (!GetVar(obj, NETWORKID))
    {
	SetVar(obj, NETWORKID, NewInt(GetNetworkID()));
    }
    netid = GetInt(GetIntVar("PublishObject", obj, NETWORKID));

    if (!IsPublished(obj))
    {
	obj -> flags = obj -> flags | ISPUBLISHED;
	if (!allPublished)
	{
fprintf(stderr, "Whoops! recreating allPublished!\n");
	    allPublished = NewList();
	    AddToReferenceList(allPublished);
	}

	PrefixList(allPublished, obj);
    }
#endif
    return netid;
}

#if 0
void UnPublishObject(obj)
ObjPtr obj;
/* Unmarks the published flag, removes object from published list */
{
#ifdef SOCKETS
    if (IsPublished(obj))
    {
#if 0
	obj -> flags = obj -> flags & ~ISPUBLISHED;
#endif
	DeleteFromList(allPublished, obj);
	DeleteFromList(allAdvertised, obj);
#ifdef SAVEDELETED
	if (!allDeletedPublished);
	{
fprintf(stderr, "Whoops! recreating allDeletedPublished!\n");
	    allDeletedPublished = NewList();
	    AddToReferenceList(allDeletedPublished);
	}
	PrefixList(allDeletedPublished, obj);
#endif
    }
#endif
}
#endif

ObjPtr FindPublishedObject(num)
unsigned long num;
{
#ifdef SOCKETS
    ObjPtr retVal;
    ThingListPtr runner;

    retVal = ObjFalse;

    if (allPublished)
    {

	runner = LISTOF(allPublished);
	while (runner && (num != GetInt(GetIntVar("FindPublishedObject", runner -> thing, NETWORKID))))
	{
	    runner = runner -> next;
	}
	if (runner)
	{
	    retVal = runner -> thing;
	}
    }

    /* if we didn't find it, try the list of Advertised objects */
    if (!retVal && allAdvertised)
    {
	runner = LISTOF(allAdvertised);
	while (runner && (num != GetInt(GetIntVar("FindPublishedObject", runner -> thing, NETWORKID))))
	{
	    runner = runner -> next;
	}
	if (runner)
	{
	    retVal = runner -> thing;
	}
    }

#ifdef SAVEDELETED
    /* still not found, try the deleted list */
    if (retVal && allDeletedPublished)
    {
	runner = LISTOF(allDeletedPublished);
	while(runner && (num != GetInt(GetIntVar("FindPublishedObject", runner -> thing, NETWORKID))))
	{
	    runner = runner -> next;
	}
	if (runner)
	{
	    retVal = runner -> thing;
	}
    }
#endif

    return retVal;
#else
    return ObjFalse;
#endif
}

void InitNetObjects()
{
#ifdef SOCKETS
    SetMethod(objClass, TRANSMITEXTRA, TransmitExtraStuffObject);
    SetMethod(objClass, RECEIVEEXTRA, ReceiveExtraStuffObject);

    SetMethod(stringClass, TRANSMITEXTRA, TransmitExtraStuffString);
    SetMethod(stringClass, RECEIVEEXTRA, ReceiveExtraStuffString);

    SetMethod(paletteClass, TRANSMITEXTRA, TransmitExtraStuffPalette);
    SetMethod(paletteClass, RECEIVEEXTRA, ReceiveExtraStuffPalette);

    SetMethod(objectArrayClass, TRANSMITEXTRA, TransmitExtraStuffObjectArraySpecial);
    SetMethod(objectArrayClass, RECEIVEEXTRA, ReceiveExtraStuffObjectArraySpecial);

    allPublished = NewList();
    AddToReferenceList (allPublished);
#ifdef SAVEDELETED
    allDeletedPublished = NewList();
    AddToReferenceList (allDeletedPublished);
#endif

#endif
}

void KillNetObjects()
{
#ifdef SOCKETS
    RemoveFromReferenceList(allPublished);
#endif
}
