/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
* Data File Interpreter module - handle data files - to/from geom objects    *
*****************************************************************************/

#ifdef __MSDOS__
#include <graphics.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include "program.h"
#include "allocatg.h"
#include "objects.h"
#include "geomat3d.h"
#include "dataprsl.h"
#include "dataprsg.h"			    /* Visible to the other modules. */

#ifndef __MSDOS__
#include "xgraphic.h"
#endif /* __MSDOS__ */

static int  DPGlblLineCount,	     /* Used to locate errors in input file. */
	    DPGlblParserError;		    /* Save last error number found. */
static char DPGlblTokenError[LINE_LEN]; /* Last token where error was found. */
static jmp_buf LclLongJumpBuffer;		   /* Used in error traping. */
/* The following are used to hold binary trees of Vertices & Polygon/lines.  */
static struct BinTree *VertexRoot = NULL, *PolygonRoot = NULL;

static int  GlblToken =	0;	      /* Used by the parser, to unget token. */
static char GlblStringToken[UNGET_STACK_SIZE][LINE_LEN];/* Save unget tokens.*/

/*****************************************************************************
* Routine to print the data from given object into given file FileName.	     *
*****************************************************************************/
void DataPrsrPutObject(char *FileName, ObjectStruct *PObj)
{
    char *FileType = NULL, *Pchar;
    char FullFileName[LINE_LEN];
    FILE *f = NULL;

    DPGlblParserError = 0;
    VertexRoot = PolygonRoot = NULL;

    /* If the following gain control and is non zero - its from error! */
    if (setjmp(LclLongJumpBuffer) != 0) {
	if (f != NULL) fclose(f);
	return;
    }

    switch (PObj -> ObjType) {
	case GEOMETRIC_OBJ:
	    FileType = ".ply";
	    break;
	case MATRIX_OBJ:
	    FileType = ".mat";
	    break;
	default:
	    ParserError(DP_ERR_OnlyMatGeomObj, "");
    }

    if ((Pchar = strchr(FileName, '.')) != (char *) NULL)
	*Pchar = NULL;			 /* Make sure no file type is given. */
    if (strlen(FileName) == 0) ParserError(DP_ERR_EmptyName, "");

    strcpy(FullFileName, FileName);
    strcat(FullFileName, FileType);

    if ((f = fopen(FullFileName, "w")) == NULL)
	ParserError(DP_ERR_OpenFail, FullFileName);

    switch (PObj -> ObjType) {
	case GEOMETRIC_OBJ:
	    DataPrsrPutGeomObject(f, PObj);
	    break;
	case MATRIX_OBJ:
	    DataPrsrPutMatObject(f, PObj);
	    break;
    }

    fclose(f);
    return;
}

/*****************************************************************************
* Routine to print the data from given geometric object into given file f    *
*****************************************************************************/
static void DataPrsrPutGeomObject(FILE *f, ObjectStruct *PObj)
{
    int i, CountVertices = 0, BaseVertices, CountPolys = 0;
    char *s;
    struct PolygonStruct *PPoly = PObj -> U.Pl;
    struct VertexStruct *PVertex, *PVFirst;

    while (PPoly) {
	PVFirst = PVertex = PPoly -> V;
	BaseVertices = CountVertices;	   /* To know index of first vertex. */
	if (PVertex == NULL)
	    FatalError("DataPrsrPutGeomObject: Attemp to dump empty polygon, exit\n");

	do {			      /* Assume at list one edge in polygon! */
	    if (IS_INTERNAL_EDGE(PVertex)) s = "[INTERNAL] ";
	    else s = "";

	    /* Make real zero, if number is smaller than EPSILON: */
	    for (i=0; i<3; i++) if (ABS(PVertex -> Pt[i]) < EPSILON)
		PVertex -> Pt[i] = 0.0;
#	    ifdef DOUBLE
		fprintf(f, "[VERTEX V%-3d %s%10lg %10lg %10lg]\n",
#	    else
		fprintf(f, "[VERTEX V%-3d %s%10g %10g %10g]\n",
#	    endif /* DOUBLE */
		CountVertices++, s,
		PVertex -> Pt[0], PVertex -> Pt[1], PVertex -> Pt[2]);
	    PVertex = PVertex -> Pnext;
	}
	while (PVertex != PVFirst && PVertex != NULL);

        if (IS_POLYLINE_GEOM_OBJ(PObj))
	     fprintf(f, "[POLYLINE P%-3d", CountPolys++);
	else {
	    for (i=0; i<4; i++) if (ABS(PPoly -> Plane[i]) < EPSILON)
		PPoly -> Plane[i] = 0.0;
#	    ifdef DOUBLE
		fprintf(f, "[POLYGON P%-3d [PLANE %10lg %10lg %10lg %10lg]",
#	    else
		fprintf(f, "[POLYGON P%-3d [PLANE %10g %10g %10g %10lg]",
#	    endif /* DOUBLE */
		CountPolys++, PPoly -> Plane[0], PPoly -> Plane[1],
			      PPoly -> Plane[2], PPoly -> Plane[3]);
	}

	for (i=BaseVertices; i<CountVertices; i++) {
	    if ((i-BaseVertices) % 10 == 0) fprintf(f, "\n\t");
	    fprintf(f, "V%-3d ", i);
	}
	fprintf(f, "]\n\n");

	PPoly = PPoly -> Pnext;
    }
    fprintf(f, "[OBJECT %s [COLOR %d]", PObj -> Name, GET_OBJECT_COLOR(PObj));
    for (i=0; i<CountPolys; i++) {
	if (i % 10 == 0) fprintf(f, "\n\t");
	fprintf(f, "P%-4d", i);
    }
    fprintf(f, "]\n");
}

/*****************************************************************************
* Routine to print the data from given matrix object into given file f	     *
*****************************************************************************/
static void DataPrsrPutMatObject(FILE *f, ObjectStruct *PObj)
{
    int i, j;

    for (i=0; i<4; i++) {
	for (j=0; j<4; j++)
#	    ifdef DOUBLE
		fprintf(f, "%17.8le ",
#	    else
		fprintf(f, "%17.8e ",
#	    endif /* DOUBLE */
			PObj -> U.Mat[i][j]);
	fprintf(f, "\n");
    }
    fprintf(f, "MATRIX %s\n", PObj -> Name);
}

/*****************************************************************************
* Routine to read the data from	a given	file and scans for the Geometric     *
* object named ObjName. Returns a pointer to the data structure represent    *
* that geometic object. If ObjName is NULL, returns first object in file.    *
*****************************************************************************/
ObjectStruct *DataPrsrGetObject(char *FileName, char *ObjName)
{
    int	i, ObjType;
    char FullFileName[LINE_LEN];
    FILE *f = NULL;
    ObjectStruct *PObj;

    DPGlblParserError = 0;
    VertexRoot = PolygonRoot = NULL;

    /* If the following gain control and is non zero - its from error! */
    if (setjmp(LclLongJumpBuffer) != 0) {
	if (f != NULL) fclose(f);
	return NULL;
    }

    if (strlen(FileName) == 0) ParserError(DP_ERR_EmptyName, "");

    strcpy(FullFileName, FileName);
    if (strchr(FullFileName, '.') == NULL) strcat(FullFileName, ".ply");

    i = strlen(FullFileName) - 4;
    if (stricmp(&FullFileName[i], ".ply") == 0) ObjType = GEOMETRIC_OBJ;
    else
    if (stricmp(&FullFileName[i], ".mat") == 0) ObjType = MATRIX_OBJ;
    else
    ParserError(DP_ERR_WrongFileType, FullFileName);

    if ((f = fopen(FullFileName, "r")) == NULL)
	ParserError(DP_ERR_OpenFail, FullFileName);

    switch (ObjType) {
	case GEOMETRIC_OBJ:
	    PObj = DataPrsrGetGeomObject(f, ObjName);
	    break;
	case MATRIX_OBJ:
	    PObj = DataPrsrGetMatObject(f);
	    break;
    }

    fclose(f);
    return PObj;
}

/*****************************************************************************
* Routine to read the data from	a given	file and scans for the Geometric     *
* object named ObjName. Returns a pointer to the data structure represent    *
* that geometic object. If ObjName is NULL, returns first object in file.    *
*****************************************************************************/
static ObjectStruct *DataPrsrGetGeomObject(FILE *f, char *ObjName)
{
    int i, PlaneDefined, PolyType = 0, Color;
    char StringToken[LINE_LEN], CrntObjName[OBJ_NAME_LEN];
    struct BinTree *PBinTree;
    struct VertexStruct	   *PVertex;
    struct PolygonStruct   *PPolygon;
    struct ObjectStruct	   *PObject;

    VertexRoot = PolygonRoot = NULL;		  /* Start from empty trees. */
    GlblToken =	0;
    DPGlblLineCount = 1;			      /* Reset line counter. */

    EliminateComments(f);		 /* Skip all comments including '['. */

    /* Set the long jump address to trap errors, and start the parser: */
    while (!feof(f)) {
	 switch	(GetToken(f, StringToken)) {
	     case TKN_VERTEX:
		  PVertex = AllocVertex(0, 0, NULL, NULL);
		  PBinTree = AllocBinTree("", NULL, NULL, (VoidPtr) PVertex);
		  GetToken(f, PBinTree -> Name);	    /* Get the name. */
		  /* The following handles the optional attr definition:     */
		  if (GetToken(f, StringToken) == TKN_OPEN_PARAN) {
		      GetVertexAttr(f, PVertex);
		  }
		  else {
		      UnGetToken(StringToken);
		  }
		  /* The following handles reading of 3	coord. of vertex. */
		  for (i=0; i<3	;i++) {
		      GetToken(f, StringToken);
#		      ifdef DOUBLE
			  if (sscanf(StringToken, "%lf", &PVertex -> Pt[i]) != 1)
#		      else
			  if (sscanf(StringToken, "%f", &PVertex -> Pt[i]) != 1)
#		      endif /* DOUBLE */
			      ParserError(DP_ERR_NumberExpected, StringToken);
		  }
		  if ((i=GetToken(f, StringToken)) != TKN_CLOSE_PARAN)
			ParserError(DP_ERR_CloseParanExpected, StringToken);
		  InsertBinTree(&VertexRoot, PBinTree);
		  break;
	     case TKN_POLYGON:
		  if (PolyType && PolyType != TKN_POLYGON)
		      ParserError(DP_ERR_MixedPolygonLine, "");
		  else PolyType = TKN_POLYGON;
		  PPolygon = AllocPolygon(NULL, NULL, 0, 0);
		  RST_CONVEX_POLY(PPolygon);	       /* May be not convex! */
		  PBinTree = AllocBinTree("", NULL, NULL, (VoidPtr) PPolygon);
		  GetToken(f, PBinTree -> Name);	    /* Get the name. */
		  /* The following handles the optional attr definition:     */
		  PlaneDefined = FALSE;
		  if (GetToken(f, StringToken) == TKN_OPEN_PARAN) {
		      GetPolyAttr(f, PPolygon, &PlaneDefined);
		  }
		  else {
		      UnGetToken(StringToken);
		  }
		  /* The following handles reading the vertex list */
		  PPolygon -> V = GetVertexList(f, VertexRoot, PolyType);
		  if (!PlaneDefined) DPUpdatePolyPlane(PPolygon);
		  InsertBinTree(&PolygonRoot, PBinTree);
		  break;
	     case TKN_POLYLINE:
		  if (PolyType && PolyType != TKN_POLYLINE)
		      ParserError(DP_ERR_MixedPolygonLine, "");
		  else PolyType = TKN_POLYLINE;
		  PPolygon = AllocPolygon(NULL, NULL, 0, 0);
		  PBinTree = AllocBinTree("", NULL, NULL, (VoidPtr) PPolygon);
		  GetToken(f, PBinTree -> Name);	    /* Get the name. */
		  /* The following handles the optional attr definition:     */
		  PlaneDefined = FALSE;
		  if (GetToken(f, StringToken) == TKN_OPEN_PARAN) {
		      GetPolyAttr(f, PPolygon, &PlaneDefined);
		  }
		  else {
		      UnGetToken(StringToken);
		  }
		  /* The following handles reading the vertex list */
		  PPolygon -> V = GetVertexList(f, VertexRoot, PolyType);
		  if (!PlaneDefined) DPUpdatePolyPlane(PPolygon);
		  InsertBinTree(&PolygonRoot, PBinTree);
		  break;
	     case TKN_OBJECT:
		  PObject = GenGeomObject("", NULL, NULL);
		  GetToken(f, CrntObjName);		    /* Get the name. */
		  Color = LoadColor;
		  if (GetToken(f, StringToken) == TKN_OPEN_PARAN) {
		      GetObjectAttr(f, &Color);
		  }
		  else {
		      UnGetToken(StringToken);
		  }
		  /* The following handles reading the polygon list.	     */
		  /* Note object might be created from Polygons/lines only.  */
		  PObject -> U.Pl = GetPolyList(f, PolygonRoot);
		  if (ObjName == NULL || strlen(ObjName) == 0 ||
		      strcmp(CrntObjName, ObjName) == 0) {
		      /* We got it - free unused data and return it: */
		      DataPrsrFreeAll(VertexRoot, PolygonRoot);
		      if (PolyType == TKN_POLYLINE)
			  SET_POLYLINE_GEOM_OBJ(PObject);/* Mark as polyline.*/
		      SET_OBJECT_COLOR(PObject, Color);
		      return PObject;
		  }
		  else {
		      PObject -> U.Pl = NULL;  /* Dont free the polys twice! */
		      MyFree((VoidPtr) PObject, OBJECT_TYPE);/* Not the one! */
		  }
		  break;
	     default:
		  ParserError(DP_ERR_UndefExprHeader, StringToken);
		  break;
	 } /* of switch	*/
	 if (!feof(f)) EliminateComments(f);/* Skip comments including '['. */
    } /* of while !eof */

    /* If we are here, object was not found in file - return NULL. */
    DataPrsrFreeAll(VertexRoot, PolygonRoot);
    return NULL;
}

/*****************************************************************************
* Routine to read the data from	a given	file and scans for the Matrix        *
* object. The file is assumed to hold 16 (4 by 4 matrix) float numbers.      *
*****************************************************************************/
static ObjectStruct *DataPrsrGetMatObject(FILE *f)
{
    int i, j;
    MatrixType Mat;

    for (i=0; i<4; i++)
	for (j=0; j<4; j++)
	    if (1 !=
#		ifdef DOUBLE
		    fscanf(f, "%lg",
#		else
		    fscanf(f, "%g",
#		endif /* DOUBLE */
			&Mat[i][j]))
		ParserError(DP_ERR_NonMatrixObject, "");

    return GenMatObject("", Mat, NULL);
}

/*****************************************************************************
*   Routine to update the Plane equation of the given polygon by the order   *
* of the first 3 vertices of that polygon.				     *
*****************************************************************************/
static void DPUpdatePolyPlane(PolygonStruct *PPoly)
{
    int i;
    VectorType V1, V2;
    RealType Len;
    struct VertexStruct *V;

    V = PPoly -> V;	PT_SUB(V1, V -> Pt, V -> Pnext -> Pt);
    V = V -> Pnext;	PT_SUB(V2, V -> Pt, V -> Pnext -> Pt);

    VecCrossProd(PPoly -> Plane, V1, V2);	       /* Find Plane Normal. */

    PPoly -> Plane[3] = (-DOT_PROD(PPoly -> Plane, PPoly -> V -> Pt));

    /* Normalize the plane such that the normal has length of 1: */
    Len = PT_LENGTH(PPoly -> Plane);
    for (i=0; i<4; i++) PPoly -> Plane[i] /= Len;
}

/*****************************************************************************
* Allocate one BinTree Structure:					     *
*****************************************************************************/
static struct BinTree * AllocBinTree(char *Name,
				BinTree * Right, BinTree * Left, VoidPtr Ptr)
{
    struct BinTree *p;

    p = (BinTree *) MyMalloc(sizeof(BinTree), OTHER_TYPE);

    strcpy(p -> Name, Name);
    p -> Right = Right;
    p -> Left = Left;
    p -> U.PVoid = Ptr;

    return p;
}

/*****************************************************************************
* Free all the old data saved in the vertices/polygons trees.		     *
* Note that unused vertices/polygons are also freed (tested via their Count) *
*****************************************************************************/
static void DataPrsrFreeAll(BinTree *VertexRoot, BinTree *PolygonRoot)
{
    DataPrsrFreeTree(VertexRoot, VERTEX_TYPE);
    DataPrsrFreeTree(PolygonRoot, POLYGON_TYPE);
}

/*****************************************************************************
* Free all the old data saved in the vertices/polygons trees.		     *
* Note that all vertices/polygons are also freed.			     *
*****************************************************************************/
static void DataPrsrFreeTree(BinTree *Root, int ObjType)
{
    if (Root == NULL) return;

    DataPrsrFreeTree(Root->Right, ObjType);
    DataPrsrFreeTree(Root->Left, ObjType);

    switch (ObjType) {
	case VERTEX_TYPE:
	    MyFree((char *) Root->U.PVertex, VERTEX_TYPE);
	    break;
	case POLYGON_TYPE:
	    MyFree((char *) Root->U.PPolygon, POLYGON_TYPE);
	    break;
    }
}

/*****************************************************************************
*   Routine to unget one token (on stack of UNGET_STACK_SIZE levels!)	     *
*****************************************************************************/
static void UnGetToken(char *StringToken)
{
    if (GlblToken >= UNGET_STACK_SIZE) {
	ParserError(DP_ERR_InternalStackOF, StringToken);
	return;
    }

    strcpy(GlblStringToken[GlblToken], StringToken);
    GlblToken++;  /* GlblToken exists - Something in it (no overflow check). */
}

/*****************************************************************************
*   Routine to get the next token out of the input file	f.		     *
* Returns the next token found,	as StringToken.				     *
* Note:	StringToken must be allocated before calling this routine!	     *
*****************************************************************************/
static void GetStringToken(FILE *f, char *StringToken)
{
    int	len;
    char c, *LocalStringToken;

    if (GlblToken) { /*	get first the unget token */
	GlblToken--;
	strcpy(StringToken, GlblStringToken[GlblToken]);
	return;
    }
    /* skip white spaces: */
    while ((!feof(f))
	 && (((c = getc(f)) == ' ') || (c == '\t') || (c == '\n')))
	    if (c == '\n') DPGlblLineCount++;		 /* Count the lines. */

    LocalStringToken = StringToken;
    if (c == '[')		      /* Its a token by	itself so return it. */
	*LocalStringToken++ = c;	      /* Copy the token	into string. */
    else {
	if (!feof(f))
	     do	*LocalStringToken++ = c;      /* Copy the token	into string. */
	     while ((!feof(f)) &&
		      ((c = getc(f)) !=	' ') &&	(c != '\t') && (c != '\n'));
	if (c == '\n') ungetc(c, f);	 /* Save it to be counted next time. */
    }
    *LocalStringToken =	NULL;					 /* Put	eos. */

    /* The following handles the spacial case were we have XXXX] - we must   */
    /* split it	into two token XXXX and	], UnGetToken(']') and return XXXX:  */
    if ((StringToken[len = strlen(StringToken)-1] == ']') && (len > 0))	{
	/* Return CloseParan */
	UnGetToken(&StringToken[len]);			 /* Save next token. */
	StringToken[len] = NULL;		/* Set end of string on	"]". */
    }
}

/*****************************************************************************
*   Routine to get the next token out of the input file	f as token number.   *
* Returns the next token number	found, with numeric result in NumericToken   *
* if TokenType is TKN_NUMBER.						     *
* Note:	StringToken must be allocated before calling this routine!	     *
*****************************************************************************/
static int GetToken(FILE *f, char *StringToken)
{
    int RetVal;

    GetStringToken(f, StringToken);

    if (feof(f))			     RetVal = TKN_EOF;

    else
    if (!strcmp(StringToken, "["))	     RetVal = TKN_OPEN_PARAN;
    else
    if (!strcmp(StringToken, "]"))	     RetVal = TKN_CLOSE_PARAN;
    else

    if (!strcmp(StringToken, "VERTEX"))	     RetVal = TKN_VERTEX;
    else
    if (!strcmp(StringToken, "POLYGON"))     RetVal = TKN_POLYGON;
    else
    if (!strcmp(StringToken, "POLYLINE"))    RetVal = TKN_POLYLINE;
    else
    if (!strcmp(StringToken, "OBJECT"))	     RetVal = TKN_OBJECT;

    else
    if (!strcmp(StringToken, "PLANE"))	     RetVal = TKN_PLANE;
    else
    if (!strcmp(StringToken, "INTERNAL"))    RetVal = TKN_INTERNAL;
    else
    if (!strcmp(StringToken, "COLOR"))	     RetVal = TKN_COLOR;

    else
    RetVal = TKN_OTHER;				  /* Must be number or name. */

    return RetVal;
}

/*****************************************************************************
* Routine to read the input tokens up to a '[' token - skip comments.	     *
* Note the routine reads the '[' token,	so next	is the expression itself.    *
*****************************************************************************/
static void EliminateComments(FILE *f)
{
    char StringToken[LINE_LEN];

    while ((!feof(f)) && (GetToken(f, StringToken) != TKN_OPEN_PARAN));
}

/*****************************************************************************
* Routine to print pasring error according to ErrNum and set GlblParserError.*
*****************************************************************************/
static void ParserError(int ErrNum, char *Msg)
{
    DPGlblParserError = ErrNum;
    strcpy(DPGlblTokenError, Msg);	/* Keep the message in safe place... */

    DataPrsrFreeAll(VertexRoot, PolygonRoot); /* In case of non empty trees. */
    VertexRoot = PolygonRoot = NULL;

    longjmp(LclLongJumpBuffer, 1);			       /* Jump to... */
}

/*****************************************************************************
* Routine to insert new	element	into a binary tree. If the element already   *
* exists then the new one replace it.					     *
*****************************************************************************/
static void InsertBinTree(BinTree **Tree, BinTree *PNewRecord)
{
    int	Comparison;

    if (*Tree == NULL)			 /* Only might happen if tree empty. */
	*Tree =	PNewRecord;
    else {  /* Search for the new place	to put it */
	 /* Test for Match - if	so replace old by new: */
	if ((Comparison	= strcmp((*Tree) -> Name, PNewRecord -> Name)) == 0) {
	    ParserError(DP_ERR_SameName, PNewRecord -> Name);
	    return;
	}
	else if	(Comparison > 0)	   /* go to right side - its bigge.r */
		  InsertBinTree(&((*Tree) -> Right), PNewRecord);
	     else InsertBinTree(&((*Tree) -> Left), PNewRecord); /* smaller. */
    }
}

/*****************************************************************************
* Routine to Get an element from binary	tree. If the element is	found a	     *
* pointer to it	BinTree	record is return, NULL else...			     *
*****************************************************************************/
static BinTree *GetBinTree(char *Name, BinTree *Tree)
{
    int	Comparison;

    /* If the tree is empty - not found, return	NULL: */
    if (Tree ==	(BinTree *) NULL) return (BinTree *) NULL;

    /* Test for	Match -	if so return that record: */
    if ((Comparison = strcmp(Tree -> Name, Name)) == 0)
	return Tree;			      /* Found it - so return it ... */
    else if (Comparison	> 0)
	      return GetBinTree(Name, Tree -> Right);
	 else return GetBinTree(Name, Tree -> Left);
}

/*****************************************************************************
* Routine to get linear	list of	names from file	f until	']' is detected.     *
* search for that names	in BinTree VetrexRoot.				     *
* Create a linear list of pointers to them. Return that	linear list.	     *
* If PolyType == TKN_POLYLINE, then the list in NULL terminated, otherwise   *
* (TKN_POLYGON) it is made circular.					     *
*****************************************************************************/
static struct VertexStruct *GetVertexList(FILE *f, struct BinTree *VertexRoot,
								int PolyType)
{
    char StringToken[LINE_LEN];
    struct VertexStruct *PLinHead = NULL, *PLinTail = NULL;
    BinTree *PBin;

    while (GetToken(f, StringToken) != TKN_CLOSE_PARAN) {
	if ((PBin = GetBinTree(StringToken, VertexRoot)) == NULL) {
	    ParserError(DP_ERR_ListCompUndef, StringToken); /* Record undef. */
	    return NULL;
	}
	if (PLinHead ==	NULL)				/* Its first record. */
	    PLinHead = PLinTail	= AllocVertex(0, 0, NULL, NULL);
	else {					/* its record in the middle. */
	    PLinTail ->	Pnext =	AllocVertex(0, 0, NULL, NULL);
	    PLinTail = PLinTail	-> Pnext;
	}

	/* Copy the point coordinates: */
	PT_COPY(PLinTail -> Pt, PBin -> U.PVertex -> Pt);
	PLinTail -> Count = PBin -> U.PVertex -> Count;
	PLinTail -> Tags = PBin -> U.PVertex -> Tags;
    }
    if (PLinTail != NULL)
	if (PolyType == TKN_POLYLINE)
	     PLinTail -> Pnext = NULL;		 /* Mark end of linear list. */
	else PLinTail -> Pnext = PLinHead;	   /* Make it circular list. */

    return PLinHead;
}

/*****************************************************************************
* Routine to get linear	list of	polygons from file f, find them in the	     *
* binary tree PolygonRoot, and concat them into a linear list.		     *
*****************************************************************************/
static struct PolygonStruct *GetPolyList(FILE *f, struct BinTree *PolygonRoot)
{
    char StringToken[LINE_LEN];
    struct PolygonStruct *PLinHead = NULL, *PLinTail = NULL;
    BinTree *PBin;

    while (GetToken(f, StringToken) != TKN_CLOSE_PARAN) {
	if ((PBin = GetBinTree(StringToken, PolygonRoot)) == NULL) {
	    ParserError(DP_ERR_ListCompUndef, StringToken); /* Record undef. */
	    return NULL;
	}
	if (PLinHead ==	NULL)				/* Its first record. */
	    PLinHead = PLinTail	= AllocPolygon(0, 0, NULL, NULL);
	else {					/* Its record in the middle. */
	    PLinTail ->	Pnext = AllocPolygon(0, 0, NULL, NULL);
	    PLinTail = PLinTail	-> Pnext;
	}
	/* Polygon can be referred only one - we zap its vertex list: */
	if (PBin -> U.PPolygon -> V == NULL)
	    ParserError(DP_ERR_PolyDupRefer, StringToken);

	PLANE_COPY(PLinTail -> Plane, PBin -> U.PPolygon -> Plane);
	PLinTail -> Count = PBin -> U.PPolygon -> Count;
	PLinTail -> Tags = PBin -> U.PPolygon -> Tags;
	PLinTail -> V = PBin -> U.PPolygon -> V;
	PBin -> U.PPolygon -> V = NULL;
    }
    if (PLinTail != NULL)
	PLinTail -> Pnext = NULL;		 /* Mark end of linear list. */

    return PLinHead;
}

/*****************************************************************************
* Routine to get polygon/polyline attributes. Attribute is allways of the    *
* form [ATTR data1 data2 ...]. It is assumed the '[' was allready read.      *
*****************************************************************************/
static void GetPolyAttr(FILE *f, PolygonStruct *PPolygon, int *PlaneDefined)
{
    int i;
    char StringToken[LINE_LEN];

    *PlaneDefined = FALSE;

    switch (GetToken(f, StringToken)) {
	case TKN_PLANE:
	    for (i=0; i<4; i++) {
		GetToken(f, StringToken);
#		ifdef DOUBLE
		    if (sscanf(StringToken, "%lf", &PPolygon -> Plane[i]) != 1)
#		else
		    if (sscanf(StringToken, "%f", &PPolygon -> Plane[i]) != 1)
#		endif /* DOUBLE */
			ParserError(DP_ERR_NumberExpected, StringToken);
	    }
	    if (GetToken(f, StringToken) != TKN_CLOSE_PARAN)
		ParserError(DP_ERR_CloseParanExpected, StringToken);
	    *PlaneDefined = TRUE;
	    break;
	default:
	    ParserError(DP_ERR_UndefAttr, StringToken);
	    break;
    }
}

/*****************************************************************************
* Routine to get vertex attributes. Attribute is allways of the form:	     *
* [ATTR data1 data2 ...]. It is assumed the '[' was allready read.	     *
*****************************************************************************/
static void GetVertexAttr(FILE *f, VertexStruct *PVertex)
{
    char StringToken[LINE_LEN];

    switch (GetToken(f, StringToken)) {
	case TKN_INTERNAL:
	    if (GetToken(f, StringToken) != TKN_CLOSE_PARAN)
		ParserError(DP_ERR_CloseParanExpected, StringToken);
	    SET_INTERNAL_EDGE(PVertex);
	    break;
	default:
	    ParserError(DP_ERR_UndefAttr, StringToken);
	    break;
    }
}

/*****************************************************************************
* Routine to get object attributes. Attribute is allways of the form:	     *
* [ATTR data1 data2 ...]. It is assumed the '[' was allready read.	     *
*****************************************************************************/
static void GetObjectAttr(FILE *f, int *Color)
{
    int i;
    char StringToken[LINE_LEN];

    switch (GetToken(f, StringToken)) {
	case TKN_COLOR:
	    GetToken(f, StringToken);
	    if (sscanf(StringToken, "%d", &i) != 1)
		ParserError(DP_ERR_NumberExpected, StringToken);
	    if (GetToken(f, StringToken) != TKN_CLOSE_PARAN)
		ParserError(DP_ERR_CloseParanExpected, StringToken);
	    *Color = i;
	    break;
	default:
	    ParserError(DP_ERR_UndefAttr, StringToken);
	    break;
    }
}

/*****************************************************************************
*   Routine to return evaluation error if happen one, zero elsewhere	     *
*****************************************************************************/
int DataPrsrParseError(char **ErrorMsg)
{
    int	Temp;
    char TempCopy[LINE_LEN];

    if ((Temp = DPGlblParserError) == 0) return 0;

    strcpy(TempCopy, DPGlblTokenError);
    DPGlblParserError = 0;

    switch (Temp) {
	case DP_ERR_NumberExpected:
	    sprintf(DPGlblTokenError, "Line %d: Numeric data expected - found %s",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_CloseParanExpected:
	    sprintf(DPGlblTokenError, "Line %d: ']' expected - found %s",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_ListCompUndef:
	    sprintf(DPGlblTokenError, "Line %d: Undefined list element - \"%s\"",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_UndefExprHeader:
	    sprintf(DPGlblTokenError, "Line %d: Undefined TOKEN - \"%s\"",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_InternalStackOF:
	    sprintf(DPGlblTokenError, "Line %d: Internal stack O.F. (report it!)",
		DPGlblLineCount);
	    break;
	case DP_ERR_SameName:
	    sprintf(DPGlblTokenError, "Line %d: Item defined more than once - \"%s\"",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_PolyDupRefer:
	    sprintf(DPGlblTokenError, "Line %d: Polygon refered more than once - \"%s\"",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_UndefAttr:
	    sprintf(DPGlblTokenError, "Line %d: Undefined attribute - \"%s\"",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_OnlyMatGeomObj:
	    sprintf(DPGlblTokenError, "Only matrix or geometric objects allowed");
	    break;
	case DP_ERR_EmptyName:
	    sprintf(DPGlblTokenError, "Empty file name was given");
	    break;
	case DP_ERR_OpenFail:
	    sprintf(DPGlblTokenError, "Fail to open file - %s",	TempCopy);
	    break;
	case DP_ERR_WrongFileType:
	    sprintf(DPGlblTokenError, "Only '.mat' or '.ply' types allowed, found - %s",
		TempCopy);
	    break;
	case DP_ERR_MixedPolygonLine:
	    sprintf(DPGlblTokenError, "Line %d: Mixed polygon/polylines is not allowed is same file",
		DPGlblLineCount);
	    break;
	case DP_ERR_NonMatrixObject:
	    sprintf(DPGlblTokenError, "Line %d: No matrix object element found",
		    DPGlblLineCount);
	    break;
	default:
	    sprintf(DPGlblTokenError, "Data file parser - undefined error");
	    break;
    }

    *ErrorMsg = DPGlblTokenError;

    return Temp;
}

#ifdef	DEBUG

/*****************************************************************************
* Routine to Print the Names in	tree in	lexicorgaphic order. Used only for   *
* debuging - to	see trees content...					     *
*****************************************************************************/
static void PrintBinTree(BinTree *Tree)
{
    /* If the tree is empty - not found, return	NULL: */
    if (Tree ==	(BinTree *) NULL) return;

    PrintBinTree(Tree -> Right);
    printf("%s\n", Tree -> Name);
    PrintBinTree(Tree -> Left);
}

#endif /* DEBUG */
