/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmcmpmgr.c,v 1.12 2008/11/23 15:10:40 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.12 $
 * $Log: rmcmpmgr.c,v $
 * Revision 1.12  2008/11/23 15:10:40  wes
 * private_rmNodeDelete: use rmArrayDelete to free up RMarray of children
 *
 * Revision 1.11  2005/06/15 02:10:40  wes
 * Added initial support for application-settable defaults. Apps
 * will use rmSetEnum/rmGetEnum to set or get defaults that are RMenums.
 * The first round of variables that can be set/get by apps are
 * RMnode traversal masks assigned to new scene graph nodes by rmNodeNew.
 *
 * Revision 1.10  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.9  2005/03/19 17:18:45  wes
 * For RM_MARKERS2D primitives, add call to delete prim-specific data.
 *
 * Revision 1.8  2005/02/27 19:34:04  wes
 * Added support for application supplied texture object IDs and display lists.
 *
 * Revision 1.7  2005/02/19 16:34:21  wes
 * Distro sync and consolidation.
 * Minor code rearrangement, remove dead code.
 *
 * Revision 1.6  2005/01/23 17:08:25  wes
 * Copyright updated to 2005.
 * Updates to support access and use of extensions; multitexturing on all
 * platforms, use of 3d texture extension to realize implementation of
 * volume rendering and related functions on Windows platforms.
 *
 * Revision 1.5  2004/01/16 16:43:24  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.4  2003/04/05 13:58:29  wes
 * Fixed two memory leaks: free CM mutexes upon exit, and free the fbClear struct.
 *
 * Revision 1.3  2003/02/02 17:50:57  wes
 * Added bounding boxes to RMprimitives, as a supplement to node-level bboxes.
 * The RMprimitive level bboxes are needed for the retained-mode CR work.
 *
 * Revision 1.2  2003/02/02 02:07:15  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.9  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.8  2002/09/17 14:20:03  wes
 * Added new routine rmComponentManagerPrintStatus(). Added code inside
 * private_rmDeleteComponentManager() that will free all pages of memory
 * in the object pool - this is needed in conjunction with the new
 * paging model.
 *
 * Revision 1.7  2002/09/05 15:05:03  wes
 * more thorough debug info on realloc
 *
 * Revision 1.6  2002/08/29 22:20:32  wes
 *
 * Massive upgrade to accommodate dynamic object reallocation within
 * the component manager, and within the context cache. Use the
 * debug #define DEBUG_LEVEL DEBUG_REALLOC_TRACE to get a printf
 * whenever a realloc occurs. With this upgrade, there are no
 * OpenRM limits on the size of the scene graph. There will be external
 * limits, such as the amount of RAM and the amount of space available
 * to your OpenGL implementation.
 *
 * Revision 1.5  2002/08/19 00:54:27  wes
 * Added API entries for new experiemental routines to get and set
 * the size of the OpenRM component manager object pool.
 *
 * Revision 1.4  2002/04/30 19:30:20  wes
 * rmImageDelete will automatically call rmImageSetVismap(img, NULL) to
 * induce release of any memory associated with a vis colormap in an
 * RMimage.
 *
 * Revision 1.3  2001/07/15 17:17:11  wes
 * Added code to manage "normalizeNormals" render property - fixes a
 * 4-byte memory leak.
 *
 * Revision 1.2  2001/03/31 17:12:38  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.1  2000/12/03 22:32:59  wes
 * Initial entry.
 *
 */

#include <rm/rm.h>
#include "rmprivat.h"

static RMcompList *private_rmInitComponentMetaList(int objCount);
static RMenum      private_rmReallocComponentMgrListAndPool(RMcompMgrHdr *h);
static void        private_rmComponentManagerStatus(void);


/*
 * ----------------------------------------------------
 * @Name rmComponentManagerPrintStatus
 @pstart
 void rmComponentManagerPrintStatus( void )
 @pend

 @astart
 none.
 @aend

 @dstart

 Use this routine to display the current free/alloc status of objects
 managed by the component manager. This routine will invoke rmNotice() to
 display the number of allocated and free objects managed by the
 component manager. This routine is useful in tracking down memory leaks
 in applications.

 This routine is new, and experimental, in version 1.4.3. It may be
 subsumed by more general utilities in the future.
 
 @dend
 * ----------------------------------------------------
 */
void
rmComponentManagerPrintStatus(void)
{
    private_rmComponentManagerStatus();
}


/* PRIVATE */
RMcompMgrHdr *
private_rmInitComponentManager(int objCount,
			       int objSize)
{
    RMcompMgrHdr *t;
    
    t = (RMcompMgrHdr *)malloc(sizeof(RMcompMgrHdr));

    if (t == NULL)
	return(NULL);

    t->numPages = 1;
    
    t->objectPool = (void **)malloc(sizeof(void *));
    t->objectPool[0] = (void *)malloc(objCount * objSize);
    
    t->metaListHead = private_rmInitComponentMetaList(objCount);
    
    t->numAlloc = 0;

    t->startFree = 0;
    t->startAlloc = -1;
    
    t->numFree = objCount;
    t->currentPoolSize = objCount;
    t->componentSize = objSize;

    /* create a mutex in unlocked state */

    t->guard = rmMutexNew(RM_MUTEX_UNLOCK); 

    return(t);
}

/* PRIVATE */
static RMcompList *
private_rmInitComponentMetaList(int objCount)
{
    RMcompList *t;
    int i;

    t = (RMcompList *)malloc(sizeof(RMcompList)*objCount);
    if (t == NULL)
	return(NULL);
    
    for (i=0;i<objCount;i++)
    {
	t[i].objIndx = i;
	t[i].myIndx = i;
	if (i==0)
	    t[i].prevIndx = -1;
	else
	    t[i].prevIndx = i-1;
	
	if (i == objCount-1)
	    t[i].nextIndx = -1;
	else
	    t[i].nextIndx = i+1;
    }
    return(t);
}

/* PRIVATE */
static RMenum 
private_rmReallocComponentMgrListAndPool(RMcompMgrHdr *h)
{
    /*
     * 8/29/02 - wes. implementation of dynamic object pools.
     * this routine will realloc the MetaList in a component manager,
     * and will add a page of new memory to the objectPool.
     */
    RMcompList *oldList, *t;
    int i;
    int newSize;
    int newPoolSize, oldPoolSize;

    /* first,  compute new size of alloc list  */
    newSize = h->currentPoolSize * sizeof(RMcompList);
    newSize += (NUM_ITEMS_PER_PAGE) * sizeof(RMcompList);
    
    oldList = h->metaListHead;
    t = (RMcompList *)realloc((void *)oldList, newSize);

    if (t == NULL)
	return(RM_WHACKED);

    /* update the pointer in the header */
    h->metaListHead = t;

    /*
     * realloc succeeded if we're here. initialize the new hunk of
     * memory.
     */
    oldPoolSize = h->currentPoolSize;
    newPoolSize = oldPoolSize + NUM_ITEMS_PER_PAGE;

    /*
     * initialize the new stuff. we don't need to adjust the "pointers"
     * in any of the old stuff because we are maintaining two virtual
     * lists within one pile of stuff. because we're here, we're out of
     * free objects.
     */
    for (i=oldPoolSize; i < newPoolSize; i++)
    {
	t[i].objIndx = i;
	t[i].myIndx = i;

	if (i == oldPoolSize)
	    t[i].prevIndx = -1;
	else
	    t[i].prevIndx = i-1;
	
	if (i == newPoolSize-1)
	    t[i].nextIndx = -1;
	else
	    t[i].nextIndx = i+1;
    }


    /*
     * now, update counters in the RMcompMgrHdr to reflect new values.
     */
    h->numFree += NUM_ITEMS_PER_PAGE;
    h->startFree = oldPoolSize;
    h->currentPoolSize += NUM_ITEMS_PER_PAGE;

    /*
     * OK - now we're done futzing with the alloc/free lists. now we
     * need to actually increase the size of the objectPool. This is
     * accomplished by adding another "page" of memory. we can't just
     * blindly realloc the objectPool because the application probably
     * has object pointers into that hunk of memory, and a realloc
     * might (probably) causes memory to be moved around, and then the
     * app's pointers would be stale. we use a paging scheme as a
     * compromise..application performance isn't affected at all; we
     * incur only a small amount of additional overhead when the
     * application invokes a new/delete operation.
     */

    h->objectPool = (void **)realloc(h->objectPool, sizeof(void *)*(h->numPages + 1));
    h->objectPool[h->numPages] = (void *)malloc(NUM_ITEMS_PER_PAGE * h->componentSize);
    memset(h->objectPool[h->numPages], 0, NUM_ITEMS_PER_PAGE * h->componentSize);
    
    h->numPages += 1;
		  
    return RM_CHILL;
}

/* PRIVATE */
static int
private_rmFreeToAlloc(RMcompMgrHdr *h, /* input - component mgr hdr */
		      const char *msg) /* message to bark upon error */
{
    /*
     * move an item from the head of the free list to the start
     * of the alloc list. this routine will take care of all
     * bookkeeping: updating counter of free and alloc pools,
     * updating pointers in lists, etc.
     *
     * upon successful completion, the index of the moved
     * entry is returned. upon failure, a -1 is returned. failure
     * occurs when the list of free objects has been depleted.
     */
    int freeIndx;
    RMcompList *firstFree;

    /*
     * first, make sure we have exclusive use of this pool.
     */
    if (rmMutexLock(h->guard) == RM_WHACKED)
    {
	/*
	 * if we arrive here, something is really, really wrong.
	 * the rmMutexLock() call is supposed to do a blocking wait
	 * and then grab the mutex once it's available. If we arrive
	 * here, then there's some type of fatal error deep in
	 * pthreads. To date, this branch of code has never been
	 * executed in millions of program runs (as far as I know).
	 */
	rmError(" problem locking mutex in component manager. \n");
	exit(-1);
    }
    
    if (h->numAlloc+1 > h->currentPoolSize)
    {
#if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE)
	printf("component manager execing realloc: %s. oldsize = %d ", msg,h->currentPoolSize);
#endif
	/* execute realloc on metalist */
	if (private_rmReallocComponentMgrListAndPool(h) != RM_CHILL)
	{
	    rmError(msg);
	    rmMutexUnlock(h->guard); /* wes 1/21/02 */
	    return(-1);
	}
#if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE)
	printf("newsize = %d \n", h->currentPoolSize);
	fflush(stdout);
#endif
    }
    
    /* take top of free list */
    /* don't need to update prevIndex in free list */
    freeIndx = h->startFree;
    firstFree = h->metaListHead + freeIndx;

    /* update the start of the free list.. */
    h->startFree = firstFree->nextIndx;

    /* and the count of free items. */
    h->numFree -= 1;
    
    /* update the alloc list. when we update the alloc list, we put
     the newly alloc'ed item at the head of the list. it's next pointer
     will be the old head of list, and it's prev pointer will be -1. */
    {
	RMcompList *oldFirstAlloc, *newAlloc;

	newAlloc = firstFree;

	/* add the newly alloced one to the front of the free list */
	newAlloc->nextIndx = h->startAlloc;
	h->startAlloc = newAlloc->myIndx;

	/* so there's no prev index */
	newAlloc->prevIndx = -1;
	
	if (newAlloc->nextIndx != -1)
        {
	    /* something was on the list, update it's prev pointer
	     to point to the newly added thing.*/
	    
	    oldFirstAlloc = h->metaListHead + newAlloc->nextIndx;
	    oldFirstAlloc->prevIndx = newAlloc->myIndx;
	}
	h->numAlloc += 1;
    }

    rmMutexUnlock(h->guard);
    
    return (freeIndx);
}

/* PRIVATE */
void
private_rmAllocToFree(RMcompMgrHdr *h, /* main component mgr to use */
		      unsigned int indx) /* index of item to move from alloc to free */
{
    RMcompList *toFree;

    toFree = h->metaListHead;
    toFree += indx;

    /* first, fix up the alloc list. */

    /* fix up "next" pointer of previous alloc list item */
    if (toFree->prevIndx == -1) /* it's at the start of the list */
    {
	h->startAlloc = toFree->nextIndx;
    }
    else
    {
	RMcompList *prev;
	prev = h->metaListHead + toFree->prevIndx;
	prev->nextIndx = toFree->nextIndx;
    }

    /* fix up "prev" pointer of next alloc item list */
    if (toFree->nextIndx != -1)
    {
	RMcompList *next;
	next = h->metaListHead + toFree->nextIndx;
	next->prevIndx = toFree->prevIndx;
    }

    /* now, insert the toFree entry back into the free list - always
     put it at the top of the free list (no prev pointers) */

    toFree->nextIndx = h->startFree;
    h->startFree = toFree->myIndx;

    h->numAlloc -= 1;
    h->numFree += 1;

}

/* PRIVATE */
RMimage *
private_rmImageNew(void)
{
    /*
     * 
     */
    extern RMcompMgrHdr *global_RMimagePool;
    int freeIndx;
    int pageNum, offset;
    RMimage *t;

    if (RM_ASSERT(global_RMimagePool, "Please call rmInit() prior to creating RMimage objects. \n") == RM_WHACKED)
	return(NULL);

    freeIndx = private_rmFreeToAlloc(global_RMimagePool,"private_rmImageNew() - all RMimage objects have been used.");

    if (freeIndx == -1)
	return(NULL);

    pageNum = rmCompManagerGetPage(freeIndx);
    offset = rmCompManagerGetOffset(freeIndx);
	
    /* return RMimage handle to caller */
    t = (RMimage *)(global_RMimagePool->objectPool[pageNum]);
    t += offset;
    t->compListIndx = freeIndx;

#if 0
    /* pre-paging code */
    t = (RMimage *)(global_RMimagePool->objectPool);
    t += freeIndx;
    t->compListIndx = freeIndx;
#endif
    
    return (t);
}

/* PRIVATE */
RMenum
private_rmImageDelete(RMimage *r)
{
    extern RMcompMgrHdr *global_RMimagePool;
    int indx;

    /* first, free up any pixel data associated with the RMimage object */
    
    if (private_rmImageGetCopyFlag(r) == RM_COPY_DATA)
	free((void *)(r->pixeldata));
    else
    {
	void (*appfunc)(void *);

	appfunc = r->appfreefunc;
	if (appfunc != NULL)
	    (*appfunc)((void *)(r->pixeldata));
    }

    /* assign a NULL vismap object to the RMimage - this will free any
       pre-existing RMvisMap object that may be associated with the RMimage */
    rmImageSetVismap(r, NULL);

    /* update the free list - the newly freed item goes to the
       start of the free list */
    indx = r->compListIndx;
    /* validity checks on indx? */
    
    private_rmAllocToFree(global_RMimagePool, indx);

    return(RM_CHILL);
}


/* PRIVATE */
void
private_rmDeleteComponentManager(RMcompMgrHdr *t)
{
    int i;
    
    if (RM_ASSERT(t,"private_rmDeleteComponentManager error: the input RMcompMgrHdr is NULL! \n") == RM_WHACKED)
	return;

    for (i=0;i<t->numPages;i++)
    {
	if ((t->objectPool[i]) == NULL)
	    rmWarning("private_rmDeleteComponentManager() warning: a page in the objectPool is unexpectedly not NULL!");
	else
        {
	    free((void *)(t->objectPool[i]));
	    t->objectPool[i] = NULL;
	}
    }

    rmMutexDelete(t->guard);
    
    free((void *)(t->objectPool));
    free((void *)(t->metaListHead));
    free((void *)t);
}

/* PRIVATE */
RMnode *
private_rmNodeNew(void)
{
    /*
     * 
     */
    extern RMcompMgrHdr *global_RMnodePool;
    int freeIndx;
    RMnode *t = NULL;

    if (RM_ASSERT(global_RMnodePool, "Please call rmInit() prior to creating RMnode objects. \n") == RM_WHACKED)
	return(NULL);
    
    freeIndx = private_rmFreeToAlloc(global_RMnodePool,"private_rmNodeNew() - all RMnode objects have been used.");

    if (freeIndx == -1)
	return(NULL);

    /* return RMnode handle to caller */

    {
	int pageNum = rmCompManagerGetPage(freeIndx);
	int offset = rmCompManagerGetOffset(freeIndx);
	
	t = (RMnode *)(global_RMnodePool->objectPool[pageNum]);
	t += offset;
	t->compListIndx = freeIndx;
    }

    return (t);
}

/* PRIVATE */
RMenum
private_rmNodeDelete(RMnode *r)
{
    extern RMcompMgrHdr *global_RMnodePool;
    int indx;

    /* first, free up any data associated with the RMnode object */
    
    /* need: check to see if refcount is zero */
    if (private_rmNodeGetRefcount(r) != 0)
	return(RM_WHACKED);
    
    if ((r->clientData) && (r->clientDataFreeFunc))
    {
	void (*cfunc)();

	cfunc = r->clientDataFreeFunc;
	(*cfunc)(r, rmNodeGetClientData(r));
    }
    
    if (r->scene_parms != NULL) /* free up scene parms */
    {
	int i;
	
	if (r->scene_parms->viewport)
	    free((void *)(r->scene_parms->viewport));

	if (r->scene_parms->camera3d)
	    rmCamera3DDelete(r->scene_parms->camera3d);

	if (r->scene_parms->camera2d)
	    rmCamera2DDelete(r->scene_parms->camera2d);

	if (r->scene_parms->haveAnyTextures == RM_TRUE)
	{
	    int i;
	    for (i=0; i <= RM_MAX_MULTITEXTURES; i++)
		if (r->scene_parms->textures[i] != NULL)
		    rmTextureDelete(r->scene_parms->textures[i],RM_TRUE);
	}

	if (r->scene_parms->cp0 != NULL)
	    rmClipPlaneDelete(r->scene_parms->cp0);

	if (r->scene_parms->cp1 != NULL)
	    rmClipPlaneDelete(r->scene_parms->cp1);

	if (r->scene_parms->cp2 != NULL)
	    rmClipPlaneDelete(r->scene_parms->cp2);

	if (r->scene_parms->cp3 != NULL)
	    rmClipPlaneDelete(r->scene_parms->cp3);

	if (r->scene_parms->cp4 != NULL)
	    rmClipPlaneDelete(r->scene_parms->cp4);

	if (r->scene_parms->cp5 != NULL)
	    rmClipPlaneDelete(r->scene_parms->cp5);

	if (r->scene_parms->lmodel)
	    rmLightModelDelete(r->scene_parms->lmodel);

	for (i = 0; i < RM_MAX_LIGHTS; i++)
	    if (r->scene_parms->lightSources[i] != NULL)
		rmLightDelete(r->scene_parms->lightSources[i]);

	if (r->scene_parms->textProps != NULL)
	    rmTextPropsDelete(r->scene_parms->textProps);

	free((void *)(r->scene_parms));
    }
    if (r->sprops != NULL) /* free up scene props */
    {
	if (r->sprops->ambient_color != NULL)
	    free((void *)(r->sprops->ambient_color));

	if (r->sprops->diffuse_color != NULL)
	    free((void *)(r->sprops->diffuse_color));

	if (r->sprops->specular_color != NULL)
	    free((void *)(r->sprops->specular_color));

	if (r->sprops->unlit_color != NULL)
	    free((void *)(r->sprops->unlit_color));

	if (r->sprops->specular_exponent != NULL)
	    free((void *)(r->sprops->specular_exponent));

	if (r->sprops->opacity != NULL)
	    free((void *)(r->sprops->opacity));

	free((void *)(r->sprops));
    }
    if (r->rprops != NULL)	/* free up rendermode props */
    {
	if (r->rprops->shademodel != NULL)
	    free((void *)(r->rprops->shademodel));
	
	if (r->rprops->poly_mode_face)
	    free((void *)(r->rprops->poly_mode_face));

	if (r->rprops->poly_mode_drawstyle)
	    free((void *)(r->rprops->poly_mode_drawstyle));

	if (r->rprops->cull_mode)
	    free((void *)(r->rprops->cull_mode));
	
	if (r->rprops->front_face)
	    free((void *)(r->rprops->front_face));

	if (r->rprops->pointsize)
	    free((void *)(r->rprops->pointsize));
	
	if (r->rprops->linewidth)
	    free((void *)(r->rprops->linewidth));
	
	if (r->rprops->linestyle)
	    free((void *)(r->rprops->linestyle));

	if (r->rprops->normalizeNormals)
	    free((void *)(r->rprops->normalizeNormals));

	free((void *)(r->rprops));
    }

    if (r->fbClear != NULL)
    {
	if (r->fbClear->bgColor)
	    free((void *)(r->fbClear->bgColor));

	if (r->fbClear->bgImageTile)
	    rmImageDelete(r->fbClear->bgImageTile);

	if (r->fbClear->depthValue != NULL)
	    free((void *)(r->fbClear->depthValue));

	if (r->fbClear->depthImage != NULL)
	    rmImageDelete(r->fbClear->depthImage);

	free((void *)(r->fbClear));
    }
	
    if (r->transforms != NULL)
    {
	void private_rmNodeTransformsDelete(internals_RMtransformationStruct *t);
	
	private_rmNodeTransformsDelete(r->transforms);
    }
    {	
	int nprims, i;

	/* now delete all the prims */
	nprims = rmNodeGetNumPrims(r);
	for (i=0; i < nprims; i++)
	    rmPrimitiveDelete((RMprimitive *)rmNodeGetPrimitive(r, i));
	free((void *)r->prims);
    }
#if 0
    /* 1.6.2 and earlier */
    if (r->children)
	free((void *)r->children);
#endif
    /* 1.8.0 */
    if (private_rmNodeGetNumChildren(r) > 0)
	rmArrayDelete(&(r->childrenArray));

    /* update the free list - the newly freed item goes to the
       start of the free list */
    indx = r->compListIndx;
    /* validity checks on indx? */
    
    private_rmAllocToFree(global_RMnodePool, indx);

    return(RM_CHILL);
}

/* PRIVATE */
RMprimitive *
private_rmPrimitiveNew(void)
{
    /*
     * 
     */
    extern RMcompMgrHdr *global_RMprimitivePool;
    int freeIndx;
    RMprimitive *t;

    if (RM_ASSERT(global_RMprimitivePool, "Please call rmInit() prior to creating RMprimitive objects. \n") == RM_WHACKED)
	return(NULL);
    
    freeIndx = private_rmFreeToAlloc(global_RMprimitivePool,"private_rmPrimitiveNew() - all RMprimitive objects have been used.");

    if (freeIndx == -1)
	return(NULL);

    /* return RMprimitive handle to caller */
    {
	int pageNum = rmCompManagerGetPage(freeIndx);
	int offset = rmCompManagerGetOffset(freeIndx);
	
	t = (RMprimitive *)(global_RMprimitivePool->objectPool[pageNum]);
	t += offset;
	t->compListIndx = freeIndx;
    }

    t->cacheKey = t->utilCacheKey = RM_CACHEKEY_UNINITIALIZED_VALUE;
   
    return (t);
}

/* PRIVATE */
RMenum
private_rmPrimitiveDelete(RMprimitive *r)
{
    extern RMcompMgrHdr *global_RMprimitivePool;
    int indx;

    /* first, free up any data associated with the RMprimitive object */
    
    if ((r->clientData) && (r->clientDataFreeFunc))
    {
	void (*cfunc)();

	cfunc = r->clientDataFreeFunc;
	(*cfunc)(r, rmPrimitiveGetClientData(r));
    }

    /* delete the bbox, if present */
    if (r->bmin)
	free((void *)(r->bmin));
    
    if (r->bmax)
	free((void *)(r->bmax));

    /* free all blobs */
    {
	int                  i;
	void               (*freefunc)();
	RMprimitiveDataBlob *b;

	for (i = 0; i < RM_MAXBLOBS_PER_PRIMITIVE; i++)
        {
	    b = &(r->blobs[i]);

	    if (b->appfreefunc != NULL)
	    {
		freefunc = b->appfreefunc;
		(*freefunc)(b->ptr);
	    }
	    else 
		if (b->ptr != NULL)
		    free(b->ptr);
	}
    }

    /* now free the weird stuff that's wedged into the cracks.. */
    if ((r->type == RM_BITMAP) || (r->type == RM_INDEXED_BITMAP))
    {
        int        i;
	RMbitmap **bmp_list, *bmp;
	
	bmp_list = (RMbitmap **)(r->p1);

	/* flags1 is unsigned */
	for (i = 0; i < (int)(r->flags1); i++)
	{
	    bmp = bmp_list[i];
	    rmBitmapDelete(bmp);
	}
	free((void *)bmp_list);
    }
    else if ((r->type == RM_TEXT) || (r->type == RM_INDEXED_TEXT))
    {
	int          nstrings, i;
	RMtextPrim *tp;
		
	tp = (RMtextPrim *)(r->p1);
	nstrings = r->flags1;
		
	for (i = 0; i < nstrings; i++)
	    free((void *)(tp[i].string));

	free((void *)(r->p1));
    }
    else if (r->type == RM_SPRITE)
    {
	if (r->p1)
	{
	    int      i, n;
	    RMimage *img, **sprite_list;
	    
	    private_rmPrimitiveGetItem(r, RM_PRIMITIVE_SPRITES, &n, (void **)&sprite_list);

	    for (i = 0; i < n; i++)
	    {
		img = sprite_list[i];
		rmImageDelete(img);
	    }

	    free((void *)(r->p1));
	    r->flags1 = 0;
	}
    }
    else if (r->type == RM_OCTMESH)
    {
	/* free octmesh-specific stuff */
	private_rmOctmeshPrimitiveFree(r);
    }
    else if (r->type == RM_MARKERS2D) /* thanks Alex, 3/2005 */
    {
	if (r->p1)
	    rmInternalMarker2DDelete(r->p1);
    }

    /* delete blobs */
    free((void *)r->blobs);

    /* update the free list - the newly freed item goes to the
       start of the free list */
    indx = r->compListIndx;
    /* validity checks on indx? */
    
    private_rmAllocToFree(global_RMprimitivePool, indx);

    return(RM_CHILL);
}

/* PRIVATE */
RMtexture *
private_rmTextureNew(void)
{
    /*
     * 
     */
    extern RMcompMgrHdr *global_RMtexturePool;
    int freeIndx;
    RMtexture *t;

    if (RM_ASSERT(global_RMtexturePool, "Please call rmInit() prior to creating RMtexture objects. \n") == RM_WHACKED)
	return(NULL);
    
    freeIndx = private_rmFreeToAlloc(global_RMtexturePool,"private_rmTextureNew() - all RMtexture objects have been used.");

    if (freeIndx == -1)
	return(NULL);

    /* return RMtexture handle to caller */
    {
	int pageNum = rmCompManagerGetPage(freeIndx);
	int offset = rmCompManagerGetOffset(freeIndx);
	
	t = (RMtexture *)(global_RMtexturePool->objectPool[pageNum]);
	t += offset;
	t->compListIndx = freeIndx;
    }
    
    return (t);
}

/* PRIVATE */
RMenum
private_rmTextureDelete(RMtexture *r)
{
    extern RMcompMgrHdr *global_RMtexturePool;
    int indx;

    /* update the free list - the newly freed item goes to the
       start of the free list */
    indx = r->compListIndx;
    /* validity checks on indx? */
    
    private_rmAllocToFree(global_RMtexturePool, indx);

    return(RM_CHILL);
}

/* PRIVATE */
void
private_rmPrimitiveSetCacheKey(RMprimitive *p)
{
    /* we could put a mutex here to force serial access. 10/2000 */
    p->cacheKey = private_rmGetNewCacheKey();
}

/* PRIVATE */
void
private_rmTextureSetIDCacheKey(RMtexture *t)
{
    /* we could put a mutex here to force serial access. 10/2000 */
    t->cacheKeyID = private_rmGetNewCacheKey();
}

/* PRIVATE */
void
private_rmTextureSetDataCacheKey(RMtexture *t)
{
    /* we could put a mutex here to force serial access. 10/2000 */
    t->cacheKeyData = private_rmGetNewCacheKey();
}

/* PRIVATE */
RMtextProps *
private_rmTextPropsNew(void)
{
    /*
     * 
     */
    extern RMcompMgrHdr *global_RMtextPropsPool;
    int freeIndx;
    RMtextProps *t;

    if (RM_ASSERT(global_RMtextPropsPool, "Please call rmInit() prior to creating RMtextProps objects. \n") == RM_WHACKED)
	return(NULL);

    freeIndx = private_rmFreeToAlloc(global_RMtextPropsPool,"private_rmTextPropsNew() - all RMtextProps objects have been used.");

    if (freeIndx == -1)
	return(NULL);

    /* return RMtextProps handle to caller */

    {
	int pageNum = rmCompManagerGetPage(freeIndx);
	int offset = rmCompManagerGetOffset(freeIndx);
	
	t = (RMtextProps *)(global_RMtextPropsPool->objectPool[pageNum]);
	t += offset;
	t->compListIndx = freeIndx;
    }
#if 0    
    t = (RMtextProps *)(global_RMtextPropsPool->objectPool);
    t += freeIndx;
    t->compListIndx = freeIndx;
#endif
    return (t);
}

/* PRIVATE */
RMenum
private_rmTextPropsDelete(RMtextProps *r)
{
    extern RMcompMgrHdr *global_RMtextPropsPool;
    int indx;

    /* update the free list - the newly freed item goes to the
       start of the free list */
    indx = r->compListIndx;
    /* validity checks on indx? */
    
    private_rmAllocToFree(global_RMtextPropsPool, indx);

    return(RM_CHILL);
}

/* PRIVATE */
void
private_rmComponentManagerStatus(void)
{
    extern RMcompMgrHdr *global_RMimagePool;
    extern RMcompMgrHdr *global_RMprimitivePool;
    extern RMcompMgrHdr *global_RMnodePool;
    extern RMcompMgrHdr *global_RMtexturePool;
    extern RMcompMgrHdr *global_RMtextPropsPool;
    RMcompMgrHdr *t;

    char buf[2048], buf2[256];

    sprintf(buf,"\tComponent Manager Status \n");
    strcat(buf,"\t\tAlloc \tFree \tPoolSize \n");

    t = global_RMimagePool;
    sprintf(buf2,"RMimages\t%d \t%d \t%d \n",t->numAlloc, t->numFree, t->currentPoolSize);
    strcat(buf, buf2);
    
    t = global_RMprimitivePool;
    sprintf(buf2,"RMprimitives\t%d \t%d \t%d \n",t->numAlloc, t->numFree, t->currentPoolSize);
    strcat(buf, buf2);
    
    t = global_RMnodePool;
    sprintf(buf2,"RMnodes  \t%d \t%d \t%d \n",t->numAlloc, t->numFree, t->currentPoolSize);
    strcat(buf, buf2);
    
    t = global_RMtexturePool;
    sprintf(buf2,"RMtextures\t%d \t%d \t%d \n",t->numAlloc, t->numFree, t->currentPoolSize);
    strcat(buf, buf2);
    
    t = global_RMtextPropsPool;
    sprintf(buf2,"RMtextProps\t%d \t%d \t%d \n",t->numAlloc, t->numFree, t->currentPoolSize);
    strcat(buf, buf2);

    rmNotice(buf);
}
/* EOF */
