/*****************************************************************************
* Generic parser for the "Irit" solid modeller.				     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Gershon Elber				Ver 0.2, Sep. 1991   *
*****************************************************************************/

#ifdef USE_VARARGS
#include <varargs.h>
#else
#include <stdarg.h>
#endif /* USE_VARARGS */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <setjmp.h>
#include "irit_sm.h"
#include "prsr_loc.h"
#include "allocate.h"
#include "attribut.h"
#include "irit_soc.h"

#ifdef __WINNT__
#include <fcntl.h>
#include <io.h>
#endif /* __WINNT__ */

#define MAX_OBJECTS		1e6
#define CONVEX_IRIT_EPS		1e-3
#define ZERO_NUM_IRIT_EPS	1e-15
#define NORMAL_MIN_VALID_LEN	0.03

static char
    GlblTokenError[LINE_LEN_LONG];          /* Last token error was found. */
static IritPrsrPrintFuncType
    IritPrsrPrintFunc = NULL;

jmp_buf _IritPrsrLongJumpBuffer;		   /* Used in error traping. */
int _IritPrsrPolyListCirc = FALSE,
    _IritPrsrReadOneObject = FALSE,	/* If only one object is to be read. */
    IritPrsrWasViewMat = FALSE,
    IritPrsrWasPrspMat = FALSE;
IritPrsrErrType
    _IritPrsrGlblParserError = IP_NO_ERR;		/* Last err # found. */
char
    *_IPGlblFloatFormat = "%lg";

MatrixType IritPrsrViewMat = {		      /* 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 }
};
MatrixType IritPrsrPrspMat = {
    { 1, 0, 0, 0 },
    { 0, 1, 0, 0 },
    { 0, 0, 1, -0.35 },
    { 0, 0, 0, 1.0 }
};

static void IritPrsrPutAttributes(int Handler,
				  IPAttributeStruct *Attr,
				  int Indent);
static void IritPrsrPutAllObjects(IPObjectStruct *PObj,
				  int Handler,
				  int Indent);
static IPObjectStruct *IPGetObjectByNameAux(char *Name, IPObjectStruct *PObj);
static void IPTraverseObjListHierarchyAux(IPObjectStruct *PObj,
					  IPObjectStruct *PObjList,
					  IritPrsrApplyObjFuncType ApplyFunc,
					  MatrixType Mat,
					  int IsInstance);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to update the Plane equation of the given polygon by the order     M
* of the most robust three vertices of that polygon to define the normal.    M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:     To update its normal/plane equation.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:       TRUE if succesful, FALSE otherwise.                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrUpdatePolyPlane, files, parser                                   M
*****************************************************************************/
int IritPrsrUpdatePolyPlane(IPPolygonStruct *PPoly)
{
    int i;
    RealType Len, V1[3], V2[3],
	MaxLen = SQR(IRIT_EPS);
    IPVertexStruct
	*VLast = NULL,
	*V = PPoly -> PVertex;
    PlaneType Plane;

    if (V == NULL || V -> Pnext == NULL || V -> Pnext -> Pnext == NULL)
	_IPParserAbort(IP_ERR_DEGEN_POLYGON, "");

    for (i = 0; i < 4; i++)
	PPoly -> Plane[i] = 0.0;

    /* Force list to be circular. Will be recovered immediately after. */
    if (!_IritPrsrPolyListCirc) {
	VLast = IritPrsrGetLastVrtx(V);
	VLast -> Pnext = V;
    }

    do {
	PT_SUB(V1, V -> Coord, V -> Pnext -> Coord);
	V = V -> Pnext;
	PT_SUB(V2, V -> Coord, V -> Pnext -> Coord);

	Plane[0] = V1[1] * V2[2] - V2[1] * V1[2];
	Plane[1] = V1[2] * V2[0] - V2[2] * V1[0];
	Plane[2] = V1[0] * V2[1] - V2[0] * V1[1];

	/* Normalize the plane such that the normal has length of 1: */
	Len = PT_LENGTH(Plane);
	if (Len > MaxLen) {
	    for (i = 0; i < 3; i++)
		PPoly -> Plane[i] = Plane[i] / Len;

	    MaxLen = Len;

	    if (MaxLen > SQR(NORMAL_MIN_VALID_LEN))
	        break;
	}

	V = V -> Pnext;
    }
    while (V != PPoly -> PVertex &&
	   V -> Pnext != NULL &&
	   V -> Pnext -> Pnext != NULL);

    if (VLast != NULL) /* Recover non circular list, if was non circular. */
	VLast -> Pnext = NULL;

    if (MaxLen <= SQR(IRIT_EPS))
	return FALSE;

    PPoly -> Plane[3] =
	-DOT_PROD(PPoly -> Plane, PPoly -> PVertex -> Coord);

    IP_SET_PLANE_POLY(PPoly);

    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to update the Plane equation of the given polygon such that the  M
* Vin vertex will be in the positive side of it.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:     To update its normal/plane equation.                          M
*   Vin:       A vertex to be considered in the inside, respective to PPoly. M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:       TRUE if succesful, FALSE otherwise.                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrUpdatePolyPlane2, files, parser                                  M
*****************************************************************************/
int IritPrsrUpdatePolyPlane2(IPPolygonStruct *PPoly, VectorType Vin)
{
    int i;

    if (!IritPrsrUpdatePolyPlane(PPoly))
        return FALSE;

    if (DOT_PROD(PPoly -> Plane, Vin) + PPoly -> Plane[3] < 0) {
	/* Flip plane normal and reverse the vertex list. */
	IritPrsrReverseVrtxList(PPoly);
	for (i = 0; i < 4; i++)
	    PPoly -> Plane[i] = (-PPoly -> Plane[i]);
    }

    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to update all vertices in polygon to hold a default normal if      M
* have none already.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:       Polygon to update normal information.                       M
*   DefNrml:     Normal tp use in update.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrUpdateVrtxNrml, files, parser				     M
*****************************************************************************/
void IritPrsrUpdateVrtxNrml(IPPolygonStruct *PPoly, VectorType DefNrml)
{
    IPVertexStruct
	*V = PPoly -> PVertex;

    do {
	if (!IP_HAS_NORMAL_VRTX(V)) {
	    PT_COPY(V -> Normal, DefNrml);
	    IP_SET_NORMAL_VRTX(V);
	}
	V = V -> Pnext;
    }
    while (V != NULL && V != PPoly -> PVertex);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reverses a list of objects, in place.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      A list of objects to reverse.                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  Reverse list of objects, in place.                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrReverseObjList, reverse, files, parser                           M
*                                                                            M
*****************************************************************************/
IPObjectStruct *IritPrsrReverseObjList(IPObjectStruct *PObj)
{
    IPObjectStruct
	*NewPObjs = NULL;

    while (PObj) {
	IPObjectStruct
	    *Pnext = PObj -> Pnext;

	PObj -> Pnext = NewPObjs;
	NewPObjs = PObj;

	PObj = Pnext;
    }

    return NewPObjs;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reverses a list of polygons, in place.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PPl:      A list of polygons to reverse.                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:  Reversed list of polygons, in place.                 M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrReversePlList, reverse, files, parser                            M
*****************************************************************************/
IPPolygonStruct *IritPrsrReversePlList(IPPolygonStruct *PPl)
{
    IPPolygonStruct
	*NewPPls = NULL;

    while (PPl) {
	IPPolygonStruct
	    *Pnext = PPl -> Pnext;

	PPl -> Pnext = NewPPls;
	NewPPls = PPl;

	PPl = Pnext;
    }

    return NewPPls;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reverses the vertex list of a given polygon. This is used mainly to        M
* reverse polygons such that cross product of consecutive edges which form   M
* a convex corner will point in the polygon normal direction.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Pl:        A polygon to reverse its vertex list, in place.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrReverseVrtxList , reverse, files, parser                         M
*****************************************************************************/
void IritPrsrReverseVrtxList(IPPolygonStruct *Pl)
{
    ByteType Tags;
    IPVertexStruct *VNextNext, *VLast,
	*V = Pl -> PVertex,
	*VNext = V -> Pnext;

    /* Force list to be circular. Will be recovered immediately after. */
    if (!_IritPrsrPolyListCirc) {
	VLast = IritPrsrGetLastVrtx(V);
	VLast -> Pnext = V;
    }

    do {
	VNextNext = VNext -> Pnext;
	VNext -> Pnext = V;			     /* Reverse the pointer! */

	V = VNext;			   /* Advance all 3 pointers by one. */
	VNext = VNextNext;
	VNextNext = VNextNext -> Pnext;
    }
    while (V != Pl -> PVertex);

    V = Pl -> PVertex;	        /* Move the Tags by one - to the right edge. */
    Tags = V -> Tags;
    do {
	if (V -> Pnext == Pl -> PVertex) {
	    V -> Tags = Tags;
	}
	else {
	    V -> Tags = V -> Pnext -> Tags;
	}

	V = V -> Pnext;
    }
    while (V != Pl -> PVertex);

    /* Recover non circular list, if needs to. */
    if (!_IritPrsrPolyListCirc) {
	VLast = IritPrsrGetLastVrtx(Pl -> PVertex);
	VLast -> Pnext = NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reverses a list of vertices of a polyline, in place. The list is assumed   M
* to be non circular and hence can be treated as a polyline.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PVertex:      A list of vertices to reverse.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:  Reversed list of vertices, in place.		     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrReverseVrtxList2, reverse, files, parser                         M
*****************************************************************************/
IPVertexStruct *IritPrsrReverseVrtxList2(IPVertexStruct *PVrtx)
{
    IPVertexStruct
	*NewPVrtx = NULL;

    while (PVrtx) {
	IPVertexStruct
	    *Pnext = PVrtx -> Pnext;

	PVrtx -> Pnext = NewPVrtx;
	NewPVrtx = PVrtx;

	PVrtx = Pnext;
    }

    return NewPVrtx;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Merges seperated polylines into longer ones, as possible.                M
* Given a list of polylines, matches end points and merged as possible       M
* polylines with common end points, in place.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   Polys:      Polylines to merge.                                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:  Merged as possible polylines.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrMergePolylines, merge, polyline                                  M
*****************************************************************************/
IPPolygonStruct *IritPrsrMergePolylines(IPPolygonStruct *Polys)
{
    IPPolygonStruct *Pl, *Pl2, *Pl2Prev;

    for (Pl = Polys; Pl != NULL; ) {
	int HasChanged = FALSE;
	IPVertexStruct
	    *V1 = Pl -> PVertex,
	    *V2 = IritPrsrGetLastVrtx(V1);

	for (Pl2Prev = Pl, Pl2 = Pl -> Pnext; Pl2 != NULL && !HasChanged; ) { 
	    IPVertexStruct *V,
		*Pl2V1 = Pl2 -> PVertex,
		*Pl2V2 = IritPrsrGetLastVrtx(Pl2V1);
	    int FoundMatch = TRUE;

	    if (PT_APX_EQ(V1 -> Coord, Pl2V1 -> Coord)) {
		Pl -> PVertex = IritPrsrReverseVrtxList2(Pl -> PVertex);
		V = IritPrsrGetLastVrtx(Pl -> PVertex);
		V -> Pnext = Pl2 -> PVertex -> Pnext;
	    }
	    else if (PT_APX_EQ(V1 -> Coord, Pl2V2 -> Coord)) {
		Pl -> PVertex = IritPrsrReverseVrtxList2(Pl -> PVertex);
		Pl2 -> PVertex = IritPrsrReverseVrtxList2(Pl2 -> PVertex);
		V = IritPrsrGetLastVrtx(Pl -> PVertex);
		V -> Pnext = Pl2 -> PVertex -> Pnext;
	    }
	    else if (PT_APX_EQ(V2 -> Coord, Pl2V1 -> Coord)) {
		V2 -> Pnext = Pl2V1 -> Pnext;
	    }
	    else if (PT_APX_EQ(V2 -> Coord, Pl2V2 -> Coord)) {
		Pl2 -> PVertex = IritPrsrReverseVrtxList2(Pl2 -> PVertex);
		V2 -> Pnext = Pl2 -> PVertex -> Pnext;
	    }
	    else
	        FoundMatch = FALSE;

	    if (FoundMatch) {
		Pl2Prev -> Pnext = Pl2 -> Pnext;

		Pl2 -> PVertex -> Pnext = NULL;
		IPFreePolygon(Pl2);
		Pl2 = Pl2Prev -> Pnext;

		HasChanged = TRUE;
	    }
	    else {
	        Pl2Prev = Pl2;
		Pl2 = Pl2 -> Pnext;
	    }
	}

	if (!HasChanged)
	    Pl = Pl -> Pnext;
    }

    return Polys;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to abort parsing operation and save error reported.		     M
*   See also function IritPrsrParseError.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   ErrNum:   Type of error that had occured.                                M
*   Msg:      A message to accompany the error number.                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   _IPParserAbort, error handling, files, parser                            M
*****************************************************************************/
void _IPParserAbort(IritPrsrErrType ErrNum, char *Msg)
{
    _IritPrsrGlblParserError = ErrNum;
    strcpy(GlblTokenError, Msg);	/* Keep the message in safe place... */

    longjmp(_IritPrsrLongJumpBuffer, 1);		       /* Jump to... */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns TRUE if error has happend since last call to this function during  M
* data read or write, FALSE otherwise.					     M
*   If error, then ErrorMsg is updated to point on static str describing it. M
*                                                                            *
* PARAMETERS:                                                                M
*   LineNum:    Line number of error, in file/stream.	                     M
*   ErrorMsg:   To be updated with latest error to have happened in parser.  M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE  if error occured since last call, FALSE otherwise.     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrParseError, error handling, files, parser                        M
*****************************************************************************/
int IritPrsrParseError(int LineNum, char **ErrorMsg)
{
    IritPrsrErrType Temp;
    char TempCopy[LINE_LEN_LONG];

    if ((Temp = _IritPrsrGlblParserError) == IP_NO_ERR)
	return FALSE;

    strcpy(TempCopy, GlblTokenError);
    _IritPrsrGlblParserError = IP_NO_ERR;

    switch (Temp) {
	case IP_ERR_NUMBER_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: Numeric data expected - found %s",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_OPEN_PAREN_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: '[' expected - found '%s'",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_CLOSE_PAREN_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: ']' expected - found '%s'",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_LIST_COMP_UNDEF:
	    sprintf(GlblTokenError, "Line %d: Undefined list element - \"%s\"",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_UNDEF_EXPR_HEADER:
	    sprintf(GlblTokenError, "Line %d: Undefined TOKEN - \"%s\"",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_PT_TYPE_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: Point type expected",
		    LineNum);
	    break;
	case IP_ERR_OBJECT_EMPTY:
	    sprintf(GlblTokenError, "Line %d: Empty object found",
		    LineNum);
	    break;
	case IP_ERR_FILE_EMPTY:
	    sprintf(GlblTokenError, "Line %d: Empty object found",
		    LineNum);
	    break;
	case IP_ERR_MIXED_TYPES:
	    sprintf(GlblTokenError,
		    "Line %d: Mixed data types in same object",
		    LineNum);
	    break;
	case IP_ERR_STR_NOT_IN_QUOTES:
	    sprintf(GlblTokenError,
		    "Line %d: String not in quotes (%s)",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_OBJECT_EXPECTED:
	    sprintf(GlblTokenError,
		    "Line %d: 'OBJECT' expected, found '%s'",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_CAGD_LIB_ERR:
	case IP_ERR_TRIM_LIB_ERR:
	case IP_ERR_TRIV_LIB_ERR:
	case IP_ERR_TRNG_LIB_ERR:
	    sprintf(GlblTokenError, "Line %d: %s",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_STACK_OVERFLOW:
	    sprintf(GlblTokenError, "Line %d: Parser Stack overflow",
		    LineNum);
	    break;
	case IP_ERR_DEGEN_POLYGON:
	    sprintf(GlblTokenError, "Line %d: Degenerate polygon",
		    LineNum);
	    break;
	case IP_ERR_DEGEN_NORMAL:
	    sprintf(GlblTokenError, "Line %d: Degenerate normal",
		    LineNum);
	    break;
	case IP_ERR_SOCKET_BROKEN:
	    sprintf(GlblTokenError, "Line %d: Socket connection is broken",
		    LineNum);
	    break;
	case IP_ERR_SOCKET_TIME_OUT:
	    sprintf(GlblTokenError, "Line %d: Socket connection is broken",
		    LineNum);
	    break;
	case IP_ERR_BIN_IN_TEXT:
	    sprintf(GlblTokenError, "Binary information in text file - %s",
		    TempCopy);
	    break;
	case IP_ERR_BIN_UNDEF_OBJ:
	    sprintf(GlblTokenError, "Binary stream: Undefined object");
	    break;
	default:
	    sprintf(GlblTokenError,
		    "Line %d: Data file parser - undefined error",
		    LineNum);
	    break;
    }

    *ErrorMsg = GlblTokenError;

    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to test if the given polygon is convex (by IRIT definition) or     M
* not.									     M
*   Algorithm: The polygon is convex iff the normals generated from cross    M
* products of two consecutive edges points to the same direction. The same   M
* direction is tested by a positive dot product.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   Pl:         To test for convexity.                                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE if PL convex, FALSE otherwise.                          M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrIsConvexPolygon, convexity, files, parser                        M
*****************************************************************************/
int IritPrsrIsConvexPolygon(IPPolygonStruct *Pl)
{
    RealType Size, V1[3], V2[3], LastNormal[3], Normal[3];
    IPVertexStruct *VNext, *VNextNext,
	*V = Pl -> PVertex;

    LastNormal[0] = LastNormal[1] = LastNormal[2] = 0.0;

    do {
	if ((VNext = V -> Pnext) == NULL)
	    VNext = Pl -> PVertex;
	if ((VNextNext = VNext -> Pnext) == NULL)
	    VNextNext = Pl -> PVertex;

	PT_SUB(V1, VNext -> Coord, V -> Coord);
	if ((Size = PT_LENGTH(V1)) > IRIT_EPS) {
	    Size = 1.0 / Size;
	    PT_SCALE(V1, Size);
	}
	PT_SUB(V2, VNextNext -> Coord, VNext -> Coord);
	if ((Size = PT_LENGTH(V2)) > IRIT_EPS) {
	    Size = 1.0 / Size;
	    PT_SCALE(V2, Size);
	}
	CROSS_PROD(Normal, V1, V2);

	if (V != Pl -> PVertex) {
	    if (PT_LENGTH(Normal) > CONVEX_IRIT_EPS &&
		DOT_PROD(Normal, LastNormal) < -CONVEX_IRIT_EPS)
		return FALSE;
	}

	PT_COPY(LastNormal, Normal);

	V = VNext;
    }
    while (V != Pl -> PVertex && V != NULL);

    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into stdout.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To be put out to stdout.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrStdoutObject, files                                              M
*****************************************************************************/
void IritPrsrStdoutObject(IPObjectStruct *PObj)
{
    IritPrsrPutObjectToFile(stdout, PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into stderr.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To be put out to stderr.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrStderrObject, files                                              M
*****************************************************************************/
void IritPrsrStderrObject(IPObjectStruct *PObj)
{
    IritPrsrPutObjectToFile(stderr, PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into given file FileName.	     M
*   If FileName is NULL or empty, print using IritPrsrPrintFunc.	     M
*   See function IritPrsrSetPrintFunc, IritPrsrSetFloatFormat.    	     M
*                                                                            *
* PARAMETERS:                                                                M
*   f:        Output stream.                                                 M
*   PObj:     Object to put on output stream.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPutObjectToFile, files                                           M
*****************************************************************************/
void IritPrsrPutObjectToFile(FILE *f, IPObjectStruct *PObj)
{
    int Handler = -1;

    /* If the following gain control and is non zero - its from error! */
    if (setjmp(_IritPrsrLongJumpBuffer) != 0) {
	IritPrsrCloseStream(Handler, TRUE);
	return;
    }

    Handler = IritPrsrOpenStreamFromFile(f, FALSE, FALSE, FALSE);

    if (f != NULL && f != stdout && f != stderr)
	IRIT_DATA_HEADER(f, "Irit");

    if (_IPStream[Handler].IsBinary)
        IritPrsrPutBinObject(Handler, PObj);
    else
	IritPrsrPutAllObjects(PObj, Handler, 0);

    IritPrsrCloseStream(Handler, TRUE);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into given file designated via M
* Handler.								     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:  A handler to the open stream.				     M
*   PObj:     Object to put on output stream.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* SEE ALSO:                                                                  M
*   IritPrsrSetPrintFunc, IritPrsrSetFloatFormat, IritPrsrPrintFunc          M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPutObjectToHandler, files                                        M
*****************************************************************************/
void IritPrsrPutObjectToHandler(int Handler, IPObjectStruct *PObj)
{
    if (_IPStream[Handler].IsBinary)
        IritPrsrPutBinObject(Handler, PObj);
    else
	IritPrsrPutAllObjects(PObj, Handler, 0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to print out the data from given object.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:      Object to put out.                                            *
*   Handler:   A handler to the open stream.				     *
*   Indent:    Indentation to put object at.                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IritPrsrPutAllObjects(IPObjectStruct *PObj,
				  int Handler,
				  int Indent)
{
    int i, IsRational, NumCoords;
    char Str[LINE_LEN],
	*ErrStr = NULL;
    CagdRType *Coords;
    IPObjectStruct *PObjTmp;
    IPPolygonStruct *PPolygon;
    IPVertexStruct *PVertex;
    IPAttributeStruct
	*Attr = AttrTraceAttributes(PObj -> Attrs, PObj -> Attrs);

    if (Attr) {
	_IPFprintf(Handler, Indent, "[OBJECT ");

	IritPrsrPutAttributes(Handler, Attr, Indent);

	_IPFprintf(Handler, 0,
		 "%s\n", strlen(PObj -> Name) ? PObj -> Name : "NONE");
    }
    else {
	_IPFprintf(Handler, Indent, "[OBJECT %s\n",
		   strlen(PObj -> Name) ? PObj -> Name : "NONE");
    }
    Indent += 4;

    switch (PObj -> ObjType) {
	case IP_OBJ_POLY:
	    for (PPolygon = PObj -> U.Pl;
		 PPolygon != NULL;
		 PPolygon = PPolygon -> Pnext) {
		if (PPolygon -> PVertex == NULL)
		    continue;

		if (IP_IS_POLYLINE_OBJ(PObj))
		    _IPFprintf(Handler, Indent, "[POLYLINE ");
		else if (IP_IS_POINTLIST_OBJ(PObj))
		    _IPFprintf(Handler, Indent, "[POINTLIST ");
		else {
		    /* Make sure it has a valid plane normal. */
		    if (PT_APX_EQ_ZERO_EPS(PPolygon -> Plane, IRIT_UEPS))
		        if (!IritPrsrUpdatePolyPlane(PPolygon))
			    continue;

		    _IPFprintf(Handler,
			       Indent, "[POLYGON [PLANE %s %s %s %s] ",
			       _IPReal2Str(PPolygon -> Plane[0]),
			       _IPReal2Str(PPolygon -> Plane[1]),
			       _IPReal2Str(PPolygon -> Plane[2]),
			       _IPReal2Str(PPolygon -> Plane[3]));
		}
		IritPrsrPutAttributes(Handler, PPolygon -> Attrs, Indent);

		for (PVertex = PPolygon -> PVertex -> Pnext, i = 1;
		     PVertex != PPolygon -> PVertex && PVertex != NULL;
		     PVertex = PVertex -> Pnext, i++);
		_IPFprintf(Handler, Indent + 4, "%d\n", i);

		PVertex = PPolygon -> PVertex;
		do {		     /* Assume at least one edge in polygon! */
		    _IPFprintf(Handler, Indent + 4, "[");
		    
		    IritPrsrPutAttributes(Handler, PVertex -> Attrs, Indent);

		    if (IP_IS_POLYLINE_OBJ(PObj) ||
			(IP_IS_POLYGON_OBJ(PObj) &&
			 (PT_APX_EQ(PPolygon -> Plane, PVertex -> Normal) ||
			  PT_APX_EQ_ZERO_EPS(PVertex -> Normal, IRIT_UEPS))))
			_IPFprintf(Handler, 0, "%s%s %s %s]\n",
			    IP_IS_INTERNAL_VRTX(PVertex) ? "[INTERNAL] " : "",
			    _IPReal2Str(PVertex -> Coord[0]),
			    _IPReal2Str(PVertex -> Coord[1]),
			    _IPReal2Str(PVertex -> Coord[2]));
		    else if (IP_IS_POINTLIST_OBJ(PObj))
			_IPFprintf(Handler, 0, "%s %s %s]\n",
			    _IPReal2Str(PVertex -> Coord[0]),
			    _IPReal2Str(PVertex -> Coord[1]),
			    _IPReal2Str(PVertex -> Coord[2]));
		    else
			_IPFprintf(Handler, 0,
			    "%s[NORMAL %s %s %s] %s %s %s]\n",
			    IP_IS_INTERNAL_VRTX(PVertex) ? "[INTERNAL] " : "",
			    _IPReal2Str(PVertex -> Normal[0]),
			    _IPReal2Str(PVertex -> Normal[1]),
			    _IPReal2Str(PVertex -> Normal[2]),
			    _IPReal2Str(PVertex -> Coord[0]),
			    _IPReal2Str(PVertex -> Coord[1]),
			    _IPReal2Str(PVertex -> Coord[2]));

		    PVertex = PVertex -> Pnext;
		}
		while (PVertex != PPolygon -> PVertex && PVertex != NULL);
		_IPFprintf(Handler, Indent, "]\n");    /* Close the polygon. */
	    }
	    break;
	case IP_OBJ_NUMERIC:
	    _IPFprintf(Handler, Indent, "[NUMBER %s]\n",
		       _IPReal2Str(PObj -> U.R));
	    break;
	case IP_OBJ_POINT:
	    _IPFprintf(Handler, Indent, "[POINT %s %s %s]\n",
		     _IPReal2Str(PObj -> U.Pt[0]),
		     _IPReal2Str(PObj -> U.Pt[1]),
		     _IPReal2Str(PObj -> U.Pt[2]));
	    break;
	case IP_OBJ_VECTOR:
	    _IPFprintf(Handler, Indent, "[VECTOR %s %s %s]\n",
		     _IPReal2Str(PObj -> U.Vec[0]),
		     _IPReal2Str(PObj -> U.Vec[1]),
		     _IPReal2Str(PObj -> U.Vec[2]));
	    break;
	case IP_OBJ_PLANE:
	    _IPFprintf(Handler, Indent, "[PLANE %s %s %s %s]\n",
		     _IPReal2Str(PObj -> U.Plane[0]),
		     _IPReal2Str(PObj -> U.Plane[1]),
		     _IPReal2Str(PObj -> U.Plane[2]),
		     _IPReal2Str(PObj -> U.Plane[3]));
	    break;
	case IP_OBJ_CTLPT:
	    Coords = PObj -> U.CtlPt.Coords;
	    IsRational = CAGD_IS_RATIONAL_PT(PObj -> U.CtlPt.PtType);
	    NumCoords = CAGD_NUM_OF_PT_COORD(PObj -> U.CtlPt.PtType);

	    sprintf(Str, "[CTLPT %c%d %s", IsRational ? 'P' : 'E', NumCoords,
		    IsRational ? _IPReal2Str(Coords[0]) : "");
	    
	    for (i = 1; i <= NumCoords; i++) {
		strcat(Str, " ");
	        strcat(Str, _IPReal2Str(Coords[i]));
	    }
	    strcat(Str,"]\n");
	    _IPFprintf(Handler, Indent, Str);
	    break;
	case IP_OBJ_MATRIX:
	    _IPFprintf(Handler, Indent, "[MATRIX\n");
	    for (i = 0; i < 4; i++)
		_IPFprintf(Handler, Indent + 8, "%s %s %s %s%s\n",
			 _IPReal2Str((*PObj -> U.Mat)[i][0]),
			 _IPReal2Str((*PObj -> U.Mat)[i][1]),
			 _IPReal2Str((*PObj -> U.Mat)[i][2]),
			 _IPReal2Str((*PObj -> U.Mat)[i][3]),
			 i == 3 ? "]" : "");
	    break;
	case IP_OBJ_INSTANCE:
	    _IPFprintf(Handler, Indent, "[INSTANCE %s\n",
		       PObj -> U.Instance -> Name);
	    for (i = 0; i < 4; i++)
		_IPFprintf(Handler, Indent + 8, "%s %s %s %s%s\n",
			 _IPReal2Str(PObj -> U.Instance -> Mat[i][0]),
			 _IPReal2Str(PObj -> U.Instance -> Mat[i][1]),
			 _IPReal2Str(PObj -> U.Instance -> Mat[i][2]),
			 _IPReal2Str(PObj -> U.Instance -> Mat[i][3]),
			 i == 3 ? "]" : "");
	    break;
	case IP_OBJ_STRING:
	    _IPFprintf(Handler, Indent, "[STRING \"%s\"]\n", PObj -> U.Str);
	    break;
	case IP_OBJ_LIST_OBJ:
	    for (i = 0; (PObjTmp = ListObjectGet(PObj, i)) != NULL; i++) {
		if (PObjTmp == PObj)
		    IritPrsrFatalError("A list containing itself detected.\n");
		else
		    IritPrsrPutAllObjects(PObjTmp, Handler, Indent);
	    }
	    break;
	case IP_OBJ_CURVE:
    	    CagdCrvWriteToFile2(PObj -> U.Crvs, Handler,
				Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_SURFACE:
	    CagdSrfWriteToFile2(PObj -> U.Srfs, Handler,
				Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_TRIMSRF:
	    TrimWriteTrimmedSrfToFile2(PObj -> U.TrimSrfs, Handler,
				       Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_TRIM_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_TRIVAR:
	    TrivTVWriteToFile2(PObj -> U.Trivars, Handler,
			       Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_TRIV_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_TRISRF:
	    TrngTriSrfWriteToFile2(PObj -> U.TriSrfs, Handler,
				   Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_TRNG_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_MODEL:
	    MdlWriteModelToFile2(PObj -> U.Mdls, Handler,
				 Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_TRNG_LIB_ERR, ErrStr);
	    break;
	default:
	    IritPrsrFatalError("Attemp to print undefine object type.");
	    break;
    }

    Indent -= 4;
    _IPFprintf(Handler, Indent, "]\n");			/* Close the object. */
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Same as fprintf but with indentation.					     *
*   See function IritPrsrSetPrintFunc, IritPrsrSetFloatFormat		     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:    A handler to the open stream.				     *
*   Indent:     All printing will start at this column.                      *
*   va_alist:   Do "man stdarg"                                              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
#ifdef USE_VARARGS
void _IPFprintf(int Handler, int Indent, char *va_alist, ...)
{
    char *Format, Line[LINE_LEN_LONG];
    int i;
    va_list ArgPtr;

    va_start(ArgPtr);
    Format = va_arg(ArgPtr, char *);
#else
void _IPFprintf(int Handler, int Indent, char *Format, ...)
{
    char Line[LINE_LEN_LONG];
    int i;
    va_list ArgPtr;

    va_start(ArgPtr, Format);
#endif /* USE_VARARGS */

    if (IritPrsrPrintFunc != NULL || _IPStream[Handler].f != NULL) {
	for (i = 0; Indent >= 8; i++, Indent -= 8)
	    Line[i] = '\t';
	while (Indent--)
	    Line[i++] = ' ';
	vsprintf(&Line[i], Format, ArgPtr);

	if (_IPStream[Handler].f != NULL)
	    fprintf(_IPStream[Handler].f, Line);
	else
	    IritPrsrPrintFunc(Line);
    }
    else {     /* _IPStream[Handler].f == NULL and it is a socket connction. */
	/* No need for indentation if writing to a socket. */
	vsprintf(Line, Format, ArgPtr);
	SocWriteLine(Handler, Line, strlen(Line));
    }

    va_end(ArgPtr);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to print the attributes of given attribute list.		     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:    A handler to the open stream.				     *
*   Attr:     Attributes to put out.                                         *
*   Indent:   Indentation to put attributes at.                              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IritPrsrPutAttributes(int Handler,
				  IPAttributeStruct *Attr,
				  int Indent)
{
    int Count = 0;

    Attr = AttrTraceAttributes(Attr, Attr);

    while (Attr) {
	if (Attr -> Type == IP_ATTR_OBJ) {
	    _IPFprintf(Handler, 0, "\n");
	    _IPFprintf(Handler, Indent + 4, "[%s\n", Attr -> Name);
	    IritPrsrPutAllObjects(Attr -> U.PObj, Handler, Indent + 8);
	    _IPFprintf(Handler, Indent + 4, "]\n");
	    _IPFprintf(Handler, Indent + 4, "");
	}
	else 
	    _IPFprintf(Handler, 0, "%s ", Attr2String(Attr, TRUE));

	Attr = AttrTraceAttributes(Attr, NULL);

	if (++Count >= 2 && Attr != NULL) {
	    /* Allow two attributes at most per line. */
	    _IPFprintf(Handler, 0, "\n");
	    _IPFprintf(Handler, Indent + 4, "");
	    Count = 0;
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Convert a real number into a string.					     *
*   The routine maintains six different buffers simultanuously so up to six  *
* consecutive calls can be issued from same printf and stiil have valid      *
* strings.								     *
*                                                                            *
* PARAMETERS:                                                                *
*   R:        A  real number to convert to a string.                         *
*                                                                            *
* RETURN VALUE:                                                              *
*   char *:   A string representing R.                                       *
*****************************************************************************/
char *_IPReal2Str(RealType R)
{
    static int j, k,
	i = 0;
    static char Buffer[6][LINE_LEN_SHORT];

    if (ABS(R) < ZERO_NUM_IRIT_EPS)
	R = 0.0;	 		   /* Round off very small numbers. */

    sprintf(Buffer[i], _IPGlblFloatFormat, R);

    for (k = 0; !isdigit(Buffer[i][k]) && k < LINE_LEN; k++);
    if (k >= LINE_LEN) {
	fprintf(stderr, "Warning: Conversion of real number (%f) failed, zero coerced.\n", R);
	R = 0.0;
    }

    for (j = strlen(Buffer[i]) - 1; Buffer[i][j] == ' ' && j > k; j--);
    if (strchr(Buffer[i], '.') != NULL &&
	strchr(Buffer[i], 'e') == NULL &&
	strchr(Buffer[i], 'E') == NULL)
	for (; Buffer[i][j] == '0' && j > k; j--);
    Buffer[i][j + 1] = 0;

    j = i;
    i = (i + 1) % 6;
    return Buffer[j];
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to last vertex of a list.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   VList:    A list of vertices                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:    Last vertex in VList.                               M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetLastVrtx, linked lists, last element                          M
*****************************************************************************/
IPVertexStruct *IritPrsrGetLastVrtx(IPVertexStruct *VList)
{
    return IritPrsrGetPrevVrtx(VList, NULL);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to previous vertex in VList to V.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   VList:    A list of vertices.                                            M
*   V:        For which the previous vertex in VList is pursuit.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:   Previous vertex to V in VList if found, NULL         M
*                       otherwise.                                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetPrevVrtx, previous element, linked lists                      M
*****************************************************************************/
IPVertexStruct *IritPrsrGetPrevVrtx(IPVertexStruct *VList, IPVertexStruct *V)
{
    IPVertexStruct
	*VHead = VList;

    if (VList == NULL || VList == V)
	return NULL;

    for ( ;
	  VList != NULL && VList -> Pnext != V && VList -> Pnext != VHead;
	  VList = VList -> Pnext);

    return VList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Appends two vertex lists together.         				     M
*                                                                            *
* PARAMETERS:                                                                M
*   VList1, VList2:  Two lists to append.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:    Appended list.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrAppendVrtxLists, linked lists                                    M
*****************************************************************************/
IPVertexStruct *IritPrsrAppendVrtxLists(IPVertexStruct *VList1,
					IPVertexStruct *VList2)
{
    if (VList1 == NULL)
        return VList2;
    else if (VList2 == NULL)
        return VList1;
    else {
	IPVertexStruct
	    *VLast = IritPrsrGetLastVrtx(VList1);

	VLast -> Pnext = VList2;

	return VList1;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to last polygon/line of a list.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PList:    A list of polygons.                                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Last polygon in list PList.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetLastPoly, linked lists, last element                          M
*****************************************************************************/
IPPolygonStruct *IritPrsrGetLastPoly(IPPolygonStruct *PList)
{
    return IritPrsrGetPrevPoly(PList, NULL);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to previous polygon in PList to P.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PList:    A list of polygons.                                            M
*   P:        For which the previous polygon in PList is pursuit.            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:   Previous polygon to P in PList if found, NULL        M
*                       otherwise.                                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetPrevPoly, previous element, linked lists                      M
*****************************************************************************/
IPPolygonStruct *IritPrsrGetPrevPoly(IPPolygonStruct *PList,
				     IPPolygonStruct *P)
{
    if (PList == NULL || PList == P)
	return NULL;

    for ( ; PList != NULL && PList -> Pnext != P; PList = PList -> Pnext);

    return PList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Appends two poly lists together.         				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PList1, PList2:  Two lists to append.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Appended list.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrAppendPolyLists, linked lists                                    M
*****************************************************************************/
IPPolygonStruct *IritPrsrAppendPolyLists(IPPolygonStruct *PList1,
					 IPPolygonStruct *PList2)
{
    if (PList1 == NULL)
        return PList2;
    else if (PList2 == NULL)
        return PList1;
    else {
	IPPolygonStruct
	    *PLast = IritPrsrGetLastPoly(PList1);

	PLast -> Pnext = PList2;

	return PList1;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to last object of a list.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   OList:    A list of objects.                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Last object in list OList.                         M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetLastObj, linked lists, last element                           M
*****************************************************************************/
IPObjectStruct *IritPrsrGetLastObj(IPObjectStruct *OList)
{
    return IritPrsrGetPrevObj(OList, NULL);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to previous object in OList to O.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   OList:    A list of objects.                                             M
*   O:        For which the previous object in OList is pursuit.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:   Previous object to O in OList if found, NULL         M
*                       otherwise.                                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetPrevObj, previous element, linked lists                       M
*****************************************************************************/
IPObjectStruct *IritPrsrGetPrevObj(IPObjectStruct *OList, IPObjectStruct *O)
{
    if (OList == NULL || OList == O)
	return NULL;

    for ( ; OList != NULL && OList -> Pnext != O; OList = OList -> Pnext);
	if (OList == NULL)
	    return NULL;

    return OList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Appends two object lists together.          				     M
*                                                                            *
* PARAMETERS:                                                                M
*   OList1, OList2:  Two lists to append.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Appended list.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrAppendObjLists, linked lists                                     M
*****************************************************************************/
IPObjectStruct *IritPrsrAppendObjLists(IPObjectStruct *OList1,
				       IPObjectStruct *OList2)
{
    if (OList1 == NULL)
        return OList2;
    else if (OList2 == NULL)
        return OList1;
    else {
	IPObjectStruct
	    *OLast = IritPrsrGetLastObj(OList1);

	OLast -> Pnext = OList2;

	return OList1;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Appends two lists.                                                         M
*                                                                            *
* PARAMETERS:                                                                M
*   ListObj1, ListObj2:  The two list objects to append.                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    A combined list.                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrAppendLists                                                      M
*****************************************************************************/
IPObjectStruct *IritPrsrAppendLists(IPObjectStruct *ListObj1,
				    IPObjectStruct *ListObj2)
{
    int i, j;
    IPObjectStruct *PObj, *PObjTmp;

    if (!IP_IS_OLST_OBJ(ListObj1) && !IP_IS_OLST_OBJ(ListObj2)) {
	IritPrsrFatalError("None list object ignored.");
	return NULL;
    }

    PObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (i = 0; (PObjTmp = ListObjectGet(ListObj1, i)) != NULL; i++) {
	ListObjectInsert(PObj, i, PObjTmp);
    }

    for (j = 0; (PObjTmp = ListObjectGet(ListObj2, j)) != NULL; i++, j++) {
	ListObjectInsert(PObj, i, PObjTmp);
    }

    ListObjectInsert(PObj, i, NULL);

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reverses a list.                                                           M
*                                                                            *
* PARAMETERS:                                                                M
*   ListObj:    List object to reverses its entries.                         M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Reversed list object.                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrReverseListObj                                                   M
*****************************************************************************/
IPObjectStruct *IritPrsrReverseListObj(IPObjectStruct *ListObj)
{
    int i, j;
    IPObjectStruct *PObj, *PObjTmp;

    if (!IP_IS_OLST_OBJ(ListObj)) {
	IritPrsrFatalError("None list object ignored.");
	return NULL;
    }

    PObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (i = ListObjectLength(ListObj) - 1, j = 0; i >= 0; i--) {
	PObjTmp = ListObjectGet(ListObj, i);
	ListObjectInsert(PObj, j++, PObjTmp);
    }

    ListObjectInsert(PObj, j, NULL);

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns the length of a list of vertices.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   V:        Vertex list to compute its length.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Number of elements in V list.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrVrtxListLen, length, linked lists                                M
*****************************************************************************/
int IritPrsrVrtxListLen(IPVertexStruct *V)
{
    int i = 0;
    IPVertexStruct
	*VHead = V;

    if (V == NULL)
	return 0;

    do {
	i++;
	V = V -> Pnext;
    }
    while (V != NULL && V != VHead);

    return i;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns the length of a list of polygons. 				     M
*                                                                            *
* PARAMETERS:                                                                M
*   P:        Polygon list to compute its length.                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Number of elements in P list.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPolyListLen, length, linked lists                                M
*****************************************************************************/
int IritPrsrPolyListLen(IPPolygonStruct *P)
{
    int i;

    for (i = 0; P != NULL; i++, P = P -> Pnext);

    return i;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns the length of a list of objects. 				     M
*                                                                            *
* PARAMETERS:                                                                M
*   O:        Object list to compute its length.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Number of elements in O list.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrObjListLen, length, linked lists                                 M
*****************************************************************************/
int IritPrsrObjListLen(IPObjectStruct *O)
{
    int i;

    for (i = 0; O != NULL; i++, O = O -> Pnext);

    return i;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Iterates on Irit object list and calls CallBack function on every        M
*   polygon found, passing it a pointer to the polygon object.               M
*                                                                            *
* PARAMETERS:                                                                M
*   OList:    Pointer to the Irit objects' linked list.	                     M
*   CallBack: Callback function.                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrForEachPoly                                                      M
*****************************************************************************/
void IritPrsrForEachPoly(IPObjectStruct *OList,
			 void (*CallBack) (IPPolygonStruct *))
{
    IPPolygonStruct *Poly;

    for ( ; OList != NULL; OList = OList -> Pnext)
        if (IP_IS_POLY_OBJ(OList))
            for (Poly = OList -> U.Pl; Poly != NULL; Poly = Poly -> Pnext)
                CallBack(Poly);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Iterates on Irit object list and for each vertex in every polygon calls  M
*   CallBack function passing it a pointer to the vertex object.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   OList:    Pointer to Irit objects' linked list.		             M
*   CallBack: Callback function.                   	                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrForEachVertex                                                    M
*****************************************************************************/
void IritPrsrForEachVertex(IPObjectStruct *OList,
			   void (*CallBack) (IPVertexStruct *))
{
    IPPolygonStruct *Poly;
    IPVertexStruct *Vertex;

    for ( ; OList != NULL; OList = OList -> Pnext)
        if (IP_IS_POLY_OBJ(OList))
            for (Poly = OList -> U.Pl; Poly != NULL; Poly = Poly -> Pnext)
                for (Vertex = Poly -> PVertex;
		     Vertex != NULL;
		     Vertex = Vertex -> Pnext)
                    CallBack(Vertex);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the printing function to call if needs to redirect printing.          M
*                                                                            *
* PARAMETERS:                                                                M
*   PrintFunc:   A function that gets a single string it should print.       M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetPrintFunc, files                                              M
*****************************************************************************/
void IritPrsrSetPrintFunc(IritPrsrPrintFuncType PrintFunc)
{
    IritPrsrPrintFunc = PrintFunc;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the floating point printing format.                                   M
*                                                                            *
* PARAMETERS:                                                                M
*   FloatFormat:    A printf style floating point printing format string.    M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetFloatFormat, files                                            M
*****************************************************************************/
void IritPrsrSetFloatFormat(char *FloatFormat)
{
    char Str[LINE_LEN];

    /* Not a full-proof test but something. */
    if (strlen(FloatFormat) >= 2 &&
	strchr(FloatFormat, '%') != NULL &&
	(strchr(FloatFormat, 'e') != NULL ||
	 strchr(FloatFormat, 'f') != NULL ||
	 strchr(FloatFormat, 'g') != NULL ||
	 strchr(FloatFormat, 'E') != NULL ||
	 strchr(FloatFormat, 'F') != NULL ||
	 strchr(FloatFormat, 'G') != NULL)) {
	_IPGlblFloatFormat = FloatFormat;
    }
    else {
	sprintf(Str, "Illegal floating point format \"%s\".", FloatFormat);
	IritPrsrFatalError(Str);
    }	
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Searchs for an onbject in given PObjList, named Name.                    M
*                                                                            *
* PARAMETERS:                                                                M
*   Name:      Of object to find and return a reference of.                  M
*   PObjList:  List of objects to scan.                                      M
*   TopLevel:  if TRUE, scan only the top level list.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    A reference to found object, NULL otherwise.        M
*                                                                            *
* KEYWORDS:                                                                  M
*   IPGetObjectByName                                                        M
*****************************************************************************/
IPObjectStruct *IPGetObjectByName(char *Name,
				  IPObjectStruct *PObjList,
				  int TopLevel)
{
    int i = 0;
    IPObjectStruct *PObj;

    for (PObj = PObjList; PObj != NULL; PObj = PObj -> Pnext) {
	if (TopLevel) {
	    if (stricmp(Name, PObj -> Name) == 0)
	        return PObj;
	}
	else {
	    IPObjectStruct
	        *PNamedObj = IPGetObjectByNameAux(Name, PObj);

	    if (PNamedObj != NULL)
	        return PNamedObj;
	}

	if (i++ >= MAX_OBJECTS)
	    IritPrsrFatalError("GetObjectByName: Object list too large");
    }

    return NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Auxiliary function of IPGetObjectByName.				     *
*****************************************************************************/
static IPObjectStruct *IPGetObjectByNameAux(char *Name, IPObjectStruct *PObj)
{
    if (stricmp(Name, PObj -> Name) == 0)
        return PObj;

    if (IP_IS_OLST_OBJ(PObj)) {
	IPObjectStruct *PTmp;
	int j = 0;

	/* Search in its list. */
	while ((PTmp = ListObjectGet(PObj, j++)) != NULL) {
	    IPObjectStruct
	        *PNamedObj = IPGetObjectByNameAux(Name, PTmp);

	    if (PNamedObj != NULL)
	        return PNamedObj;
	}
    }

    return NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Traverses a hierarchy of objects and invokes ApplyObject to each leaf    M
* (non list) object in the given object list with an associated matrix.      M
*   Instances are converted into their real objects on the fly. Objects that M
* have an "Invisible" attribute are ignored (they might be invoked           M
* indirectly as an instance).						     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjList:     To traverse and apply.                                     M
*   CrntViewMat:  Viewing matrix.                                            M
*   ApplyFunc:    To invoke on each and every non leaf object.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IPTraverseObjListHierarchy, instances                                    M
*****************************************************************************/
void IPTraverseObjListHierarchy(IPObjectStruct *PObjList,
				MatrixType CrntViewMat,
				IritPrsrApplyObjFuncType ApplyFunc)
{
    IPObjectStruct *PObj;

    for (PObj = PObjList; PObj != NULL; PObj = PObj -> Pnext)
	IPTraverseObjListHierarchyAux(PObj, PObjList, ApplyFunc,
				      CrntViewMat, FALSE);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Auxiliary function of IPTraverseObjListHierarchy                         *
*   Instances are converted into their real objects on the fly. Objects that *
* have an "Invisible" attribute are ignored (they might be invoked           *
* indirectly as an instance).						     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:       To traverse and apply.                                       *
*   PObjList:   Entire list of objects to tranverse.                         *
*   ApplyFunc:  To invoke on each and every non leaf object.                 *
*   Mat:	Local transformation matrix of current instance.             *
*   IsInstance: If TRUE, we were invoked via an instance reference.          *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IPTraverseObjListHierarchyAux(IPObjectStruct *PObj,
					  IPObjectStruct *PObjList,
					  IritPrsrApplyObjFuncType ApplyFunc,
					  MatrixType Mat,
					  int IsInstance)
{
    if (AttrGetObjectIntAttrib(PObj, "WasInstance") == TRUE) {
	/* This object originated from an instance.  Make it look like one. */
	IsInstance = TRUE;
    }

    if (!IsInstance && ATTR_OBJ_IS_INVISIBLE(PObj))
	return;

    if (IP_IS_OLST_OBJ(PObj)) {
	int i = 0;
	IPObjectStruct *PTmp;

	while ((PTmp = ListObjectGet(PObj, i++)) != NULL) {
	    IPTraverseObjListHierarchyAux(PTmp, PObjList,
					  ApplyFunc, Mat, IsInstance);
	}
    }
    else {
	/* It is a leaf node. */
	int Visible = TRUE;
	MatrixType NewMat;
	IPObjectStruct *MatObj;

	if ((MatObj = AttrGetObjectObjAttrib(PObj, "_animation_mat")) != NULL &&
	    IP_IS_MAT_OBJ(MatObj)) {
	    if ((Visible = AttrGetObjectIntAttrib(PObj, "_isvisible")) != FALSE) {
		MatMultTwo4by4(NewMat, *MatObj -> U.Mat, Mat);
	    }
	}
	else
	    GEN_COPY(NewMat, Mat, sizeof(MatrixType));

	if (Visible) {
	    if (IP_IS_INSTNC_OBJ(PObj)) {
		MatrixType InstMat;
		IPObjectStruct *PTmp;

		if ((PTmp = IPGetObjectByName(PObj -> U.Instance -> Name,
					      PObjList, FALSE)) == NULL) {
		    fprintf(stderr, "Failed to find instance's origin \"%s\"\n",
			    PObj -> U.Instance -> Name);
		    return;
		}
		MatMultTwo4by4(InstMat, PObj -> U.Instance -> Mat, NewMat);

		IPTraverseObjListHierarchyAux(PTmp, PObjList,
					      ApplyFunc, InstMat, TRUE);
	    }
	    else {
		IPObjectStruct
		    *PObjNext = PObj -> Pnext;

		PObj -> Pnext = NULL;    /* Isolate object before ApplyFunc. */

		/* Its a leaf in the hierarchy - apply it now. */
		ApplyFunc(PObj, NewMat);

		PObj -> Pnext = PObjNext;
	    }
	}
    }
}
