/*****************************************************************************
*   General routines common to graphics driver.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "irit_sm.h"
#include "genmat.h"
#include "getarg.h"
#include "config.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "iritgrap.h"
#include "irit_soc.h"

#ifdef OS2GCC
#define INCL_DOSPROCESS
#include <os2.h>
#endif /* OS2GCC */

#define MAX_ROTATE_ANGLE	45.0 /* Max. rates used by interactive mode. */
#define MAX_TRANSLATE_FACTOR	2.0
#define MAX_SCALE_FACTOR	2.0
#define MAX_CLIP_FACTOR		0.5

typedef struct StateNameNumType {
   IGGlblStateType Num;
   char *Name;
} StateNameNumType;

static StateNameNumType StateNameNum[] =
{
    { IG_STATE_MORE_SENSITIVE,		"MoreSense" },
    { IG_STATE_LESS_SENSITIVE,		"LessSense" },
    { IG_STATE_SCR_OBJ_TGL,		"ScrnObjct" },
    { IG_STATE_PERS_ORTHO_TGL,		"PerpsOrtho" },
    { IG_STATE_DEPTH_CUE,		"DepthCue" },
    { IG_STATE_CACHE_GEOM,		"CacheGeom" },
    { IG_STATE_DRAW_SOLID,		"DrawSolid" },
    { IG_STATE_BACK_FACE_CULL,		"BFaceCull" },
    { IG_STATE_DOUBLE_BUFFER,		"DblBuffer" },
    { IG_STATE_ANTI_ALIASING,		"AntiAlias" },
    { IG_STATE_DRAW_INTERNAL,		"DrawIntrnl" },
    { IG_STATE_DRAW_VNORMAL,		"DrawVNrml" },
    { IG_STATE_DRAW_PNORMAL,		"DrawPNrml" },
    { IG_STATE_DRAW_SRF_MESH,		"DSrfMesh" },
    { IG_STATE_DRAW_SRF_POLY,		"DSrfPoly" },
    { IG_STATE_FOUR_PER_FLAT,		"4PerFlat" },
    { IG_STATE_MORE_ISOLINES,		"MoreIso" },
    { IG_STATE_LESS_ISOLINES,		"LessIso" },
    { IG_STATE_FINER_APPROX,		"FinrAprx" },
    { IG_STATE_COARSER_APPROX,		"CrsrAprx" },
    { IG_STATE_LONGER_VECTORS,		"LngrVecs" },
    { IG_STATE_SHORTER_VECTORS,		"ShrtrVecs" },
    { IG_STATE_WIDER_LINES,		"WiderLns" },
    { IG_STATE_NARROW_LINES,		"NarrwLns" },
    { IG_STATE_FINER_ADAP_ISO,		"FinrAdapIso" },
    { IG_STATE_COARSER_ADAP_ISO,	"CRsrAdapIso" },
    { IG_STATE_FINER_RULED_SRF,		"FinerRld" },
    { IG_STATE_COARSER_RULED_SRF,	"CrsrRld" },
    { IG_STATE_RULED_SRF_APPROX,	"RuledSrfApx" },
    { IG_STATE_ADAP_ISO_DIR,		"AdapIsoDir" },
    { IG_STATE_VIEW_FRONT,		"Front" },
    { IG_STATE_VIEW_SIDE,		"Side" },
    { IG_STATE_VIEW_TOP,		"Top" },
    { IG_STATE_VIEW_ISOMETRY,		"Isometry" },
    { IG_STATE_CLEAR_VIEW,		"Clear" },
    { 0,				NULL }
};

static int
    GlblDelayedClear = FALSE,
    GlblProcessCommandMesssages = TRUE,
    GlblNormalLenAux = 200;

static MatrixType PushViewMat, PushPrspMat;
MatrixType IGGlblIsometryViewMat = {	     /* Isometric view, by default. */
    { -0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.000000,  0.816496, 0.577350, 0.000000 },
    {  0.000000,  0.000000, 0.000000, 1.000000 }
};

static char
    *GlblExecAnimEachStep = NULL,
    *GlblLightSrcPosStr = "1.0, 2.0, 10.0, 0.0";

#ifdef __UNIX__
static char
    *GlblBackGroundStr = "20 20 20";

int IGGlblBackGroundColor[3] = { 20, 20, 20 },
#else
static char
    *GlblBackGroundStr = "63 63 63";

int IGGlblBackGroundColor[3] = { 63, 63, 63 },
#endif /* __UNIX__ */
    IGGlblDrawInternal = FALSE,
    IGGlblDrawVNormal = FALSE,
    IGGlblDrawPNormal = FALSE,
    IGGlblMore = FALSE,
    IGGlblPolygonOptiApprox = 0,
    IGGlblPolylineOptiApprox = 0,
    IGGlblForceUnitMat = FALSE,
    IGGlblDrawSolid = FALSE,
    IGGlblBackFaceCull = FALSE,
    IGGlblDoDoubleBuffer = TRUE,
    IGGlblNumOfIsolines = 10,
    IGGlblSamplesPerCurve = IG_DEFAULT_SAMPLES_PER_CURVE,
    IGGlblLineWidth = 1,
    IGGlblAdapIsoDir = CAGD_CONST_U_DIR,
    IGGlblDepthCue = TRUE,
    IGGlblCacheGeom = TRUE,
    IGGlblFourPerFlat = TRUE,
    IGGlblAntiAliasing = TRUE,
    IGGlblDrawSurfaceMesh = FALSE,
    IGGlblDrawSurfacePoly = FALSE,
    IGGlblStandAlone = TRUE,
    IGGlblTransformMode = IG_TRANS_SCREEN,
    IGGlblViewMode = IG_VIEW_PERSPECTIVE,
    IGGlblDebugObjectsFlag = FALSE,
    IGGlblDebugEchoInputFlag = FALSE,
    IGGlblIntensityHighState = TRUE,
    IGGlblDisplayListIsUsed = FALSE,
    IGGlblAbortKeyPressed = FALSE,
    IGGlblAnimation = FALSE,
    IGGlblNumFiles,
    IGGlblPrgmInput,
    IGGlblPrgmOutput;

char
    *IGGlblTransPrefPos = "455, 640, 520, 965",
    *IGGlblViewPrefPos =  "  1, 450, 520, 965",
    **IGGlblFileNames;

RealType
    IGGlblLightSrcPos[4] = { 1.0, 2.0, 10.0, 0.0 },
    IGGlblFineNess = 10.0,
    IGGlblChangeFactor = 1.0,
    IGGlblZMinClip = -2.0,
    IGGlblZMaxClip = 2.0,
    IGGlblNormalLen = 0.2;

MatrixType IGGlblCrntViewMat;

IPObjectStruct
    *IGGlblDisplayList = NULL;

AnimationStruct IGAnimation;

static ConfigStruct SetUp[] =
{
  { "TransPrefPos",   "-g", (VoidPtr) &IGGlblTransPrefPos,	SU_STRING_TYPE },
  { "ViewPrefPos",    "-G", (VoidPtr) &IGGlblViewPrefPos,	SU_STRING_TYPE },
  { "BackGround",     "-b", (VoidPtr) &GlblBackGroundStr,	SU_STRING_TYPE },
  { "LightSrcPos",    "-S", (VoidPtr) &GlblLightSrcPosStr,	SU_STRING_TYPE },
  { "ExecAnim",       "-x", (VoidPtr) &GlblExecAnimEachStep,	SU_STRING_TYPE },
  { "Internal",	      "-i", (VoidPtr) &IGGlblDrawInternal,	SU_BOOLEAN_TYPE },
  { "DrawVNormal",    "-n", (VoidPtr) &IGGlblDrawVNormal,	SU_BOOLEAN_TYPE },
  { "DrawPNormal",    "-N", (VoidPtr) &IGGlblDrawPNormal,	SU_BOOLEAN_TYPE },
  { "MoreVerbose",    "-m", (VoidPtr) &IGGlblMore,		SU_BOOLEAN_TYPE },
  { "UnitMatrix",     "-u", (VoidPtr) &IGGlblForceUnitMat,	SU_BOOLEAN_TYPE },
  { "DrawSolid",      "-r", (VoidPtr) &IGGlblDrawSolid,		SU_BOOLEAN_TYPE },
  { "BFaceCull",      "-B", (VoidPtr) &IGGlblBackFaceCull,	SU_BOOLEAN_TYPE },
  { "DoubleBuffer",   "-2", (VoidPtr) &IGGlblDoDoubleBuffer,	SU_BOOLEAN_TYPE },
  { "DebugObjects",   "-d", (VoidPtr) &IGGlblDebugObjectsFlag,	SU_BOOLEAN_TYPE },
  { "DebugEchoInput", "-D", (VoidPtr) &IGGlblDebugEchoInputFlag,SU_BOOLEAN_TYPE },
  { "DepthCue",	      "-c", (VoidPtr) &IGGlblDepthCue,		SU_BOOLEAN_TYPE },
  { "CacheGeom",      "-C", (VoidPtr) &IGGlblCacheGeom,		SU_BOOLEAN_TYPE },
  { "FourPerFlat",    "-4", (VoidPtr) &IGGlblFourPerFlat,	SU_BOOLEAN_TYPE },
  { "AntiAlias",      "-a", (VoidPtr) &IGGlblAntiAliasing,	SU_BOOLEAN_TYPE },
  { "DrawSurfaceMesh","-M", (VoidPtr) &IGGlblDrawSurfaceMesh,	SU_BOOLEAN_TYPE },
  { "DrawSurfacePoly","-P", (VoidPtr) &IGGlblDrawSurfacePoly,	SU_BOOLEAN_TYPE },
  { "StandAlone",     "-s", (VoidPtr) &IGGlblStandAlone,	SU_BOOLEAN_TYPE },
  { "NumOfIsolines",  "-I", (VoidPtr) &IGGlblNumOfIsolines,	SU_INTEGER_TYPE },
  { "SamplesPerCurve","-f", (VoidPtr) &IGGlblSamplesPerCurve,	SU_INTEGER_TYPE },
  { "LineWidth",      "-l", (VoidPtr) &IGGlblLineWidth,		SU_INTEGER_TYPE },
  { "AdapIsoDir",     "",   (VoidPtr) &IGGlblAdapIsoDir,	SU_INTEGER_TYPE },
  { "PolygonOpti",    "-F", (VoidPtr) &IGGlblPolygonOptiApprox,	SU_INTEGER_TYPE },
  { "PolylineOpti",   "-f", (VoidPtr) &IGGlblPolylineOptiApprox,SU_INTEGER_TYPE },
  { "TransMode",      "",   (VoidPtr) &IGGlblTransformMode,	SU_INTEGER_TYPE },
  { "ViewMode",	      "",   (VoidPtr) &IGGlblViewMode,		SU_INTEGER_TYPE },
  { "NormalLength",   "-L", (VoidPtr) &GlblNormalLenAux,	SU_INTEGER_TYPE },
  { "ZClipMin",	      "-Z", (VoidPtr) &IGGlblZMinClip,		SU_REAL_TYPE },
  { "ZClipMax",	      "-Z", (VoidPtr) &IGGlblZMaxClip,		SU_REAL_TYPE },
  { "FineNess",	      "-F", (VoidPtr) &IGGlblFineNess,		SU_REAL_TYPE }
};
#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

#ifdef NO_CONCAT_STR
static char
    *GenVersionStr = "	 Version 5.0,	Gershon Elber,\n\
	(C) Copyright 1989/90-95 Gershon Elber, Non commercial use only.";
#else
static char
    *GenVersionStr = "	" VERSION ",	Gershon Elber,	"
    __DATE__ ",   " __TIME__ "\n"
    "(C) Copyright 1989/90-95 Gershon Elber, Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char
#ifdef DOUBLE
    *GenCtrlStr = " s%- u%- n%- N%- i%- c%- C%- m%- a%- g%-x1,x2,y1,y2!s G%-x1,x2,y1,y2!s I%-#IsoLines!d F%-PolygonOpti|FineNess!d!F f%-PolylineOpti|SampPerCrv!d!d l%-LineWidth!d r%- B%- 2%- d%- D%- L%-NormalLen!d 4%- b%-BackGround!d!d!d S%-LgtSrcPos!F!F!F!F Z%-ZMin|ZMax!F!F M%- P%- x%-ExecAnim!s z%- DFiles!*s";
#else
    *GenCtrlStr = " s%- u%- n%- N%- i%- c%- C%- m%- a%- g%-x1,x2,y1,y2!s G%-x1,x2,y1,y2!s I%-#IsoLines!d F%-PolygonOpti|FineNess!d!f f%-PolylineOpti|SampPerCrv!d!d l%-LineWidth!d r%- B%- 2%- d%- D%- L%-NormalLen!d 4%- b%-BackGround!d!d!d S%-LgtSrcPos!f!f!f!f Z%-ZMin|ZMax!f!f M%- P%- x%-ExecAnim!s z%- DFiles!*s";
#endif /* DOUBLE */

static void HandleAnimate(char *Params);
static void AddReplaceObjDisplayList(IPObjectStruct **DisplayList,
				     IPObjectStruct *NewObj,
				     char *ObjName);
static int ReplyWithObject(IPObjectStruct *DisplayList, char *ObjName);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets up configuration of global variables.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PrgmName:    Name of program invoking this module.                       M
*   argc, argv:  Comand line.						     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGConfigureGlobals                                                       M
*****************************************************************************/
void IGConfigureGlobals(char *PrgmName, int argc, char **argv)
{
    int Error,
	TransPosFlag = FALSE,
	ViewPosFlag = FALSE,
	IsoLinesFlag = FALSE,
	SrfFinenessFlag = FALSE,
	CrvFinenessFlag = FALSE,
	LineWidthFlag = FALSE,
	NormalLenFlag = FALSE,
	BackGroundFlag = FALSE,
	LightSrcPosFlag = FALSE,
	ZClipPlaneFlag = FALSE,
	ExecAnimEachStepFlag = FALSE,
	VerFlag = FALSE;
    char Line[LINE_LEN_VLONG];

    Config(PrgmName, SetUp, NUM_SET_UP);   /* Read config. file if exists. */

    sscanf(GlblBackGroundStr, "%d %d %d",
	   &IGGlblBackGroundColor[0],
	   &IGGlblBackGroundColor[1],
	   &IGGlblBackGroundColor[2]);
    sscanf(GlblLightSrcPosStr,
#ifdef DOUBLE
	   "%lf,%lf,%lf,%lf",
#else
	   "%f,%f,%f,%f",
#endif /* DOUBLE */
	   &IGGlblLightSrcPos[0],
	   &IGGlblLightSrcPos[1],
	   &IGGlblLightSrcPos[2],
	   &IGGlblLightSrcPos[3]);

    strcpy(Line, PrgmName);
    strcat(Line, GenCtrlStr);

    if ((Error = GAGetArgs(argc, argv, Line, &IGGlblStandAlone,
			   &IGGlblForceUnitMat, &IGGlblDrawVNormal,
			   &IGGlblDrawPNormal, &IGGlblDrawInternal,
			   &IGGlblDepthCue, &IGGlblCacheGeom,
			   &IGGlblMore, &IGGlblAntiAliasing,
			   &TransPosFlag, &IGGlblTransPrefPos,
			   &ViewPosFlag, &IGGlblViewPrefPos,
			   &IsoLinesFlag, &IGGlblNumOfIsolines,
			   &SrfFinenessFlag, &IGGlblPolygonOptiApprox,
			   &IGGlblFineNess, &CrvFinenessFlag,
			   &IGGlblPolylineOptiApprox,
			   &IGGlblSamplesPerCurve, &LineWidthFlag,
			   &IGGlblLineWidth, &IGGlblDrawSolid,
			   &IGGlblBackFaceCull, &IGGlblDoDoubleBuffer,
			   &IGGlblDebugObjectsFlag,
			   &IGGlblDebugEchoInputFlag, &NormalLenFlag,
			   &GlblNormalLenAux, &IGGlblFourPerFlat,
			   &BackGroundFlag, &IGGlblBackGroundColor[0],
			   &IGGlblBackGroundColor[1],
			   &IGGlblBackGroundColor[2],
			   &LightSrcPosFlag, &IGGlblLightSrcPos[0],
			   &IGGlblLightSrcPos[1],
			   &IGGlblLightSrcPos[2],
			   &IGGlblLightSrcPos[3], &ZClipPlaneFlag,
			   &IGGlblZMinClip, &IGGlblZMaxClip,
			   &IGGlblDrawSurfaceMesh,
			   &IGGlblDrawSurfacePoly,
			   &ExecAnimEachStepFlag, &GlblExecAnimEachStep,
			   &VerFlag,
			   &IGGlblNumFiles, &IGGlblFileNames )) != 0) {
	GAPrintErrMsg(Error); GAPrintHowTo(Line);
	exit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s%s\n\n", PrgmName, GenVersionStr);
	GAPrintHowTo(Line);
	ConfigPrint(SetUp, NUM_SET_UP);
	exit(0);
    }

    if (!IGGlblStandAlone)
	IritPrsrClntAcceptConnect(&IGGlblPrgmInput, &IGGlblPrgmOutput);
    if (IGGlblDebugEchoInputFlag)
	SocEchoInput(IGGlblPrgmInput, TRUE);

    IGGlblNormalLen = GlblNormalLenAux / 1000.0;

    if (IGGlblAdapIsoDir == 1)
	IGGlblAdapIsoDir = CAGD_CONST_U_DIR;
    else if (IGGlblAdapIsoDir == 2)
	IGGlblAdapIsoDir = CAGD_CONST_V_DIR;

    /* Get the data files: */
    if (IGGlblNumFiles > 0 &&
	(IGGlblDisplayList = IritPrsrGetDataFiles(IGGlblFileNames,
						  IGGlblNumFiles, TRUE, TRUE))
								     != NULL) {
	/* We expects a linear linked list - no hierarchy. */
	IGGlblDisplayList = IritPrsrFlattenTree(IGGlblDisplayList);

	IGGlblViewMode = IritPrsrWasPrspMat ? IG_VIEW_PERSPECTIVE
					    : IG_VIEW_ORTHOGRAPHIC;
    }

    if (IGGlblForceUnitMat) {
	MatGenUnitMat(IritPrsrViewMat);
	IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
    }

    /* Make sure that if we ask to draw solid, depth cueing is disabled. */
    if (IGGlblDrawSolid)
	IGGlblDepthCue = FALSE;

    GEN_COPY(PushViewMat, IritPrsrViewMat, sizeof(MatrixType));
    GEN_COPY(PushPrspMat, IritPrsrPrspMat, sizeof(MatrixType));

    AnimResetAnimStruct(&IGAnimation);
    if (GlblExecAnimEachStep != NULL && strlen(GlblExecAnimEachStep) > 0)
	IGAnimation.ExecEachStep = GlblExecAnimEachStep;
    AnimFindAnimationTime(&IGAnimation, IGGlblDisplayList);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Are we suppose to handle command messages of the socket? A command message M
* will be a string objct sent over the socket with object name "COMMAND_".   M
*                                                                            *
* PARAMETERS:                                                                M
*   ProcessCommandMessages:   Sets the command message handling option.      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGProcessCommandMessages                                                 M
*****************************************************************************/
void IGProcessCommandMessages(int ProcessCommandMessages)
{
    GlblProcessCommandMesssages = ProcessCommandMessages;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reads an object from communication socket.				     M
*   Returns TRUE if screen needs to be redrawn in the case that the	     M
* DisplayList was modified.						     M
*   This function DOES NOT BLOCK if no input is unavailable.		     M
*   Handles commands via a string object with name "COMMAND_", if required   M
*                                                                            *
* PARAMETERS:                                                                M
*   ViewMode:      Either perspective or orthographics.                      M
*   DisplayList:   Global object display list. Will be updated in place.     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:           TRUE, if display needs to be refreshed.                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGReadObjectsFromSocket                                                  M
*****************************************************************************/
int IGReadObjectsFromSocket(int ViewMode, IPObjectStruct **DisplayList)
{
    int Redraw = FALSE;

    IPObjectStruct *PObjs;

    if ((PObjs = SocReadOneObject(IGGlblPrgmInput)) != NULL) {
	IPObjectStruct *PObj;

	/* We expects a linear linked list - no hierarchy. */
	PObjs = IritPrsrFlattenTree(PObjs);

#       ifdef __WINNT__
	    /* Slim chance that a race will oocur if the following test and */
	    /* set is done simultanuously. Should use semaphors probably.   */
	    while (IGGlblDisplayListIsUsed)
	        IritSleep(10);
	    IGGlblDisplayListIsUsed = TRUE;
#	endif /* __WINNT__ */
#	ifdef OS2GCC
	while (IGGlblDisplayListIsUsed) {
	    IritSleep(10);
	    DosEnterCritSec();
	    if (!IGGlblDisplayListIsUsed) {
		IGGlblDisplayListIsUsed = TRUE;
		DosExitCritSec();
		break;
	    }
	    DosExitCritSec();
	}
#	endif /* OS2GCC */

	Redraw = TRUE;

	if (GlblDelayedClear) {
#	    ifdef OS2GCC
	        /* For some reason without this delay, this  */
	        /* driver crashes on command toggles.	     */
		IritSleep(100);
#	    endif /* OS2GCC */
	    GlblDelayedClear = FALSE;
	    IPFreeObjectList(*DisplayList);
	    *DisplayList = NULL;
	}

	while (PObjs != NULL) {
	    PObj = PObjs;
	    PObjs = PObjs -> Pnext;
	    PObj -> Pnext = NULL;

	    if (IGGlblDebugObjectsFlag)
		IritPrsrPutObjectToFile(stderr, PObj);

	    if (GlblProcessCommandMesssages) {
		switch (PObj -> ObjType) {
		    case IP_OBJ_STRING:
		        if (stricmp(PObj -> Name, "COMMAND_") == 0) {
			    char
				*Str = PObj -> U.Str;

#			    ifdef OS2GCC
			        /* For some reason without this delay, this  */
			        /* driver crashes on command toggles.	     */
				IritSleep(300);
#			    endif /* OS2GCC */
			    if (stricmp(Str, "BEEP") == 0) {
				IGIritBeep();
				Redraw = FALSE;
			    }
			    else if (stricmp(Str, "CLEAR") == 0) {
				IPFreeObjectList(*DisplayList);
				*DisplayList = NULL;
			    }
			    else if (stricmp(Str, "DCLEAR") == 0) {
				GlblDelayedClear = TRUE;
				Redraw = FALSE;
			    }
			    else if (stricmp(Str, "DISCONNECT") == 0) {
				IritPrsrClntCloseConnect(IGGlblPrgmInput,
							 IGGlblPrgmOutput);
				Redraw = FALSE;
			    }
			    else if (stricmp(Str, "EXIT") == 0) {
				IritPrsrClntCloseConnect(IGGlblPrgmInput,
							 IGGlblPrgmOutput);
				exit(0);
			    }
			    else if (strnicmp(Str, "GETOBJ", 6) == 0) {
				if (!ReplyWithObject(*DisplayList,
						     &PObj -> U.Str[7]))
				    fprintf(stderr,
					    "No such object \"%s\"\n",
					    &PObj -> U.Str[7]);
				Redraw = FALSE;
			    }
			    else if (strnicmp(Str, "MSAVE", 5) == 0) {
				IGSaveCurrentMat(ViewMode, &PObj -> U.Str[6]);
				Redraw = FALSE;
			    }
			    else if (strnicmp(Str, "REMOVE", 6) == 0) {
				AddReplaceObjDisplayList(DisplayList, NULL,
							 &PObj -> U.Str[7]);
				Redraw = TRUE;
			    }
			    else if (strnicmp(Str, "ANIMATE", 7) == 0) {
				HandleAnimate(&Str[7]);
				Redraw = TRUE;
			    }
			    else if (strnicmp(Str, "STATE", 5) == 0) {
				int i;

				Str = &Str[6];

				for (i = 0;
				     StateNameNum[i].Name != NULL;
				     i++) {
				    if (stricmp(Str,
						StateNameNum[i].Name) == 0) {
					IGHandleState(StateNameNum[i].Num,
						      TRUE);
					break;
				    }
				}
			    }
			}
			IPFreeObject(PObj);
			break;
		    case IP_OBJ_MATRIX:
			if (strnicmp(PObj -> Name, "VIEW_MAT", 8) == 0) {
			    GEN_COPY(IritPrsrViewMat, *PObj -> U.Mat,
				     sizeof(MatrixType));
			}
			else if (strnicmp(PObj -> Name, "PRSP_MAT", 8) == 0) {
			    GEN_COPY(IritPrsrPrspMat, *PObj -> U.Mat,
				     sizeof(MatrixType));
			}
			IPFreeObject(PObj);
			break;
		    default:
			AddReplaceObjDisplayList(DisplayList, PObj, NULL);
			break;
		}
	    }
	    else {
		AddReplaceObjDisplayList(DisplayList, PObj, NULL);
	    }
	}

	IGGlblDisplayListIsUsed = FALSE;

	return Redraw;
    }
    else
	return Redraw;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Honors an animate request from the server.                               *
*                                                                            *
* PARAMETERS:                                                                *
*   Params:    A strings contains Tmin Tmax Dt, in this order.               *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void HandleAnimate(char *Params)
{
    double TMin, TMax, Dt;

    if (sscanf(Params, "%lf %lf %lf", &TMin, &TMax, &Dt) == 3) {
	IGAnimation.StartT = TMin;
	IGAnimation.FinalT = TMax;
	IGAnimation.Dt = Dt;
	AnimDoAnimation(&IGAnimation, IGGlblDisplayList);
    }
    else {
	fprintf(stderr,
		"Animate param, expected \"Tmin Tmax Dt\", found \"%s\"\n",
		Params);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Adds or replaces an object on the display list.			     *
*   If ObjName is not NULL, that object is removed from the display list.    *
*   Otherwise NewObj is added to the display list, possibly replacing an     *
* object on the display list with the same name.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   DisplayList:   Global display list to update.                            *
*   NewObj:        New object to add to the global display list.             *
*   ObjName:       Name of object to remove from display list.               *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void AddReplaceObjDisplayList(IPObjectStruct **DisplayList,
				     IPObjectStruct *NewObj,
				     char *ObjName)
{
    int Remove = ObjName != NULL;
    char
	*Name = ObjName != NULL ? ObjName : NewObj -> Name;
    IPObjectStruct *PObj;

    /* If object has no name or display list is empty, add it. */
    if (!Remove &&
	NewObj != NULL &&
	(strlen(NewObj -> Name) == 0 ||
	 NewObj -> Name[0] == '_' ||
	 stricmp(NewObj -> Name, "none") == 0)) {
	NewObj -> Pnext = *DisplayList;
	*DisplayList = NewObj;
	return;
    }
    if (*DisplayList == NULL) {
	if (NewObj != NULL) {
	    *DisplayList = NewObj;
	}
	return;
    }

    if (stricmp(Name, (*DisplayList) -> Name) == 0) {
	if (Remove) {
	    PObj = *DisplayList;
	    *DisplayList = (*DisplayList) -> Pnext;
	    IPFreeObject(PObj);
	}
	else {
	    NewObj -> Pnext = (*DisplayList) -> Pnext;
	    IPFreeObject(*DisplayList);
	    *DisplayList = NewObj;
	}
    }
    else {
	for (PObj = *DisplayList; PObj -> Pnext != NULL; PObj = PObj -> Pnext) {
	    if (stricmp(Name, PObj -> Pnext -> Name) == 0) {
		IPObjectStruct
		    *PObjTmp = PObj -> Pnext;

		if (Remove) {
		    PObj -> Pnext = PObjTmp -> Pnext;
		}
		else {
		    PObj -> Pnext = NewObj;
		    NewObj -> Pnext = PObjTmp -> Pnext;
		}
		IPFreeObject(PObjTmp);
		return;
	    }
	}

	/* Name was not found. */
	if (!Remove) {
	    NewObj -> Pnext = *DisplayList;
	    *DisplayList = NewObj;
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Returns innto the output channel the object requested via ObjName.       *
*                                                                            *
* PARAMETERS:                                                                *
*   DisplayList:   List of all current objects.                              *
*   ObjName:       Name of object to search.                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:	   TRUE if succesful, FALSE otherwise.                       *
*                                                                            *
* KEYWORDS:                                                                  *
*   ReplayWithObject                                                         *
*****************************************************************************/
static int ReplyWithObject(IPObjectStruct *DisplayList, char *ObjName)
{
    IPObjectStruct *PObj;

    for (PObj = DisplayList; PObj != NULL; PObj = PObj -> Pnext) {
	if (stricmp(ObjName, PObj -> Name) == 0) {
	    SocWriteOneObject(IGGlblPrgmOutput, PObj);
	    return TRUE;
	}
    }

    /* Dumps this string object instead, so we will not block the server. */
    PObj = GenSTRObject("no such object");
    SocWriteOneObject(IGGlblPrgmOutput, PObj);
    IPFreeObject(PObj);

    return FALSE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Saves the current viewing matrices.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   ViewMode:     Either perspective or orthographic.                        M
*   Name:         File name to save current view at.                         M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGSaveCurrentMat                                                         M
*****************************************************************************/
void IGSaveCurrentMat(int ViewMode, char *Name)
{
    int	i, j;
    FILE *f;

    if (Name == NULL)
	Name = IG_DEFAULT_IRIT_MAT;

    if (strlen(Name) == 0 || (f = fopen(Name, "wt")) == NULL) {
	IGIritBeep();
	return;
    }

    fprintf(f, "[OBJECT MATRICES\n    [OBJECT VIEW_MAT\n\t[MATRIX");
    for (i = 0; i < 4; i++) {
	fprintf(f, "\n\t    ");
	for (j = 0; j < 4; j++)
	    fprintf(f, "%12.9f ", IritPrsrViewMat[i][j]);
    }
    fprintf(f, "\n\t]\n    ]\n");

    if (ViewMode == IG_VIEW_PERSPECTIVE) {
	fprintf(f, "    [OBJECT PRSP_MAT\n\t[MATRIX");
	for (i = 0; i < 4; i++) {
	    fprintf(f, "\n\t    ");
	    for (j = 0; j < 4; j++)
		fprintf(f, "%12.9f ", IritPrsrPrspMat[i][j]);
	}
	fprintf(f, "\n\t]\n    ]\n");
    }

    fprintf(f, "]\n");

    fclose(f);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Processesthe given event. Returns TRUE if redraw of view window is needed  M
*                                                                            *
* PARAMETERS:                                                                M
*   Event:          Event to process.                                        M
*   ChangeFactor:   A continuous scale between -1 and 1 to quantify the      M
*                   change to apply according to the event type.	     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:            TRUE if refresh is needed.                               M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGProcessEvent                                                           M
*****************************************************************************/
int IGProcessEvent(IGGraphicEventType Event, RealType ChangeFactor)
{
    int UpdateView = TRUE;
    MatrixType Mat;

    MatGenUnitMat(Mat);

    switch (Event) {
	case IG_EVENT_SCR_OBJ_TGL:    /* Its Coordinate system - toggle it. */
	    UpdateView = FALSE;
	    break;
	case IG_EVENT_PERS_ORTHO_TGL:	      /* Its View mode - toggle it. */
	    break;
	case IG_EVENT_PERS_ORTHO_Z: /* Its Perspective Z focal point modif. */
	    if (IGGlblViewMode != IG_VIEW_PERSPECTIVE) {
		IGIritBeep();
		UpdateView = FALSE;
		break;
	    }
	    /* Make it between 0.5 and 1.5: */
	    ChangeFactor = ChangeFactor / 2.0 + 1.0;
	    IritPrsrPrspMat[2][2] *= ChangeFactor;
	    IritPrsrPrspMat[2][3] *= ChangeFactor;
	    IritPrsrPrspMat[3][2] *= ChangeFactor;
	    break;
	case IG_EVENT_ROTATE_X:		   /* Its rotation along the X axis. */
	    MatGenMatRotX1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
	    break;
	case IG_EVENT_ROTATE_Y:		   /* Its rotation along the Y axis. */
	    MatGenMatRotY1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
	    break;
	case IG_EVENT_ROTATE_Z:		   /* Its rotation along the Z axis. */
	    MatGenMatRotZ1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
	    break;
	case IG_EVENT_TRANSLATE_X:	/* Its translation along the X axis. */
	    MatGenMatTrans(ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, 0.0, Mat);
	    break;
	case IG_EVENT_TRANSLATE_Y:	/* Its translation along the Y axis. */
	    MatGenMatTrans(0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, Mat);
	    break;
	case IG_EVENT_TRANSLATE_Z:	/* Its translation along the Z axis. */
	    MatGenMatTrans(0.0, 0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, Mat);
	    break;
	case IG_EVENT_SCALE:		      /* Its scaling along all axes. */
	    if (ChangeFactor > 0.0)		      /* Make it around 1... */
	        ChangeFactor = ChangeFactor * MAX_SCALE_FACTOR + 1.0;
	    else
	        ChangeFactor = 1.0 / (-ChangeFactor * MAX_SCALE_FACTOR + 1.0);
	    MatGenMatUnifScale(ChangeFactor, Mat);
	    break;
	case IG_EVENT_NEAR_CLIP:		     /* Near plane clipping. */
	    IGGlblZMinClip += ChangeFactor * MAX_CLIP_FACTOR;
	    break;
	case IG_EVENT_FAR_CLIP:		              /* Far plane clipping. */
	    IGGlblZMaxClip += ChangeFactor * MAX_CLIP_FACTOR;
	    break;
        case IG_EVENT_ANIMATION:
	    IGGlblAnimation = TRUE;
	    AnimGetAnimInfoText(&IGAnimation);
	    AnimDoAnimation(&IGAnimation, IGGlblDisplayList);
	    IGGlblAnimation = FALSE;
	    break;
	case IG_EVENT_DEPTH_CUE:
	    break;
	case IG_EVENT_SAVE_MATRIX:
	    IGSaveCurrentMat(IGGlblViewMode, NULL);
	    UpdateView = FALSE;
	    break;
	case IG_EVENT_PUSH_MATRIX:
	    GEN_COPY(PushViewMat, IritPrsrViewMat, sizeof(MatrixType));
	    GEN_COPY(PushPrspMat, IritPrsrPrspMat, sizeof(MatrixType));
	    break;
	case IG_EVENT_POP_MATRIX:
	    GEN_COPY(IritPrsrViewMat, PushViewMat, sizeof(MatrixType));
	    GEN_COPY(IritPrsrPrspMat, PushPrspMat, sizeof(MatrixType));
	    break;
	case IG_EVENT_STATE:
	    IGCreateStateMenu();
	    break;
	default:
	    IGIritBeep();
	    UpdateView = FALSE;
    }

    if (UpdateView) {
	switch (IGGlblTransformMode) {/* Udpate the global viewing matrix. */
	    case IG_TRANS_SCREEN:
	        MatMultTwo4by4(IritPrsrViewMat, IritPrsrViewMat, Mat);
		break;
	    case IG_TRANS_OBJECT:
		MatMultTwo4by4(IritPrsrViewMat, Mat, IritPrsrViewMat);
		break;
	}
    }
    return UpdateView;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Free the attribute named Name from all objects in the IGGlblDisplayList.   M
*                                                                            *
* PARAMETERS:                                                                M
*   Name:      Name of attribute to remove.                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGActiveListFreeNamedAttribute                                           M
*****************************************************************************/
void IGActiveListFreeNamedAttribute(char *Name)
{
    IPObjectStruct
	*PObj = IGGlblDisplayList;

    for (; PObj != NULL; PObj = PObj -> Pnext) {
	AttrFreeOneAttribute(&PObj -> Attrs, Name);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Handle the event of a pop up window. This is the default handler which can M
* be invoked by other specific handlers for event they do not care about.    M
*                                                                            *
* PARAMETERS:                                                                M
*   State:      State event type to handle.                                  M
*   Refresh:    Not used.						     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE if needs to refresh.                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGDefaultStateHandler                                                    M
*****************************************************************************/
int IGDefaultStateHandler(int State, int Refresh)
{
    int UpdateView = TRUE;

    switch (State) {
	case IG_STATE_MORE_SENSITIVE:
	    IGGlblChangeFactor *= 2.0;
	    break;
	case IG_STATE_LESS_SENSITIVE:
	    IGGlblChangeFactor *= 0.5;
	    break;
	case IG_STATE_SCR_OBJ_TGL:
	    IGGlblTransformMode = IGGlblTransformMode == IG_TRANS_OBJECT ?
							 IG_TRANS_SCREEN :
							 IG_TRANS_OBJECT;
	    UpdateView = FALSE;
	    break;
	case IG_STATE_PERS_ORTHO_TGL:
	    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					       IG_VIEW_ORTHOGRAPHIC :
					       IG_VIEW_PERSPECTIVE;
	    break;
	case IG_STATE_BACK_FACE_CULL:
	    IGGlblBackFaceCull = !IGGlblBackFaceCull;
	    break;
	case IG_STATE_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    break;
	case IG_STATE_CACHE_GEOM:
	    fprintf(stderr, "You cannot change the geometry caching now\n");
	    break;
	case IG_STATE_DRAW_INTERNAL:
	    IGGlblDrawInternal = !IGGlblDrawInternal;
	    break;
	case IG_STATE_DRAW_VNORMAL:
	    IGGlblDrawVNormal = !IGGlblDrawVNormal;
	    break;
	case IG_STATE_DRAW_PNORMAL:
	    IGGlblDrawPNormal = !IGGlblDrawPNormal;
	    break;
	case IG_STATE_DRAW_SRF_MESH:
	    IGGlblDrawSurfaceMesh = !IGGlblDrawSurfaceMesh;
	    break;
	case IG_STATE_DRAW_SRF_POLY:
	    IGGlblDrawSurfacePoly = !IGGlblDrawSurfacePoly;
	    break;
	case IG_STATE_FOUR_PER_FLAT:
	    IGGlblFourPerFlat = !IGGlblFourPerFlat;
	    IGActiveListFreeNamedAttribute("_polygons");
	    break;
	case IG_STATE_MORE_ISOLINES:
	    IGGlblNumOfIsolines *= 2;
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_LESS_ISOLINES:
	    IGGlblNumOfIsolines /= 2;
	    if (IGGlblNumOfIsolines < 2)
		IGGlblNumOfIsolines = 2;
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_LONGER_VECTORS:
	    IGGlblNormalLen *= 2.0;
	    break;
	case IG_STATE_SHORTER_VECTORS:
	    IGGlblNormalLen /= 2.0;
	    break;
	case IG_STATE_FINER_APPROX:
	    IGGlblFineNess *= 2.0;
	    IGGlblSamplesPerCurve *= 2;
	    IGActiveListFreeNamedAttribute("_polygons");
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_COARSER_APPROX:
	    IGGlblFineNess /= 2.0;
	    if (IGGlblPolygonOptiApprox == 0 && IGGlblFineNess < 2.0)
		IGGlblFineNess = 2.0;
	    IGGlblSamplesPerCurve /= 2;
	    if (IGGlblSamplesPerCurve < 2)
		IGGlblSamplesPerCurve = 2;
	    IGActiveListFreeNamedAttribute("_polygons");
	    IGActiveListFreeNamedAttribute("_isoline");
	    break;
	case IG_STATE_VIEW_FRONT:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    MatGenUnitMat(IritPrsrViewMat);
	    break;
	case IG_STATE_VIEW_SIDE:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    MatGenMatRotY1(DEG2RAD(90.0), IritPrsrViewMat);
	    break;
	case IG_STATE_VIEW_TOP:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    MatGenMatRotX1(DEG2RAD(90.0), IritPrsrViewMat);
	    break;
	case IG_STATE_VIEW_ISOMETRY:
	    IGGlblViewMode = IG_VIEW_ORTHOGRAPHIC;
	    GEN_COPY(IritPrsrViewMat, IGGlblIsometryViewMat,
		     sizeof(MatrixType));
	    break;
	case IG_STATE_CLEAR_VIEW:
	    IPFreeObjectList(IGGlblDisplayList);
	    IGGlblDisplayList = NULL;
	    break;
	case IG_STATE_WIDER_LINES:
	    IGGlblLineWidth *= 2;
	    break;
	case IG_STATE_NARROW_LINES:
	    IGGlblLineWidth /= 2;
	    if (IGGlblLineWidth < 1)
		IGGlblLineWidth = 1;
	    break;
        case IG_STATE_ANIMATION:
	    IGGlblAnimation = TRUE;
	    AnimGetAnimInfoText(&IGAnimation);
	    AnimDoAnimation(&IGAnimation, IGGlblDisplayList);
	    IGGlblAnimation = FALSE;
	    break;
	default:
	    UpdateView = FALSE;
	    break;
    }

    return UpdateView;
}

#ifdef DEBUG

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Dummy function to link at debugging time.                               *
*                                                                            *
* PARAMETERS:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*****************************************************************************/
void DummyLinkCagdDebug(void)
{
    IritPrsrDbg();
}

#endif /* DEBUG */
