/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Dynamic allocation module of "Irit" - the 3d polygonal solid modeller.   *
*****************************************************************************/

/* #define DEBUG		    Print more messages in free/allocating. */

#ifdef __MSDOS__
#include <alloc.h>
#include <time.h>
#include <graphics.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <string.h>
#include "program.h"
#include "ctrl-brk.h"
#include "graphgng.h"
#include "allocatl.h"
#include "allocatg.h"
#include "windowsg.h"

#ifndef __MSDOS__
#include "xgraphic.h"
#endif /* __MSDOS__ */

#ifdef __MSDOS__
/* Used to say when to update core left on screen: */
static time_t LastTimeAlloc = 0;
#endif /* __MSDOS__ */

/* Used for fast reallocation of most common object types: */
static VertexStruct *VertexFreedList = NULL;
static PolygonStruct *PolygonFreedList = NULL;
static ObjectStruct *ObjectFreedList = NULL;

/*****************************************************************************
* My Routine to	allocate dynamic memory. All program requests must call this *
* routine (no direct call to malloc). Dies if no memory.		     *
* In order to reduce the overhead of the allocation, basic objects are       *
* allocated in ALLOCATE_NUM number of objects blocks.			     *
*****************************************************************************/
char *MyMalloc(unsigned int Size, int Type)
{
    char *p;
    int i;

#   ifdef __MSDOS__
	if (time(NULL) != LastTimeAlloc) {
	    if (WasCtrlBrk) FatalError(NULL);	       /* Jump to main loop. */
	    WndwStatusWindowUpdate();		       /* Print free memory. */
	    LastTimeAlloc = time(NULL);
        }
#   endif /* __MSDOS__ */

    switch (Type) {
	case VERTEX_TYPE:
#	    ifdef DEBUG
		fprintf(stderr, "MyMalloc: Allocate vertex type");
#	    endif /* DEBUG */
	    if (VertexFreedList != NULL) {
		p = (char *) VertexFreedList;
		VertexFreedList = VertexFreedList -> Pnext;
	    }
	    else {
		VertexStruct *V;

		/* Allocate ALLOCATE_NUM objects, returns first one as new   */
		/* and chain together the rest of them into the free list.   */
		p = malloc(Size * ALLOCATE_NUM);
		V = (VertexStruct *) p;
		if (V != NULL) {
		    for (i=1; i<ALLOCATE_NUM-1; i++) V[i].Pnext = &V[i+1];
		    V[ALLOCATE_NUM-1].Pnext = NULL;
		    VertexFreedList = &V[1];
		}
	    }
	    break;
	case POLYGON_TYPE:
#	    ifdef DEBUG
		fprintf(stderr, "MyMalloc: Allocate polygon type");
#	    endif /* DEBUG */
	    if (PolygonFreedList != NULL) {
		p = (char *) PolygonFreedList;
		PolygonFreedList = PolygonFreedList -> Pnext;
	    }
	    else {
		PolygonStruct *V;

		/* Allocate ALLOCATE_NUM objects, returns first one as new   */
		/* and chain together the rest of them into the free list.   */
		p = malloc(Size * ALLOCATE_NUM);
		V = (PolygonStruct *) p;
		if (V != NULL) {
		    for (i=1; i<ALLOCATE_NUM-1; i++) V[i].Pnext = &V[i+1];
		    V[ALLOCATE_NUM-1].Pnext = NULL;
		    PolygonFreedList = &V[1];
		}
	    }
	    break;
	case OBJECT_TYPE:
#	    ifdef DEBUG
		fprintf(stderr, "MyMalloc: Allocate object type");
#	    endif /* DEBUG */
	    if (ObjectFreedList != NULL) {
		p = (char *) ObjectFreedList;
		ObjectFreedList = ObjectFreedList -> Pnext;
	    }
	    else {
		ObjectStruct *V;

		/* Allocate ALLOCATE_NUM objects, returns first one as new   */
		/* and chain together the rest of them into the free list.   */
		p = malloc(Size * ALLOCATE_NUM);
		V = (ObjectStruct *) p;
		if (V != NULL) {
		    for (i=1; i<ALLOCATE_NUM-1; i++) V[i].Pnext = &V[i+1];
		    V[ALLOCATE_NUM-1].Pnext = NULL;
		    ObjectFreedList = &V[1];
		}
	    }
	    break;
	default:
#	    ifdef DEBUG
		fprintf(stderr, "MyMalloc: Allocate undefined Type %d", Type);
#	    endif /* DEBUG */
	    p = malloc(Size);
	    break;
    }

#   ifdef DEBUG
	fprintf(stderr, " (Size = %d, ptr = %p)\n", Size, p);
#   endif /* DEBUG */

    if (p != NULL) return p;

    WndwInputWindowPutStr(
	"Not enough memory - program cannt continue", RED);
    GlblFatalError = TRUE;
    /* Long jump to main intraction loop - irit.c module */
    longjmp(LongJumpBuffer, 2);

    return NULL;			    /* Only makes warnings silent... */
}

/**********************************************************************
* Routine to free a given structure, which is not needed any more     *
* Note usually only object will be given directly to MyFree which     *
* recursively free its structure...				      *
* Also, it is perfectly legal to call with NULL to MyFree...	      *
**********************************************************************/
void MyFree(char *p, int Type)
{
    int index;
    char Line[LINE_LEN];

#   ifdef __MSDOS__
	if (time(NULL) != LastTimeAlloc) {
	    if (WasCtrlBrk) FatalError(NULL);	       /* Jump to main loop. */
	    WndwStatusWindowUpdate();		       /* Print free memory. */
	    LastTimeAlloc = time(NULL);
        }
#   endif /* __MSDOS__ */

    if (p == NULL) return;

#   ifdef DEBUG
	/* The following might fail if a record is freed without any usage!  */
	if (*((long *) p) == MAGIC_FREE_NUM)
	    FatalError("MyFree:Free the same record twice, dies");
	*((long *) p) = MAGIC_FREE_NUM;	      /* And set it for next time... */
#   endif /* DEBUG */

    switch (Type) {
	case VERTEX_TYPE:
#	    ifdef DEBUG
		fprintf(stderr, "MyFree: free vertex type\n");
#	    endif /* DEBUG */
	    FreeVertexList((VertexStruct *) p);
	    break;
	case POLYGON_TYPE:
#	    ifdef DEBUG
		fprintf(stderr, "MyFree: free polygon type\n");
#	    endif /* DEBUG */
	    FreePolygonList((PolygonStruct *) p);
	    break;
	case OBJECT_TYPE:
#	    ifdef DEBUG
		fprintf(stderr, "MyFree: free object type\n");
#	    endif /* DEBUG */
	    switch (((ObjectStruct *) p)->ObjType) {
		case UNDEF_OBJ:
		    break;
		case GEOMETRIC_OBJ:     /* The union points on Polygon list. */
		    MyFree((char *) (((ObjectStruct *) p)-> U.Pl),
								POLYGON_TYPE);
		    break;
		case NUMERIC_OBJ:
		case VECTOR_OBJ:
		case MATRIX_OBJ:
		case STRING_OBJ:
		    break;
		case OBJ_LIST_OBJ:  /* Need to dereference elements in list. */
		    index = 0;
		    while (index < MAX_OBJ_LIST &&
			   ((ObjectStruct *) p) -> U.PObjList[index] != NULL)
			((ObjectStruct *) p) -> U.PObjList[index++] -> Count--;
		    break;
		default:  	   /* Kill the program - something is WRONG! */
		    sprintf(Line,
			"MyFree: Attempt to free undefined Object type %d",
			((ObjectStruct *) p)->ObjType);
		    FatalError(Line);
		    break;
	    }
	    /* Add it to global freed object list: */
	    ((ObjectStruct *) p) -> Pnext = ObjectFreedList;
	    ObjectFreedList = (ObjectStruct *) p;
	    break;
	default:
#	    ifdef DEBUG
		fprintf(stderr, "MyFree: Free undefined Type %d\n", Type);
#	    endif /* DEBUG */
	    free(p);
	    break;
    }
}

/**********************************************************************
*   Routine to free a polygon list each consists of circular vertex   *
* list.								      *
**********************************************************************/
static void FreePolygonList(PolygonStruct *PPoly)
{
    struct PolygonStruct *Ptemp, *PPolyHead = PPoly;

#   ifdef DEBUG
	fprintf(stderr, "FreePolygonList: free polygon list\n");
#   endif /* DEBUG */

    while (PPoly) {
	FreeVertexList(PPoly -> V);
	Ptemp = PPoly;
	PPoly = PPoly -> Pnext;
    }

    /* Now chain this new list to the global freed polygon list: */
    Ptemp -> Pnext = PolygonFreedList;
    PolygonFreedList = PPolyHead;
}

/**********************************************************************
*   Routine to free a circular vertex list - one polygon contour.     *
**********************************************************************/
static void FreeVertexList(VertexStruct *VFirst)
{
    struct VertexStruct *V = VFirst, *Vtemp;

#   ifdef DEBUG
	fprintf(stderr, "FreeVertexList: free vertex list\n");
#   endif /* DEBUG */

    if (VFirst == NULL) return;

    do {
	Vtemp = V;
	V = V -> Pnext;
    }
    while (V != NULL && V != VFirst); /* Both - circular or NULL terminated. */

    /* Now chain this new list to the global freed vertex list: */
    Vtemp -> Pnext = VertexFreedList;
    VertexFreedList = VFirst;
}

/**********************************************************************
* Allocate one Vertex Structure:				      *
**********************************************************************/
struct VertexStruct * AllocVertex(ByteType Count, ByteType Tags,
				PolygonStruct * PAdj, VertexStruct * Pnext)
{
    struct VertexStruct *p;

    p = (VertexStruct *) MyMalloc(sizeof(VertexStruct), VERTEX_TYPE);

    p -> Count = Count;
    p -> Tags = Tags;
    p -> Pnext = Pnext;
    p -> PAdj = PAdj;

    return p;
}

/**********************************************************************
* Allocate one Polygon Structure:				      *
**********************************************************************/
struct PolygonStruct * AllocPolygon(ByteType Count, ByteType Tags,
				VertexStruct * V, PolygonStruct * Pnext)
{
    struct PolygonStruct *p;

    p = (PolygonStruct *) MyMalloc(sizeof(PolygonStruct), POLYGON_TYPE);

    p -> Plane[0] = p -> Plane[1] = p -> Plane[2] = p -> Plane[3] = 0.0;
    p -> Count = Count;
    p -> Tags = Tags;
    p -> V = V;
    p -> Pnext = Pnext;

    return p;
}

/**********************************************************************
* Allocate one Object Structure:				      *
**********************************************************************/
struct ObjectStruct * AllocObject(char *Name, ByteType ObjType,
						 ObjectStruct * Pnext)
{
    struct ObjectStruct *p;

    p = (ObjectStruct *) MyMalloc(sizeof(ObjectStruct), OBJECT_TYPE);

    strcpy(p -> Name, Name);
    p -> ObjType = ObjType;
    p -> Count = 0;
    p -> Pnext = Pnext;
    p -> U.Pl = NULL;				/* To be on the safe size... */

    return p;
}
