/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Module to handle the objects list - fetch, insert, delete etc...	     *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "program.h"
#include "allocate.h"
#include "attribut.h"
#include "geomat3d.h"
#include "objects.h"
#include "windows.h"

#define IS_FLOAT_PRINTF_CMD(c)	((c) == 'e' || (c) == 'f' || (c) == 'g' || \
				 (c) == 'E' || (c) == 'F')
#define IS_PRINTF_CMD(c)  (IS_FLOAT_PRINTF_CMD(c) || \
			   (c) == 'd' || (c) == 'i' || (c) == 'u' || \
			   (c) == 'o' || (c) == 'x' || (c) == 'X' || \
			   (c) == 's' || (c) == 'p' || (c) == 'v' || \
			   (c) == 'P' || (c) == 'D')

static int GetDumpLevel(void);
static IPPolygonStruct *GenAxesObjectPolylines(void);
static void PrintOneLine(char *Line);
static void UpdateLoadedHierarchy(IPObjectStruct *PObj, IPObjectStruct *Root);
static int MatGenMatGeneric(IPObjectStruct *LstObjList, MatrixType Mat);

/*****************************************************************************
*   Routine to set up all the predefined objects - objects that the system   *
* must have all the time, like global transformation matrix.		     *
*****************************************************************************/
void SetUpPredefObjects(void)
{
    RealType R;
    MatrixType Mat1, Mat2;
    IPObjectStruct *PObj;

    /* 90 - 35.2644 = 54.7356 */
    MatGenMatRotX1(DEG2RAD(-54.7356), Mat1); /* Generate default view trans. */
    MatGenMatRotZ1(M_PI+M_PI/4, Mat2);		 /* which is isometric view. */
    MatMultTwo4by4(Mat2, Mat2, Mat1);
    PObj = GenMatObject("VIEW_MAT", Mat2, NULL);
    InsertObject(PObj);

    MatGenUnitMat(Mat1);	      /* Generate default perspective trans. */
    Mat1[2][2] = 0.1;
    Mat1[2][3] = -0.35;
    Mat1[3][2] = 0.35;
    PObj = GenMatObject("PRSP_MAT", Mat1, NULL);
    InsertObject(PObj);

    R = DEFAULT_RESOLUTION;
    PObj = GenNumObject("RESOLUTION", &R, NULL);
    InsertObject(PObj);

    R = DEFAULT_DRAW_CTLPT;
    PObj = GenNumObject("DRAWCTLPT", &R, NULL);
    InsertObject(PObj);

    R = DEFAULT_INTERCRV;
    PObj = GenNumObject("INTERCRV", &R, NULL);
    InsertObject(PObj);

    R = DEFAULT_ECHOSRC;
    PObj = GenNumObject("ECHOSRC", &R, NULL);
    InsertObject(PObj);

    R = DEFAULT_DUMPLVL;
    PObj = GenNumObject("DUMPLVL", &R, NULL);
    InsertObject(PObj);

    R = 0;
    PObj = GenNumObject("FLAT4PLY", &R, NULL);
    InsertObject(PObj);

    R = 0;
    PObj = GenNumObject("POLYSORT", &R, NULL);
    InsertObject(PObj);

    R = 0;
    PObj = GenNumObject("COPLANAR", &R, NULL);
    InsertObject(PObj);

    R = MACHINE_UNIX;
#if defined(__MSDOS__) || defined(DJGCC)
    R = MACHINE_MSDOS;
#endif
#if defined(sgi)
    R = MACHINE_SGI;
#endif
#if defined(hpbsd) || defined(hpux) || defined(__hpux)
    R = MACHINE_HP;
#endif
#if defined(sun)
    R = MACHINE_SUN;
#endif
#if defined(apollo)
    R = MACHINE_APOLLO;
#endif
#if defined(OS2GCC)
    R = MACHINE_IBMOS2;
#endif
#if defined(WIN32NT)
    R = MACHINE_IBMNT;
#endif
#if defined(AMIGA)
    R = MACHINE_AMIGA;
#endif

    PObj = GenNumObject("MACHINE", &R, NULL);
    InsertObject(PObj);

    PObj = GenPolyObject("AXES", GenAxesObjectPolylines(), NULL);
    IP_SET_POLYLINE_OBJ(PObj);		      /* Mark it as polyline object. */
    InsertObject(PObj);
}

/*****************************************************************************
*   Routine to set an attribute of an object.				     *
*****************************************************************************/
void SetObjectAttrib(IPObjectStruct *PObj, char *Name, IPObjectStruct *Data)
{
    if (IP_IS_STR_OBJ(Data))
	AttrSetObjectStrAttrib(PObj, Name, Data -> U.Str);
    else if (IP_IS_NUM_OBJ(Data)) {
	RealType
	    r = Data -> U.R;

	if (r == (int) r)
	    AttrSetObjectIntAttrib(PObj, Name, (int) r);
	else
	    AttrSetObjectRealAttrib(PObj, Name, r);
    }
}

/*****************************************************************************
*   Routine to get the value to EchoSrc variable.			     *
*****************************************************************************/
static int GetDumpLevel(void)
{
    int DumpLvl;
    IPObjectStruct
	*PObj = GetObject("DUMPLVL");

    if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
	WndwInputWindowPutStr("No numeric object name DumpLvl is defined");
	DumpLvl = DEFAULT_DUMPLVL;
    }
    else
	DumpLvl = (int) (PObj -> U.R);

    return DumpLvl;
}

/*****************************************************************************
*   Generate an axes system with length of 1 on each axis.		     *
*****************************************************************************/
static IPPolygonStruct *GenAxesObjectPolylines(void)
{
    IPPolygonStruct *Pl, *PlHead;
    IPVertexStruct *V;

    /* X axis. */
    Pl = PlHead = IPAllocPolygon(0, 0, NULL, NULL);
    Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
    V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 1.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 1.0;    V -> Coord[1] = 0.1;   V -> Coord[2] = 0.1;

    Pl -> Pnext = IPAllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;
    Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
    V -> Coord[0] = 1.0;    V -> Coord[1] = 0.1;   V -> Coord[2] = 0.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 1.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.1;

    /* Y axis.*/
    Pl -> Pnext = IPAllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;
    Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
    V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.0;    V -> Coord[1] = 1.0;   V -> Coord[2] = 0.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.0;    V -> Coord[1] = 1.0;   V -> Coord[2] = 0.06;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.04;   V -> Coord[1] = 1.0;   V -> Coord[2] = 0.1;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.0;    V -> Coord[1] = 1.0;   V -> Coord[2] = 0.06;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] =(-0.04); V -> Coord[1] = 1.0;   V -> Coord[2] = 0.1;

    /* Z axis.*/
    Pl -> Pnext = IPAllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;
    Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
    V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 1.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.1;    V -> Coord[1] = 0.0;   V -> Coord[2] = 1.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.0;    V -> Coord[1] = 0.1;   V -> Coord[2] = 1.0;
    V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Coord[0] = 0.1;    V -> Coord[1] = 0.1;   V -> Coord[2] = 1.0;

    return PlHead;
}

/*****************************************************************************
*   Returns the type of an object.					     *
*****************************************************************************/
double ThisObjectIs(IPObjectStruct *PObj)
{
    return (double) (PObj -> ObjType);
}

/*****************************************************************************
*   General printf routine, IRIT's style, to stdout.			     *
*****************************************************************************/
void IritObjectPrintfStdout(char *CtlStr, IPObjectStruct *PObjLst)
{
    IritObjectPrintf(stdout, CtlStr, PObjLst);
}

/*****************************************************************************
*   General printf routine, IRIT's style.				     *
*****************************************************************************/
void IritObjectPrintf(FILE *File, char *CtlStr, IPObjectStruct *PObjLst)
{
    char Buffer[LINE_LEN], Line[LINE_LEN_LONG];
    int i, j, k,
	CrntItem = 0,
	NumOfItems = ListObjectLength(PObjLst);
    IPObjectStruct *CrntObj;

    Line[0] = 0;

    for (i = 0; i < strlen(CtlStr); i++) {
	if (CtlStr[i] == '%') {
	    for (j = 0; j < 10 && !IS_PRINTF_CMD(CtlStr[i]); j++, i++)
		Buffer[j] = CtlStr[i];
	    Buffer[j++] = CtlStr[i];
	    Buffer[j--] = 0;

	    if (CrntItem >= NumOfItems) {
		WndwInputWindowPutStr("PRINTF: Not enough objects for printf.");
		return;
	    }
	    CrntObj = ListObjectGet(PObjLst, CrntItem++);

	    switch (Buffer[j]) {
		case 'c':
		    if (IP_IS_NUM_OBJ(CrntObj)) {
			sprintf(&Line[strlen(Line)], Buffer,
				(int) CrntObj -> U.R);
		    }
		    else {
			WndwInputWindowPutStr("PRINTF: Number expected.");
			return;
		    }
		    break;
		case 'd':
		case 'i':
		case 'u':
		case 'o':
		case 'x':
		case 'X':
		    if (IP_IS_NUM_OBJ(CrntObj)) {
			sprintf(&Line[strlen(Line)], Buffer,
				(int) CrntObj -> U.R);
		    }
		    else {
			WndwInputWindowPutStr("PRINTF: Number expected.");
			return;
		    }
		    break;
		case 'e':
		case 'f':
		case 'g':
		case 'E':
		case 'G':
		    if (IP_IS_NUM_OBJ(CrntObj)) {
			sprintf(&Line[strlen(Line)], Buffer,
				CrntObj -> U.R);
		    }
		    else {
			WndwInputWindowPutStr("PRINTF: Number expected.");
			return;
		    }
		    break;
		case 's':
		    if (IP_IS_STR_OBJ(CrntObj)) {
			sprintf(&Line[strlen(Line)], Buffer,
				CrntObj -> U.Str);
		    }
		    else {
			WndwInputWindowPutStr("PRINTF: String expected.");
			return;
		    }
		    break;
		case 'p':
		case 'v':
		    if (!IP_IS_VEC_OBJ(CrntObj) &&
			!IP_IS_POINT_OBJ(CrntObj)) {
			WndwInputWindowPutStr(
			    "PRINTF: Vector or Point expected.");
			return;
		    }
		    Buffer[j] = CtlStr[++i];
		    if (!IS_FLOAT_PRINTF_CMD(Buffer[j])) {
			WndwInputWindowPutStr(
			    "PRINTF: Floaing point number command expected.");
			return;
		    }
		    for (k = 0; k < 3; k++) {
			sprintf(&Line[strlen(Line)],
				Buffer, CrntObj -> U.Vec[k]);
			if (k < 2)
			    strcat(Line, ", ");
		    }
		    break;
		case 'P':
		    if (!IP_IS_PLANE_OBJ(CrntObj)) {
			WndwInputWindowPutStr("PRINTF: Plane expected.");
			return;
		    }
		    Buffer[j] = CtlStr[++i];
		    if (!IS_FLOAT_PRINTF_CMD(Buffer[j])) {
			WndwInputWindowPutStr(
			    "PRINTF: Floaing point number command expected.");
			return;
		    }
		    for (k = 0; k < 4; k++) {
			sprintf(&Line[strlen(Line)],
				Buffer, CrntObj -> U.Plane[k]);
			if (k < 3)
			    strcat(Line, ", ");
		    }
		    break;
		case 'D':
		    Buffer[j] = CtlStr[++i];
		    CagdSetFloatFormat(Buffer);
		    IritPrsrSetFloatFormat(Buffer);

		    if (strlen(Line) > 0) {
			WndwInputWindowPutStr(Line);
			Line[0] = 0;
		    }
		    PrintObject(CrntObj);
		    CagdSetFloatFormat(GlblFloatFormat);
		    IritPrsrSetFloatFormat(GlblFloatFormat);
		    break;
		default:
		    WndwInputWindowPutStr(
			"PRINTF: Unknown % control to print command.");
		    return;
	    }
	}
	else if (CtlStr[i] == '\\') {
	    k = strlen(Line);

	    switch (CtlStr[++i]) {
		case 't':
		    Line[k] = '\t';
		    break;
		case 'n':
		    Line[k] = '\n';
		    break;
		case '%':
		    Line[k] = '%';
		    break;
	    }
	    Line[++k] = 0;

	}
	else {
	    k = strlen(Line);
	    Line[k++] = CtlStr[i];
	    Line[k] = 0;
	}
    }
    if (strlen(Line) > 0)
	WndwInputWindowPutStr(Line);
}

/*****************************************************************************
*   Get the size of a list object.					     *
*****************************************************************************/
double GetListSize(IPObjectStruct *Obj)
{
    if (IP_IS_POLY_OBJ(Obj)) {
	if (Obj -> U.Pl -> Pnext == NULL)
	    return (double) IritPrsrVrtxListLen(Obj -> U.Pl -> PVertex);
	else
	    return (double) IritPrsrPolyListLen(Obj -> U.Pl);
    }
    else if (IP_IS_OLST_OBJ(Obj)) {
	return (double) ListObjectLength(Obj);
    }
    else {
	WndwInputWindowPutStr("None list object ignored.");
        return 0.0;
    }
}

/*****************************************************************************
*  Create an empty list.						     *
*****************************************************************************/
IPObjectStruct *GetNilList()
{
    IPObjectStruct
	*PObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    ListObjectInsert(PObj, 0, NULL);

    return PObj;
}

/*****************************************************************************
*   Get the nth object in a list.					     *
*****************************************************************************/
IPObjectStruct *GetNthList(IPObjectStruct *ListObj, RealType *Rn)
{
    int n = REAL_PTR_TO_INT(Rn);

    IPObjectStruct *PObj;

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

    if (n < 1 || n > ListObjectLength(ListObj)) {
	WndwInputWindowPutStr("Out of range of list.");
        return NULL;
    }

    PObj = CopyObject(NULL, ListObjectGet(ListObj, n - 1), FALSE);
    return PObj;
}

/*****************************************************************************
*  Append two lists. Object reference is properly updated.		     *
*****************************************************************************/
IPObjectStruct *AppendLists(IPObjectStruct *ListObj1, IPObjectStruct *ListObj2)
{
    int i, j;
    IPObjectStruct *PObj, *PObjTmp;

    if (!IP_IS_OLST_OBJ(ListObj1) && !IP_IS_OLST_OBJ(ListObj2)) {
	WndwInputWindowPutStr("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);
	PObjTmp -> Count++;
    }

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

    ListObjectInsert(PObj, i, NULL);

    return PObj;
}

/*****************************************************************************
*  Snoc (Cons to the end of the list, in place) the object to the list in    *
* the second argument.							     *
*****************************************************************************/
void SnocList(IPObjectStruct *PObj, IPObjectStruct *ListObj)
{
    int i;
    IPObjectStruct *PObjTmp;

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

    i = ListObjectLength(ListObj);
    ListObjectInsert(ListObj, i, PObjTmp = CopyObject(NULL, PObj, FALSE));
    PObjTmp -> Count = 1;
    ListObjectInsert(ListObj, i + 1, NULL);
}

/*****************************************************************************
*   Get object by its name - scans the object linear list.		     *
* Note the termination is also on 1000 objects (simple debugging aid in case *
* the Object list became circular), and a fatal error is produced.	     *
*****************************************************************************/
IPObjectStruct *GetObject(char *ObjName)
{
    int i = 0;
    IPObjectStruct
	*PObj = GlblObjList;

    while (PObj) {
	if (strcmp(PObj -> Name, ObjName) == 0) {
	    return PObj;
	}
	PObj = PObj -> Pnext;
	if (i++ >= 1000)
	    IritFatalError("GetObject: Global Object list too big (>1000)");
    }
    return NULL;
}

/*****************************************************************************
*  Extract a single entity out af an obj holding a set of entities at Index. *
* For VECTOR, POINT, CTLPT, PLANE, MAT a single numeric data is returned.    *
* For a POLYGON a single vertex is returned (as VECTOR).		     *
* For CURVE, and SURFACE a single control point is returned.		     *
*****************************************************************************/
IPObjectStruct *GetObjectCoord(IPObjectStruct *PObj, RealType *RIndex)
{
    int Index = REAL_PTR_TO_INT(RIndex);
    IPObjectStruct
	*CoordPObj = NULL;
    CagdCtlPtStruct CtlPt;

    switch (PObj -> ObjType) {
	case IP_OBJ_POLY:
	    if (PObj -> U.Pl == NULL)
		break;
	    if (PObj -> U.Pl -> Pnext) {
		IPPolygonStruct *P;

		/* Extract a single poly from the poly list. */
		for (P = PObj -> U.Pl;
		     P != NULL && Index > 0;
		     P = P -> Pnext, Index--);
		if (P != NULL) {
		    IPPolygonStruct
		        *PTmp = IPAllocPolygon(1, P -> Tags,
					CopyVertexList(P -> PVertex), NULL);

		    CoordPObj = GenPolyObject("", PTmp, NULL);
		    PLANE_COPY(PTmp -> Plane, P -> Plane);
		    IP_RST_BBOX_POLY(PTmp);
		    CoordPObj -> Attrs = AttrCopyAttributes(PObj -> Attrs);

		    if (IP_IS_POLYGON_OBJ(PObj))
		        IP_SET_POLYGON_OBJ(CoordPObj);
		    else if (IP_IS_POLYLINE_OBJ(PObj))
		        IP_SET_POLYLINE_OBJ(CoordPObj);
		    else if (IP_IS_POINTLIST_OBJ(PObj))
		        IP_SET_POINTLIST_OBJ(CoordPObj);
		}
	    }
	    else {
		IPVertexStruct *V;

		/* Extract a vertex from the poly. */
		for (V = PObj -> U.Pl -> PVertex;
		     V != NULL && Index > 0;
		     V = V -> Pnext, Index--);
		if (V != NULL)
		  CoordPObj = GenVECObject(&V -> Coord[0], &V -> Coord[1],
					   &V -> Coord[2]);
	    }
	    break;
	case IP_OBJ_POINT:
	    if (Index >= 0 && Index < 3)
		CoordPObj = GenNUMValObject(PObj -> U.Pt[Index]);
	    break;
	case IP_OBJ_VECTOR:
	    if (Index >= 0 && Index < 3)
		CoordPObj = GenNUMValObject(PObj -> U.Pt[Index]);
	    break;
	case IP_OBJ_PLANE:
	    if (Index >= 0 && Index < 4)
		CoordPObj = GenNUMValObject(PObj -> U.Pt[Index]);
	    break;
	case IP_OBJ_CTLPT:
	    if (!CAGD_IS_RATIONAL_PT(PObj -> U.CtlPt.PtType) && Index == 0)
		break;
	    if (Index >= 0 &&
		Index <= CAGD_NUM_OF_PT_COORD(PObj -> U.CtlPt.PtType))
		CoordPObj = GenNUMValObject(PObj -> U.CtlPt.Coords[Index]);
	    break;
	case IP_OBJ_MATRIX:
	    if (Index >= 0 && Index < 16)
		CoordPObj =
		    GenNUMValObject((*PObj -> U.Mat)[Index / 4][Index % 4]);
	    break;
	case IP_OBJ_LIST_OBJ:
	    CoordPObj = GetNthList(PObj, RIndex);
	    break;
	case IP_OBJ_CURVE:
	    if (PObj -> U.Crvs == NULL)
	        break;
	    if (PObj -> U.Crvs -> Pnext != NULL) {
		CagdCrvStruct *Crv;

		/* Extract a single curve from the curve list. */
		for (Crv = PObj -> U.Crvs;
		     Crv != NULL && Index > 0;
		     Crv = Crv -> Pnext, Index--);
		if (Crv != NULL)
		    CoordPObj = GenCRVObject(CagdCrvCopy(Crv));
	    }
	    else {
		/* Extract a ctlpt from the curve. */
		CagdEditSingleCrvPt(PObj -> U.Crvs, &CtlPt, Index, FALSE);
		CoordPObj = GenCTLPTObject(PObj -> U.Crvs -> PType,
					   CtlPt.Coords, NULL);
	    }
	    break;
	case IP_OBJ_SURFACE:
	    if (PObj -> U.Srfs == NULL)
	        break;
	    if (PObj -> U.Srfs -> Pnext != NULL) {
		CagdSrfStruct *Srf;

		/* Extract a single surface from the surface list. */
		for (Srf = PObj -> U.Srfs;
		     Srf != NULL && Index > 0;
		     Srf = Srf -> Pnext, Index--);
		if (Srf != NULL)
		    CoordPObj = GenSRFObject(CagdSrfCopy(Srf));
	    }
	    else {
		/* Extract a ctlpt from the surface. */
		CagdEditSingleSrfPt(PObj -> U.Srfs, &CtlPt,
				    Index / PObj -> U.Srfs -> ULength,
				    Index % PObj -> U.Srfs -> ULength,
				    FALSE);
		CoordPObj = GenCTLPTObject(PObj -> U.Srfs -> PType,
					   CtlPt.Coords, NULL);
	    }
	    break;
	default:
	    break;
    }

   if (CoordPObj == NULL)
       WndwInputWindowPutStr("Coord: Out of range or wrong object type.");

    return CoordPObj;
}


/*****************************************************************************
*   Free Object - delete it from global active list and free all it memory   *
*****************************************************************************/
void FreeObject(IPObjectStruct *PObj)
{
    /* Force free the object. Since the reference count should be actually   */
    /* two (second for the parsing tree reference) we decrement it here.     */
    if (PObj  -> Count == 2) {
	PObj-> Count = 1;
	DeleteObject(PObj, TRUE);
    }
    else {
	/* Reduce the reference count by two - one for the parsing tree      */
	/* this routine was called from and one for the fact this object     */
	/* reference count is to be deleted since this routine was called.   */
	DeleteObject(PObj, FALSE);
	PObj -> Count -= 2;
    }
}

/*****************************************************************************
*   Delete object by its pointer - scans the object linear list.	     *
* Note the deleted record is free only if Free = TRUE.			     *
*****************************************************************************/
void DeleteObject(IPObjectStruct *PObj, int Free)
{
    IPObjectStruct
	*PObjScan = GlblObjList;

    if (GlblObjList == NULL)
	return;

    if (PObj == GlblObjList) {	   /* If it is the first one - special case. */
	GlblObjList = GlblObjList->Pnext;
	if (Free)
	    IPFreeObject(PObj);
	return;
    }

    while (PObjScan->Pnext) {
	if (PObj == PObjScan->Pnext) {
	    PObjScan->Pnext = PObjScan->Pnext->Pnext;/* Delete it from list. */
	    if (Free)
		IPFreeObject(PObj);			     /* And free it. */
	    return;
	}
	PObjScan = PObjScan->Pnext;
    }
}

/*****************************************************************************
*   Insert object by its pointer - as first in object linear list.	     *
* Note it is assumed the object is not in the object list allready.	     *
*****************************************************************************/
void InsertObject(IPObjectStruct *PObj)
{
    PObj -> Pnext = GlblObjList;
    GlblObjList = PObj;
}

/*****************************************************************************
*   Print one line.							     *
*****************************************************************************/
static void PrintOneLine(char *Line)
{
    WndwInputWindowPutStr(Line);
}

/*****************************************************************************
*   Print some usefull info on the given object.			     *
*****************************************************************************/
void PrintObject(IPObjectStruct *PObj)
{
    int Count = PObj -> Count;
    CagdRType
	DumpLvl = GetDumpLevel();
    char Line[LINE_LEN_LONG],
	*Name = (int) strlen(PObj -> Name) > 0 ? PObj -> Name : "NONE";

    CagdSetCagdFprintf(PrintOneLine);

    switch (PObj -> ObjType) {
	case IP_OBJ_UNDEF:
	    sprintf(Line, "%-10s (%d) - Undefined type", Name, Count);
	    PrintOneLine(Line);
	    break;
	case IP_OBJ_POLY:
	    sprintf(Line, "%-10s (%d) - Poly      type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 3)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_NUMERIC:
	    sprintf(Line, "%-10s (%d) - Numeric   type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 1)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_POINT:
	    sprintf(Line, "%-10s (%d) - Point     type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 1)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_VECTOR:
	    sprintf(Line, "%-10s (%d) - Vector    type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 1)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_PLANE:
	    sprintf(Line, "%-10s (%d) - Plane     type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 1)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_CTLPT:
	    sprintf(Line, "%-10s (%d) - CtlPt     type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 1)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_MATRIX:
	    sprintf(Line, "%-10s (%d) - Matrix    type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 1)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_STRING:
	    sprintf(Line, "%-10s (%d) - String    type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 1)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_LIST_OBJ:
	    sprintf(Line, "%-10s (%d) - Object List type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 4)
		SaveObjectInFile(NULL, PObj);
	    break;
	case IP_OBJ_CURVE:
	    sprintf(Line, "%-10s (%d) - Curve     type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 2) {
		SaveObjectInFile(NULL, PObj);
	    }
	    break;
	case IP_OBJ_SURFACE:
	    sprintf(Line, "%-10s (%d) - Surface   type", Name, Count);
	    PrintOneLine(Line);
	    if (DumpLvl >= 2) {
		SaveObjectInFile(NULL, PObj);
	    }
	    break;
	default:
	    sprintf(Line, "%-10s (%d) - Obj type error, type = %d",
				Name, Count, PObj->ObjType);
	    PrintOneLine(Line);
	    break;
    }

    CagdSetCagdFprintf(NULL);
}

/*****************************************************************************
*   Print some usefull info on all the given objects.			     *
*****************************************************************************/
void PrintObjectList(IPObjectStruct *PObj)
{
    PrintOneLine("");

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

/*****************************************************************************
*  Coerce an object to a new object.					     *
*****************************************************************************/
IPObjectStruct *CoerceObjectTo(IPObjectStruct *PObj, RealType *RNewType)
{
    int i,
	NewType = REAL_TO_INT(*RNewType);
    RealType Pt[CAGD_MAX_PT_SIZE], *R;
    IPObjectStruct
	*NewObj = NULL;

    switch (PObj -> ObjType) {
	case IP_OBJ_POINT:
	case IP_OBJ_VECTOR:
	case IP_OBJ_PLANE:
	case IP_OBJ_CTLPT:
	    Pt[0] = 1.0;
	    for (i = 1; i < CAGD_MAX_PT_SIZE; i++)
	        Pt[i] = 0.0;
	    switch (PObj -> ObjType) {
		case IP_OBJ_POINT:
		    PT_COPY(&Pt[1], PObj -> U.Pt);
		    break;
		case IP_OBJ_VECTOR:
		    PT_COPY(&Pt[1], PObj -> U.Vec);
		    break;
		case IP_OBJ_PLANE:
		    PLANE_COPY(&Pt[1], PObj -> U.Plane);
		    break;
		case IP_OBJ_CTLPT:
		    R = PObj -> U.CtlPt.Coords;
		    CagdCoercePointTo(&Pt[1], CAGD_PT_E5_TYPE,
				      &R, -1, PObj -> U.CtlPt.PtType);
		    break;
		default:
		    break;
	    }

	    switch (NewType) {
		case IP_OBJ_POINT:
		    NewObj = GenPTObject(&Pt[1], &Pt[2], &Pt[3]);
		    break;
		case IP_OBJ_VECTOR:
		    NewObj = GenVECObject(&Pt[1], &Pt[2], &Pt[3]);
		    break;
		case IP_OBJ_PLANE:
		    NewObj = GenPLANEObject(&Pt[1], &Pt[2], &Pt[3], &Pt[4]);
		    break;
		case IP_OBJ_CTLPT:
		    NewObj = GenCTLPTObject(CAGD_PT_E3_TYPE, NULL, Pt);
		    break;
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    if (PObj -> ObjType == IP_OBJ_CTLPT)
		        CagdCoercePointTo(Pt, CAGD_PT_P5_TYPE,
					  &R, -1, PObj -> U.CtlPt.PtType);
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		    NewObj = GenCTLPTObject(NewType, NULL, Pt);
		    break;		    
		default:
		    break;		    
	    }
	    break;
	case IP_OBJ_CURVE:
	    switch (NewType) {
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    NewObj = GenCRVObject(CagdCoerceCrvTo(PObj -> U.Crvs,
							  NewType));
		    break;
		default:
		    break;
	    }
	    break;
	case IP_OBJ_SURFACE:
	    switch (NewType) {
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    NewObj = GenSRFObject(CagdCoerceSrfTo(PObj -> U.Srfs,
							  NewType));
		    break;
		default:
		    break;
	    }

	    break;
	default:
	    break;
    }

    if (NewObj == NULL)
	WndwInputWindowPutStr("Invalid coerction requested.");
    return NewObj;
}

/*****************************************************************************
*  Save an object in a data file					     *
*****************************************************************************/
void SaveObjectInFile(char *FileName, IPObjectStruct *PObj)
{
    FILE *f;
    int IsBinary = FALSE;
    char FullFileName[LINE_LEN_LONG];

    if (FileName != NULL) {
	strcpy(FullFileName, FileName);
	if (strstr(FullFileName, ".bdt") ||
	    strstr(FullFileName, ".BDT")) {
	    /* Binary data file is requested. */
	    IsBinary = TRUE;
	}
	else if (strstr(FullFileName, ".dat") == NULL &&
		 strstr(FullFileName, ".DAT") == NULL)
	    strcat(FullFileName, ".dat");

	if ((f = IritPrsrOpenDataFile(FullFileName, FALSE, FALSE)) != NULL) {
	    if (!IsBinary)
		IRIT_DATA_HEADER(f, "Irit");

	    IritPrsrPutObject(f, PObj);
	    IritPrsrCloseDataFile(f);
	}
	else
	    WndwInputWindowPutStr("failed to open file for write.");
    }
    else {
	IritPrsrSetPrintFunc(WndwInputWindowPutStr);
	IritPrsrPutObject(NULL, PObj);
    }
}

/*****************************************************************************
*  Load an object(s) from a data file					     *
*****************************************************************************/
IPObjectStruct *LoadObjectFromFile(char *FileName)
{
    FILE *f;
    char FullFileName[LINE_LEN_LONG];

    if (FileName != NULL) {
	strcpy(FullFileName, FileName);
	if (strstr(FullFileName, ".bdt") == NULL &&
	    strstr(FullFileName, ".BDT") == NULL &&
	    strstr(FullFileName, ".dat") == NULL &&
	    strstr(FullFileName, ".DAT") == NULL)
	    strcat(FullFileName, ".dat");

	if ((f = IritPrsrOpenDataFile(FullFileName, TRUE, FALSE)) != NULL) {
    	    IPObjectStruct
    	        *PObj = IritPrsrGetObjects(f);

	    UpdateLoadedHierarchy(PObj, PObj);

	    IritPrsrCloseDataFile(f);
	    return PObj;
	}
    }

    WndwInputWindowPutStr("Failed to open file for read.");
    return NULL;
}

/*****************************************************************************
*  Update hierarchy of loaded object.					     *
*****************************************************************************/
static void UpdateLoadedHierarchy(IPObjectStruct *PObj, IPObjectStruct *Root)
{
    if (IP_IS_OLST_OBJ(PObj)) {
	int i;
	IPObjectStruct *PObjTmp;

	for (i = 0; (PObjTmp = ListObjectGet(PObj, i)) != NULL; i++)
	    UpdateLoadedHierarchy(PObjTmp, Root);
    }

    if (PObj != Root && strlen(PObj -> Name) > 0) {
	IPObjectStruct *OldPObj;

	if ((OldPObj = GetObject(PObj -> Name)) != NULL)
	    DeleteObject(OldPObj, TRUE);
	InsertObject(PObj);
	PObj -> Count++;
    }
}

/*****************************************************************************
*  Save an object in a file						     *
*****************************************************************************/
int LoadSaveObjectParseError(char **ErrorMsg)
{
    int RetVal = IritPrsrParseError(ErrorMsg);

    if (!RetVal) {
	*ErrorMsg = "";
	return FALSE;
    }
    else
	return TRUE;
}

/*****************************************************************************
* Routine to scale an object according to XYZ scaling vector Vec.	     *
*****************************************************************************/
IPObjectStruct *GenMatObjectGeneric(IPObjectStruct *LstObjList)
{
    MatrixType Mat;

    /* Generate the transformation matrix */
    if (MatGenMatGeneric(LstObjList, Mat))
	return GenMATObject(Mat);
    else
	return NULL;
}

/*****************************************************************************
* Routine to generate a 4*4 matrix  by specifying all its 16 coefficients.   *
*****************************************************************************/
static int MatGenMatGeneric(IPObjectStruct *LstObjList, MatrixType Mat)
{
    int i, j;
    IPObjectStruct *Row, *Col;

    MatGenUnitMat(Mat);                             /* Make it unit matrix, */

    if (!IP_IS_OLST_OBJ(LstObjList)) {
	WndwInputWindowPutStr("Matrix: Not object list object!");
	return FALSE;
    }

    for (i = 0; i < 4; i++) {
	if ((Row = ListObjectGet(LstObjList, i)) == NULL) {
	    WndwInputWindowPutStr("Matrix: Four rows expected, found less");
	    return FALSE;
	}
	if (!IP_IS_OLST_OBJ(Row)) {
	    WndwInputWindowPutStr("None list object found in list");
	    return FALSE;
	}

	for (j = 0; j < 4; j++) {
	    if ((Col = ListObjectGet(Row, j)) == NULL) {
		WndwInputWindowPutStr("Matrix: Four columns expected, found less.");
		return FALSE;
	    }

	    if (!IP_IS_NUM_OBJ(Col)) {
		WndwInputWindowPutStr("Numeric value expected.");
		return FALSE;
	    }

	    Mat[i][j] = Col -> U.R;
	}
    }
    return TRUE;
}

/*****************************************************************************
* Routine to handle all surfaces/curves. Simple chain them into a linear     *
* list and return it. A Call back function from IritPrsr.		     *
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IPObjectStruct *CrvObjs,
					IPObjectStruct *SrfObjs)
{
    if (CrvObjs == NULL)
	return SrfObjs;
    else if (SrfObjs == NULL)
	return CrvObjs;
    else {
	IPObjectStruct *PTmp = CrvObjs;

	while (PTmp -> Pnext)
	    PTmp = PTmp -> Pnext;
	PTmp -> Pnext = SrfObjs;
	return CrvObjs;
    }
}

