/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Module to handle the windows used by the solid modeller.		     *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "program.h"
#include "irit_soc.h"
#include "iritprsr.h"
#ifdef DJGCC
#include "iritgrap.h"
#endif /* DJGCC */

#define IRIT_PROMPT	"Irit> "
#define MAX_NUM_OF_IO_HANDLES	50

typedef struct IOHandlesStruct {
    int Input, Output;
    int InUse;
} IOHandlesStruct;
static IOHandlesStruct IOHandles[MAX_NUM_OF_IO_HANDLES];

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to toggle the input window log file printing. If turned on, test   M
* is made if file has been opened and if not open it.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   Set:       TRUE for log file active, FALSE inactive, or string name      M
*	       for a new logfile name.					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwLogPrint                                                             M
*****************************************************************************/
void WndwLogPrint(IPObjectStruct *SetObj)
{
    char s[LINE_LEN];

    if (IP_IS_NUM_OBJ(SetObj)) {
	int Set = REAL_TO_INT(SetObj -> U.R);

	if (Set) {
	    if (GlblLogFile == NULL) {	    /* Not open yet - open it now: */
		if ((GlblLogFile = fopen(GlblLogFileName, "w")) == NULL) {
		    sprintf(s, "Failed to open log file \"%s\"",
			    GlblLogFileName);
		    WndwInputWindowPutStr(s);
		    return;
		}
		GlblPrintLogFile = TRUE;
	    }
	}
	else {
	    GlblPrintLogFile = FALSE;
	    fflush(GlblLogFile);
	}
    }
    else if (IP_IS_STR_OBJ(SetObj)) {
	GlblPrintLogFile = FALSE;
	if (GlblLogFile != NULL) {
	    fclose(GlblLogFile);
	    GlblLogFile = NULL;
	}
	if (strlen(GlblLogFileName) > 0)
	    IritFree(GlblLogFileName);
	GlblLogFileName = IritStrdup(SetObj -> U.Str);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to handle the text on the Input Window. This window echoes all     M
* the input steam - the input parser input. Errors or information are also   M
* calling this function.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   Msg:        To print to stdout.                                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwInputWindowPutStr                                                    M
*****************************************************************************/
void WndwInputWindowPutStr(char *Msg)
{
    char str[INPUT_LINE_LEN];

#if defined(__UNIX__) || defined(OS2GCC) || defined(__WINNT__) || defined(AMIGA)
    /* No filtering - what we get is what is printed. */
    strcpy(str, Msg);
#else
    int i;

    for (i = 0; *Msg != 0; Msg++) {
	if (*Msg == 0x09)
	    do {
		str[i++] = ' ';
	    }
	    while (i % 8 != 0);
#ifdef AMIGA
	else if (abs(*Msg) < ' ' || (abs(*Msg) > '~' && abs(*Msg) <0xA0))
#else
	else if (*Msg < ' ' || *Msg > '~')
#endif
	    break;
	else
	    str[i++] = *Msg;
    }
    str[i] = 0;
#endif /* __UNIX__ || OS2GCC || __WINNT */

    if (GlblPrintLogFile)
	fprintf(GlblLogFile, "%s\n", str);

#ifdef DJGCC
    if (GlblDoGraphics) {
	IntrPrintf(IGGlblInputWindowID, TRUE, str);
    }
    else {
	printf("%s\n", str);
	fflush(stdout);
    }
#else
    if (str[strlen(str) - 1] == '\n')
	printf("%s", str);
    else
	printf("%s\n", str);
    fflush(stdout);
#endif /* DJGCC */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Same as WndwInputWindowPutStr but does not put a new line if have none.    M
*                                                                            *
* PARAMETERS:                                                                M
*   Msg:        To print to stdout.                                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwInputWindowPutStr2                                                   M
*****************************************************************************/
void WndwInputWindowPutStr2(char *Msg)
{
    char str[INPUT_LINE_LEN];

#if defined(__UNIX__) || defined(OS2GCC) || defined(__WINNT__) || defined(AMIGA)
    /* No filtering - what we get is what is printed. */
    strcpy(str, Msg);
#else
    int i;

    for (i = 0; *Msg != 0; Msg++) {
	if (*Msg == 0x09)
	    do {
		str[i++] = ' ';
	    }
	    while (i % 8 != 0);
#ifdef AMIGA
	else if (abs(*Msg) < ' ' || (abs(*Msg) > '~' && abs(*Msg) <0xA0))
#else
	else if (*Msg < ' ' || *Msg > '~')
#endif
	    break;
	else
	    str[i++] = *Msg;
    }
    str[i] = 0;
#endif /* __UNIX__ || OS2GCC || __WINNT */

    if (GlblPrintLogFile)
	fprintf(GlblLogFile, "%s\n", str);

#ifdef DJGCC
    if (GlblDoGraphics) {
	IntrPrintf(IGGlblInputWindowID, TRUE, str);
    }
    else {
	printf("%s\n", str);
	fflush(stdout);
    }
#else
    printf("%s", str);
    fflush(stdout);
#endif /* DJGCC */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to handle reading one line from stdin into string Str, with        M
* maximum of length Length in the Input Window.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   Str:        Where input is placed at.                                    M
*   Length:     Maximum length of input to allow.                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwInputWindowGetStr                                                    M
*****************************************************************************/
void WndwInputWindowGetStr(char *Str, int Length)
{
#ifdef DJGCC
    if (GlblDoGraphics) {
	Str[0] = 0;
    	IntrGetLineWindow(IGGlblInputWindowID, Str, Length);
    }
    else {
	printf(IRIT_PROMPT);
	fgets(Str, Length - 1, stdin);
	if (feof(stdin))
	    IritExit(0);	   /* Eof typed on keyboard (usually CtrlD). */
	if (Str[strlen(Str) - 1] < ' ')
	    Str[strlen(Str) - 1] = 0;				/* No CR/LF. */
	puts(Str);
	fflush(stdout);
    } 
#else
    printf(IRIT_PROMPT);
    fgets(Str, Length - 1, stdin);
    if (feof(stdin))
	IritExit(0);		   /* Eof typed on keyboard (usually CtrlD). */
    if (Str[strlen(Str) - 1] < ' ') 
	Str[strlen(Str) - 1] = 0;				/* No CR/LF. */
    puts(Str);
    fflush(stdout);
#endif /* DJGCC */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to handle reading one line from stdin into a object of type Type.  M
*                                                                            *
* PARAMETERS:                                                                M
*   Type:      Type of object requested.                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   An object of type Type initialized from data from    M
*                       stdin.                                               M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwInputStdinObject                                                     M
*****************************************************************************/
IPObjectStruct *WndwInputStdinObject(RealType *Type)
{
    char Str[LINE_LEN];
    RealType R, Pt[3];
    IPObjectStruct *PObj;
    IPObjStructType
	IType = (IPObjStructType) REAL_PTR_TO_INT(Type);

#ifdef DJGCC
    if (GlblDoGraphics) {
	Str[0] = 0;
    	IntrGetLineWindow(IGGlblInputWindowID, Str, LINE_LEN);
    }
    else {
	fgets(Str, LINE_LEN - 1, stdin);
	if (feof(stdin))
	    IritExit(0);	   /* Eof typed on keyboard (usually CtrlD). */
	if (Str[strlen(Str) - 1] < ' ')
	    Str[strlen(Str) - 1] = 0;				/* No CR/LF. */
    } 
#else
    fgets(Str, LINE_LEN - 1, stdin);
    if (feof(stdin))
	IritExit(0);		   /* Eof typed on keyboard (usually CtrlD). */
    if (Str[strlen(Str) - 1] < ' ') 
	Str[strlen(Str) - 1] = 0;				/* No CR/LF. */
#endif /* DJGCC */

#ifdef IRIT_DOUBLE
    switch (IType) {
	case IP_OBJ_NUMERIC:
	    if (sscanf(Str, "%lf", &R) == 1)
		return GenNUMObject(&R);
	    break;
	case IP_OBJ_POINT:
	    if (sscanf(Str, "%lf %lf %lf", &Pt[0], &Pt[1], &Pt[2]) == 3 ||
		sscanf(Str, "%lf,%lf,%lf", &Pt[0], &Pt[1], &Pt[2]) == 3)
		return GenPTObject(&Pt[0], &Pt[1], &Pt[2]);
	    break;
	case IP_OBJ_VECTOR:
	    if (sscanf(Str, "%lf %lf %lf", &Pt[0], &Pt[1], &Pt[2]) == 3 ||
		sscanf(Str, "%lf,%lf,%lf", &Pt[0], &Pt[1], &Pt[2]) == 3)
		return GenVECObject(&Pt[0], &Pt[1], &Pt[2]);
	    break;
	case IP_OBJ_PLANE:
	    if (sscanf(Str, "%lf %lf %lf %lf",
		       &Pt[0], &Pt[1], &Pt[2], &Pt[3]) == 4 ||
		sscanf(Str, "%lf,%lf,%lf,%lf",
		       &Pt[0], &Pt[1], &Pt[2], &Pt[3]) == 4)
		return GenPLANEObject(&Pt[0], &Pt[1], &Pt[2], &Pt[3]);
	    break;
	default:
	    break;
    }
#else
    switch (IType) {
	case IP_OBJ_NUMERIC:
	    if (sscanf(Str, "%f", &R) == 1)
		return GenNUMObject(&R);
	    break;
	case IP_OBJ_POINT:
	    if (sscanf(Str, "%f %f %f", &Pt[0], &Pt[1], &Pt[2]) == 3 ||
		sscanf(Str, "%f,%f,%f", &Pt[0], &Pt[1], &Pt[2]) == 3)
		return GenPTObject(&Pt[0], &Pt[1], &Pt[2]);
	    break;
	case IP_OBJ_VECTOR:
	    if (sscanf(Str, "%f %f %f", &Pt[0], &Pt[1], &Pt[2]) == 3 ||
		sscanf(Str, "%f,%f,%f", &Pt[0], &Pt[1], &Pt[2]) == 3)
		return GenVECObject(&Pt[0], &Pt[1], &Pt[2]);
	    break;
	case IP_OBJ_PLANE:
	    if (sscanf(Str, "%f %f %f %f",
		       &Pt[0], &Pt[1], &Pt[2], &Pt[3]) == 4 ||
		sscanf(Str, "%f,%f,%f,%f",
		       &Pt[0], &Pt[1], &Pt[2], &Pt[3]) == 4)
		return GenPLANEObject(&Pt[0], &Pt[1], &Pt[2], &Pt[3]);
	    break;
	default:
	    break;
    }
#endif /* IRIT_DOUBLE */

    PObj = IPAllocObject("", IP_OBJ_STRING, NULL);
    PObj -> U.Str = IritStrdup(Str);
    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Sets the display device channel.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwViewSetDisplay                                                       M
*****************************************************************************/
void WndwViewSetDisplay(void)
{
    char
	*Program = getenv("IRIT_DISPLAY");

    if (!GlblDoGraphics)
	return;

    if (!IritPrsrSrvrExecAndConnect(Program,
				    &GlblDisplayDeviceInput,
				    &GlblDisplayDeviceOutput,
				    getenv("IRIT_BIN_IPC") != NULL))
    {
	fprintf(stderr, "Failed to execute display device \"%s\"\n",
		Program == NULL ? "?" : Program);
	exit(0);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Display one object on display device.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Object to display.                                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwViewObject                                                           M
*****************************************************************************/
void WndwViewObject(IPObjectStruct *PObj)
{
    if (!GlblDoGraphics)
	return;

    if (GlblDisplayDeviceInput < 0 || GlblDisplayDeviceOutput < 0)
	WndwViewSetDisplay();

    if (IP_IS_STR_OBJ(PObj)) {
	if (GlblDisplayDeviceInput < 0)
	    return;

	if (strcmp(PObj -> Name, "COMMAND_") == 0 &&
	    (strcmp(PObj -> U.Str, "EXIT") == 0 ||
	     strcmp(PObj -> U.Str, "DISCONNECT") == 0)) {
	    IritPrsrSrvrKillAndDisConnect(strcmp(PObj -> U.Str, "EXIT") == 0,
					  GlblDisplayDeviceInput,
					  GlblDisplayDeviceOutput);
	    GlblDisplayDeviceInput = GlblDisplayDeviceOutput = -1;
	}
	else
	    SocWriteOneObject(GlblDisplayDeviceInput, PObj);
    }
    else
	SocWriteOneObject(GlblDisplayDeviceInput, PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Clears the display device's device.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwViewClearScreen                                                      M
*****************************************************************************/
void WndwViewClearScreen(void)
{
    IPObjectStruct
	*PObj = IPAllocObject("COMMAND_", IP_OBJ_STRING, NULL);

    strcpy(PObj -> U.Str, "CLEAR");
    if (GlblDisplayDeviceInput >= 0)
	WndwViewObject(PObj);

    IPFreeObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Asks the display device to save its current transformation matrix in       M
* file FileName.                                                             M
*                                                                            *
* PARAMETERS:                                                                M
*   FileName:   Name of file to save current transformation.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwViewSaveMatrix                                                       M
*****************************************************************************/
void WndwViewSaveMatrix(char *FileName)
{
    IPObjectStruct
	*PObj = IPAllocObject("COMMAND_", IP_OBJ_STRING, NULL);

    sprintf(PObj -> U.Str, "MSAVE %s", FileName);
    if (GlblDisplayDeviceInput >= 0)
	WndwViewObject(PObj);

    IPFreeObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Disconnect from current display device but dont ask it t quit.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwViewDisconnect                                                       M
*****************************************************************************/
void WndwViewDisconnect(void)
{
    IPObjectStruct
	*PObj = IPAllocObject("COMMAND_", IP_OBJ_STRING, NULL);

    strcpy(PObj -> U.Str, "DISCONNECT");
    if (GlblDisplayDeviceInput >= 0)
	WndwViewObject(PObj);

    IPFreeObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Disconnect from current display device and ask it to terminate.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   WndwViewExit                                                             M
*****************************************************************************/
void WndwViewExit(void)
{
    IPObjectStruct
	*PObj = IPAllocObject("COMMAND_", IP_OBJ_STRING, NULL);

    /* The second exit should fail on write closing the connection locally. */
    strcpy(PObj -> U.Str, "EXIT");

    if (GlblDisplayDeviceInput >= 0)
	WndwViewObject(PObj);

    IPFreeObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Executes the program named PrgmName as a subprocess and hook a           M
* bidirectional communication channel between this IRIT server and PrgmName. M
*                                                                            *
* PARAMETERS:                                                                M
*   PrgmName:   To execute as a subprocess.                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   RealType:   A handle to the input/output channel.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   ClientExecute, ipc                                                       M
*****************************************************************************/
RealType ClientExecute(char *PrgmName)
{
    int Input = -1,
	Output = -1;

    if (IritPrsrSrvrExecAndConnect(PrgmName, &Input, &Output,
				   getenv("IRIT_BIN_IPC") != NULL) &&
	Input >= 0 &&
	Output >= 0) {
	int i;

	for (i = 0; i < MAX_NUM_OF_IO_HANDLES; i++) {
	    if (!IOHandles[i].InUse) {
		IOHandles[i].Input = Input;
		IOHandles[i].Output = Output;
		IOHandles[i].InUse = TRUE;
		return i;
	    }
	}
	IritNonFatalError("Not enough client handlers, exec failed");
	return -1;
    }
    else {
	IritNonFatalError("Failed to execute program, exec failed");
	return -1;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Close a connection to a client subprocess specified by Handler.          M
*                                                                            *
* PARAMETERS:                                                                M
*   RHandler:    Valid subprocess handler.                                   M
*   KillClient:  If TRUE, a request for the client to die is sent.	     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   ClientClose, ipc                                                         M
*****************************************************************************/
void ClientClose(RealType *RHandler, int KillClient)
{
    int Handler = REAL_PTR_TO_INT(RHandler);

    if (Handler >= 0 &&
	Handler < MAX_NUM_OF_IO_HANDLES &&
	IOHandles[Handler].InUse) {
	IritPrsrSrvrKillAndDisConnect(KillClient,
				      IOHandles[Handler].Input,
				      IOHandles[Handler].Output);
	IOHandles[Handler].InUse = FALSE;
    }
    else
	IritNonFatalError("Invalid communication handler");
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Close a connection to all active clients.			             M
*                                                                            *
* PARAMETERS:                                                                M
*   KillClient:  If TRUE, a request for the client to die is sent.	     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   ClientCloseAll, ipc                                                      M
*****************************************************************************/
void ClientCloseAll(int KillClient)
{
    int i;

    for (i = 0; i < MAX_NUM_OF_IO_HANDLES; i++) {
	if (IOHandles[i].InUse) {
	    RealType
		R = i;

	    ClientClose(&R, KillClient);
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Read one object from subprocess's input channel specified by Handler.    M
*                                                                            *
* PARAMETERS:                                                                M
*   RHandler:  Valid subprocess handler.                                     M
*   Block:     If positive, blocks at most that much time if no object.      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   One read object. If timeout, a string object is      M
*			returned with content of "No data (timeout)".        M
*                                                                            *
* KEYWORDS:                                                                  M
*   ClientRead, ipc                                                          M
*****************************************************************************/
IPObjectStruct *ClientRead(RealType *RHandler, RealType *RBlock)
{
    int Handler = REAL_PTR_TO_INT(RHandler),
	Block = REAL_PTR_TO_INT(RBlock);
    IPObjectStruct
	*PObj = NULL;

    if (Handler >= 0 &&
	Handler < MAX_NUM_OF_IO_HANDLES &&
	IOHandles[Handler].InUse) {
	do {
	    PObj = SocReadOneObject(IOHandles[Handler].Output);

	    /* Sleep 10 miliseconds at a time, if no data. */
	    if (PObj == NULL) {
		Block -= 10;
		if (Block > 10)
		    IritSleep(10);
	    }
	    else
		Block = 0;
	}
	while (Block > 0);
    }
    else
	IritNonFatalError("Invalid communication handler");

    if (PObj == NULL)
	PObj = GenSTRObject("No data (timeout)");

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Writes one object to subprocess's output channel specified by Handler.   M
*                                                                            *
* PARAMETERS:                                                                M
*   RHandler:  Valid subprocess handler.                                     M
*   PObj:      Object to write.                                              M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   ClientWrite, ipc                                                         M
*****************************************************************************/
void ClientWrite(RealType *RHandler, IPObjectStruct *PObj)
{
    int Handler = REAL_PTR_TO_INT(RHandler);

    if (Handler >= 0 &&
	Handler < MAX_NUM_OF_IO_HANDLES &&
	IOHandles[Handler].InUse) {
	SocWriteOneObject(IOHandles[Handler].Input, PObj);
    }
    else
	IritNonFatalError("Invalid communication handler");
}

