/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Module to handle the objects list - fetch, insert, delete etc...	     *
*****************************************************************************/

/* #define DEBUG			   Output goes to stdout if defined. */

#ifdef __MSDOS__
#include <graphics.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "program.h"
#include "objects.h"
#include "allocatg.h"
#include "geomat3d.h"
#include "windowsg.h"

#ifndef __MSDOS__
#include "xgraphic.h"
#endif /* __MSDOS__ */

static struct PolygonStruct *GenAxesObjectPolylines(void);

/*****************************************************************************
*   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;
    struct ObjectStruct *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);

    R = DEFAULT_RESOLUTION;
    PObj = GenNumObject("RESOLUTION", &R, NULL);
    InsertObject(PObj);

    R = DEFAULT_INTERNAL;
    PObj = GenNumObject("INTERNAL", &R, NULL);
    InsertObject(PObj);

    R = DEFAULT_INTERCRV;
    PObj = GenNumObject("INTERCRV", &R, NULL);
    InsertObject(PObj);

    PObj = GenGeomObject("AXES", GenAxesObjectPolylines(), NULL);
    SET_POLYLINE_GEOM_OBJ(PObj);	      /* Mark it as polyline object. */
    InsertObject(PObj);
}

/*****************************************************************************
*   Generate an axes system with length of 1 on each axis.		     *
*****************************************************************************/
static struct PolygonStruct *GenAxesObjectPolylines(void)
{
    struct PolygonStruct *Pl, *PlHead;
    struct VertexStruct *V;

    Pl = PlHead = AllocPolygon(0, 0, NULL, NULL);		  /* X axis. */
    Pl -> V = V = AllocVertex(0, 0, NULL, NULL);
    V -> Pt[0] = 0.0;	V -> Pt[1] = 0.0;   V -> Pt[2] = 0.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 1.0;	V -> Pt[1] = 0.0;   V -> Pt[2] = 0.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 1.0;	V -> Pt[1] = 0.1;   V -> Pt[2] = 0.1;

    Pl -> Pnext = AllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;
    Pl -> V = V = AllocVertex(0, 0, NULL, NULL);
    V -> Pt[0] = 1.0;	V -> Pt[1] = 0.1;   V -> Pt[2] = 0.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 1.0;	V -> Pt[1] = 0.0;   V -> Pt[2] = 0.1;

    Pl -> Pnext = AllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;/* Y axis.*/
    Pl -> V = V = AllocVertex(0, 0, NULL, NULL);
    V -> Pt[0] = 0.0;	V -> Pt[1] = 0.0;   V -> Pt[2] = 0.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.0;	V -> Pt[1] = 1.0;   V -> Pt[2] = 0.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.0;	V -> Pt[1] = 1.0;   V -> Pt[2] = 0.06;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.04;	V -> Pt[1] = 1.0;   V -> Pt[2] = 0.1;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.0;	V -> Pt[1] = 1.0;   V -> Pt[2] = 0.06;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] =(-0.04);V -> Pt[1] = 1.0;   V -> Pt[2] = 0.1;

    Pl -> Pnext = AllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;/* Z axis.*/
    Pl -> V = V = AllocVertex(0, 0, NULL, NULL);
    V -> Pt[0] = 0.0;	V -> Pt[1] = 0.0;   V -> Pt[2] = 0.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.0;	V -> Pt[1] = 0.0;   V -> Pt[2] = 1.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.1;	V -> Pt[1] = 0.0;   V -> Pt[2] = 1.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.0;	V -> Pt[1] = 0.1;   V -> Pt[2] = 1.0;
    V -> Pnext = AllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
    V -> Pt[0] = 0.1;	V -> Pt[1] = 0.1;   V -> Pt[2] = 1.0;

    return PlHead;
}

/*****************************************************************************
*   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.	     *
*****************************************************************************/
struct ObjectStruct *GetObject(char *ObjName)
{
    int i = 0;
    struct ObjectStruct *PObj = GlblObjList;

    while (PObj) {
	if (strcmp(PObj->Name, ObjName) == 0) return PObj;
	PObj = PObj -> Pnext;
	if (i++ >= 1000)
	    FatalError("GetObject: Global Object list too big (>1000)\n");
    }
    return NULL;
}

/*****************************************************************************
*   Free Object - delete it from global active list and free all it memory   *
*****************************************************************************/
void FreeObject(ObjectStruct *PObj)
{
    DeleteObject(PObj, TRUE);
}

/*****************************************************************************
*   Delete object by its pointer - scans the object linear list.	     *
* Note the deleted record is free only if Free = TRUE.			     *
*****************************************************************************/
void DeleteObject(ObjectStruct *PObj, int Free)
{
    struct ObjectStruct *PObjScan = GlblObjList;

    if (GlblObjList == NULL) return;

    if (PObj == GlblObjList) {	   /* If it is the first one - special case. */
	GlblObjList = GlblObjList->Pnext;
	if (Free) MyFree((char *) PObj, OBJECT_TYPE);
	return;
    }

    while (PObjScan->Pnext) {
	if (PObj == PObjScan->Pnext) {
	    PObjScan->Pnext = PObjScan->Pnext->Pnext;/* Delete it from list. */
	    if (Free) MyFree((char *) PObj, OBJECT_TYPE);    /* 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(ObjectStruct *PObj)
{
    PObj -> Pnext = GlblObjList;
    GlblObjList = PObj;
}

/*****************************************************************************
*   Print some usefull info on the given object.			     *
*****************************************************************************/
void PrintObject(ObjectStruct *PObj)
{
    int i;
    char Line[LINE_LEN_LONG];

    switch(PObj->ObjType) {
	case UNDEF_OBJ:
	    sprintf(Line, "%-10s - Undefined type", PObj -> Name);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
	    break;
	case GEOMETRIC_OBJ:
	    sprintf(Line, "%-10s - Geometric type", PObj -> Name);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
#	    ifdef DEBUG
		{
		    PolygonStruct *Pl = PObj -> U.Pl;
		    VertexStruct *V, *VStart;

		    while (Pl != NULL) {
			printf("Polygon =\n");
			V = VStart = Pl -> V;
			do {
			    printf("        %-12.9lf %-12.9lf %-12.9lf\n",
				V -> Pt[0], V -> Pt[1], V -> Pt[2]);
			    V = V -> Pnext;
			} while (V != VStart && V != NULL);

			Pl = Pl -> Pnext;
		    }
		}
#	    endif /* DEBUG */
	    break;
	case NUMERIC_OBJ:
#	    ifdef DOUBLE
		sprintf(Line, "%-10s - Numeric   type = %-13lg",
#	    else
		sprintf(Line, "%-10s - Numeric   type = %-13g",
#	    endif /* DOUBLE */
						PObj -> Name, PObj -> U.R);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
	    break;
	case VECTOR_OBJ:
#	    ifdef DOUBLE
		sprintf(Line, "%-10s - Vector    type = %-13g %-13g %-13g",
#	    else
		sprintf(Line, "%-10s - Vector    type = %-13lg %-13lg %-13lg",
#	    endif /* DOUBLE */
			PObj -> Name,
			PObj -> U.Vec[0], PObj -> U.Vec[1], PObj -> U.Vec[2]);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
	    break;
	case MATRIX_OBJ:
	    sprintf(Line, "%-10s - Matrix    type", PObj -> Name);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);

	    for (i=0; i<4; i++) {
#		ifdef DOUBLE
		    sprintf(Line, "                %-13lg %-13lg %-13lg %-13lg",
#		else
		    sprintf(Line, "                %-13g %-13g %-13g %-13g",
#		endif /* DOUBLE */
		    PObj -> U.Mat[i][0], PObj -> U.Mat[i][1],
		    PObj -> U.Mat[i][2], PObj -> U.Mat[i][3]);

		WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
	    }
	    break;
	case STRING_OBJ:
	    sprintf(Line, "%-10s - String    type = \"%s\"",
			PObj -> Name, PObj -> U.Str);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
	    break;
	case OBJ_LIST_OBJ:
	    sprintf(Line, "%-10s - Object List type",
			PObj -> Name);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
	    i = 0;
	    sprintf(Line, "       ");
	    while (PObj -> U.PObjList[i] != NULL && i < MAX_OBJ_LIST) {
		sprintf(&Line[strlen(Line)], "%12s",
					PObj -> U.PObjList[i++] -> Name);
		if (i % 5 == 0) {
		    WndwInputWindowPutStrFS(Line, NO_COLOR, FALSE);
		    sprintf(Line, "       ");
		}
	    }
	    if (i % 5 != 0) WndwInputWindowPutStrFS(Line, NO_COLOR, FALSE);
	    break;
	default:
	    sprintf(Line, "%-10s - Obj type error, type = %d",
						PObj -> Name, PObj->ObjType);
	    WndwInputWindowPutStrFS(Line, NO_COLOR, TRUE);
	    break;
    }
}

/*****************************************************************************
*   Print some usefull info on all the given objects.			     *
*****************************************************************************/
void PrintObjectList(ObjectStruct *PObj)
{
    WndwInputWindowPutStrFS("", NO_COLOR, TRUE);

    while (PObj != NULL) {
	PrintObject(PObj);
	PObj = PObj -> Pnext;
    }
}

/*****************************************************************************
*   Generate one geometric object:					     *
*****************************************************************************/
void SetGeomObjectColor(ObjectStruct *PObj, RealType *Color)
{
    if (!IS_GEOM_OBJ(PObj))
	FatalError("Attempt to set color to a non geometric object");

    SET_OBJECT_COLOR(PObj, ((int) (*Color)));
}

/*****************************************************************************
*   Generate one geometric object:					     *
*****************************************************************************/
struct ObjectStruct *GenGeomObject(char *Name, PolygonStruct *Pl,
							ObjectStruct *Pnext)
{
    ObjectStruct *PObj;

    PObj = AllocObject(Name, GEOMETRIC_OBJ, Pnext);
    SET_OBJECT_COLOR(PObj, WHITE);		 /* Default color attribute. */
    RST_POLYLINE_GEOM_OBJ(PObj);	   /* Default - not polyline object. */

    PObj -> U.Pl = Pl;			     /* Link the union part of it... */

    return PObj;
}

/*****************************************************************************
*   Generate one numeric object:					     *
*****************************************************************************/
struct ObjectStruct *GenNumObject(char *Name, RealType *R, ObjectStruct *Pnext)
{
    ObjectStruct *PObj;

    PObj = AllocObject(Name, NUMERIC_OBJ, Pnext);

    PObj -> U.R = *R;			     /* Link the union part of it... */

    return PObj;
}

/*****************************************************************************
*   Generate one vector object:						     *
*****************************************************************************/
struct ObjectStruct *GenVecObject(char *Name, RealType *Vec0, RealType *Vec1,
					RealType *Vec2, ObjectStruct * Pnext)
{
    ObjectStruct *PObj;

    PObj = AllocObject(Name, VECTOR_OBJ, Pnext);

    PObj -> U.Vec[0] = *Vec0;		     /* Link the union part of it... */
    PObj -> U.Vec[1] = *Vec1;
    PObj -> U.Vec[2] = *Vec2;

    return PObj;
}

/*****************************************************************************
*   Generate one matrix object:						     *
*****************************************************************************/
struct ObjectStruct *GenMatObject(char *Name, MatrixType Mat,
							ObjectStruct *Pnext)
{
    int i, j;
    ObjectStruct *PObj;

    PObj = AllocObject(Name, MATRIX_OBJ, Pnext);

    for (i=0; i<4; i++)			     /* Link the union part of it... */
	for (j=0; j<4; j++) PObj -> U.Mat[i][j] = Mat[i][j];

    return PObj;
}

/*****************************************************************************
*   Routine to create a whole new copy of an object Src into Dest.	     *
* if Dest is NULL, new object is allocated, otherwise Dest itself is updated *
* If CopyAll then all the record is copied, otherwise, only its variant      *
* element (i.e. no Name/Pnext coping) is been copied.			     *
*****************************************************************************/
struct ObjectStruct *CopyObject(ObjectStruct *Dest, ObjectStruct *Src,
								int CopyAll)
{
    int index;
    char Line[LINE_LEN];

    if (Dest == NULL)
	Dest = AllocObject("", Src->ObjType, NULL);
    else {
	Dest->ObjType = Src->ObjType;
    }

    if (Dest == Src) return Dest;	/* Called with same object - ignore. */

    if (CopyAll) {
	strcpy(Dest->Name, Src->Name);
	Dest->Pnext = Src->Pnext;	 /* Maybe assigning NULL is better!? */
    }

    switch (Src->ObjType) {
	case GEOMETRIC_OBJ:
	    Dest->U.Pl = CopyPolygonList(Src->U.Pl);
	    COPY_GEOM_ATTRIB(Dest, Src);
	    break;
	case NUMERIC_OBJ:
	    Dest->U.R = Src->U.R;
	    break;
	case VECTOR_OBJ:
	    PT_COPY(Dest->U.Vec, Src->U.Vec);
	    break;
	case MATRIX_OBJ:
	    MAT_COPY(Dest->U.Mat, Src->U.Mat);
	    break;
	case STRING_OBJ:
	    strcpy(Dest->U.Str, Src->U.Str);
	    break;
	case OBJ_LIST_OBJ:
	    GEN_COPY(Dest->U.PObjList, Src->U.PObjList,
			MAX_OBJ_LIST * sizeof(ObjectStruct *));
	    index = 0;
	    while (index < MAX_OBJ_LIST &&
		   Dest -> U.PObjList[index] != NULL)
		Dest -> U.PObjList[index++] -> Count++;    /* Inc. # of ref. */
	    break;
	default:
	    sprintf(Line,
		"CopyObject Attemp to copy undefined object %s type %d\n",
		Src->Name, Src->ObjType);
	    FatalError(Line);
    }
    return Dest;
}

/*****************************************************************************
*   Routine to generate a new copy of an object polygon list.		     *
*****************************************************************************/
struct PolygonStruct *CopyPolygonList(PolygonStruct *Src)
{
    struct PolygonStruct *Phead, *Ptail;

    if (Src == NULL) return NULL;

    /* Prepare the header of the new polygon list: */
    Phead = Ptail = AllocPolygon(1, Src->Tags, CopyVList(Src->V), NULL);
    PLANE_COPY(Ptail -> Plane, Src -> Plane);
    Src = Src -> Pnext;

    while (Src != NULL) {
	Ptail -> Pnext = AllocPolygon(Src->Count, Src->Tags,
						CopyVList(Src->V), NULL);
	Ptail = Ptail -> Pnext;
	PLANE_COPY(Ptail -> Plane, Src -> Plane);
	Src = Src -> Pnext;
    }

    return Phead;
}

/*****************************************************************************
*   Routine to generate a new copy of a polygon vertices list.		     *
*****************************************************************************/
struct VertexStruct *CopyVList(VertexStruct *Src)
{
    struct VertexStruct *Phead, *Ptail, *SrcFirst = Src;

    if (Src == NULL) return NULL;

    /* Prepare the header of the new vertex list: */
    Phead = Ptail = AllocVertex(Src -> Count, Src -> Tags, NULL, NULL);
    PT_COPY(Phead -> Pt, Src -> Pt);
    Src = Src -> Pnext;

    while (Src != SrcFirst && Src != NULL) {
	Ptail -> Pnext = AllocVertex(Src -> Count, Src -> Tags, NULL, NULL);
	Ptail = Ptail -> Pnext;
	PT_COPY(Ptail -> Pt, Src -> Pt);

	Src = Src -> Pnext;
    }

    if (Src == SrcFirst) Ptail -> Pnext = Phead;/* Make vertex list circular.*/

    return Phead;
}
