/*
 * 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: rmserial.c,v 1.10 2008/11/23 15:15:51 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.10 $
 * $Log: rmserial.c,v $
 * Revision 1.10  2008/11/23 15:15:51  wes
 * Changes to support RMnode's use of RMarrays to hold child pointers
 *
 * Revision 1.9  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.8  2005/02/19 16:44:25  wes
 * Distro sync and consolidation.
 * Better RMstate initialization and memory management.
 *
 * Revision 1.7  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.6  2004/09/28 00:48:57  wes
 * Added render state cache as a parameter to routines that may modify
 * lighting state to fix a lighting state tracking problem.
 *
 * Revision 1.5  2004/06/22 05:01:58  wes
 * Minor tweaks to support consistent state tracking. These changes
 * were made as a result to changes in the main RMSG tree during a
 * big PS bug fixing expedition.
 *
 * Revision 1.4  2004/01/16 16:48:35  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.3  2003/11/16 16:19:40  wes
 * Removed "serial table" from picking operations. rmserial.c can probably
 * be removed from the source tree, and rmpick.c needs to have a bunch
 * of dead code surrounded by if 0's removed.
 *
 * Revision 1.2  2003/02/02 02:07:16  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.11  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.10  2002/12/04 14:50:33  wes
 * Cleanup SGI compiles.
 *
 * Revision 1.9  2002/06/17 01:03:00  wes
 * Replaced fixed-size table with one that is completely dynamic. No more
 * realloc error messages.
 *
 * Revision 1.8  2002/04/30 19:33:49  wes
 * Updated copyright dates.
 *
 * Revision 1.7  2001/05/26 14:37:49  wes
 * Added RMnode parameter to serialization code used in picking - this
 * will permit picking of scene graph subtrees that are disconnected
 * from rmRootNode().
 *
 * Revision 1.6  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.5  2000/12/03 22:35:38  wes
 * Mods for thread safety.
 *
 * Revision 1.4  2000/05/17 14:24:34  wes
 * Fixed compiler warnings on private_rmStateInit().
 *
 * Revision 1.3  2000/05/14 23:41:29  wes
 * Single parm change to RM state initialization (matrix stack control)
 * during serialization.
 *
 * Revision 1.2  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */


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

/*
 * this file contains a bunch of routines that are used to represent
 * the scene graph (a tree) in a serialized format. at this time (Jan 2000)
 * this is used for generating PostScript.
 *
 * there are no application-callable functions in this file.
 */

/*
 *
 * Last Updated: Thu Mar  6 12:09:08 PST 1997
 *
 * TODO:
 * 1. the serial table size is hard-coded.  it would be better to do
 *    dynamic memory allocation.  since the serialized table is used
 *    (ie, built and accessed) only when we enter selection or feedback
 *    rendering mode (to map tokens returned from OpenGL back to object
 *    handles),  this is not really a time crucial step. the conclusion is
 *    that we can afford to spend a little extra time doing dynamic
 *    allocation.
 *
 *    -> the hard-coded limit has been fixed as of 6/12/02. the thread-
 *    safety issues will be a relatively straightforward modification
 *    but will wait until there is sufficient popular demand.
 *
 * 2. thread-safety issues have not been considered in the building of
 *    this code.
 *
 * NOTE: dependancy upon object structure.
 *
 * when feedback mode in opengl is used, tokens are passed back to
 * the application.  we make special use of the passthrough token
 * so as to identify the object, the primitive and the particular
 * item in the primitive which is being rendered.  the reason for
 * this is so that later we can refer back to the original object
 * if need be.  
 *
 * when stuff is added to the serial table, we need:
 * - a pointer to an rmNode
 * - an integer identifying which primitive is being rendered
 *
 * the passthrough token which is generated will be a function of these 2.
 */
#define SERIAL_TABLE_BASE_SIZE    2048
#define SERIAL_TABLE_REALLOC_SIZE 1024

static int              serialized_size = 0; /* current size */
static int              serialized_max_size = 0; /* max current size */
static RMSerialNodeList *serialized_list=NULL;

/* PRIVATE */
static void
private_rmAddToSerialTable (RMnode *r, int *n, 
			    int *totalNodesAccum,
			    int *totalPrimsAccum)
{
   if (serialized_size + 1 >= serialized_max_size)
   {
      serialized_max_size += SERIAL_TABLE_REALLOC_SIZE;
      serialized_list = (RMSerialNodeList *)realloc(serialized_list, sizeof(RMSerialNodeList)*serialized_max_size);
      
#if (DEBUG_LEVEL & DEBUG_TRACE)
      printf(" private_rmAddToSerialTable realloc initiated. Old size = %d, new size = %d. \n", serialized_max_size - SERIAL_TABLE_REALLOC_SIZE, serialized_max_size);
#endif
   }

   serialized_list[*n].node = r;
   serialized_list[*n].index = *n;
   *n += 1;
   serialized_size += 1;

   *totalNodesAccum += 1;
   *totalPrimsAccum += r->nprims;
}


/* PRIVATE */
static void
private_rmBuildSerial (RMnode *r, int *n,
		       int *totalNodesAccum,
		       int *totalPrimsAccum)
{
    int i;

    /* add this node to the table */
    private_rmAddToSerialTable(r, n, totalNodesAccum, totalPrimsAccum);

    /* then, process all descendants */
    for (i = 0; i < private_rmNodeGetNumChildren(r); i++)
	private_rmBuildSerial(private_rmNodeGetIthChild(r, i), n, totalNodesAccum, totalPrimsAccum);

}


/* PRIVATE */
int
private_rmBuildSerializedList (RMnode *subTree, 
			       int *totalNodesReturn,
			       int *totalPrimsReturn)
{
    /*
     * modified 6/12/02 to compute and return the number of nodes and
     * primitives detected while building of the "serial table".
     */
    int n;
    RMnode *r;
    int totalPrims=0, totalNodes=0;

    /*
     * initialize the serial table.
     */
    if (serialized_list != NULL)
    {
      free((void *)serialized_list);
      serialized_size = serialized_max_size = 0;
    }

    serialized_list = (RMSerialNodeList *)(malloc(sizeof(RMSerialNodeList)*SERIAL_TABLE_BASE_SIZE));
    serialized_max_size = SERIAL_TABLE_BASE_SIZE;
    serialized_size = 0;

    /* traverse tree, build serialized list. */
    n = 0;
    r = subTree;
    private_rmBuildSerial(r, &n, &totalNodes, &totalPrims);

#if (DEBUG_LEVEL & DEBUG_TRACE)
    printf(" private_BuildSerializedList reality check: serialized_size = %d, n = %d \n", serialized_size, n);
#endif

    serialized_size = n;
    *totalNodesReturn = totalNodes;
    *totalPrimsReturn = totalPrims;
    return 0;
}


/* PRIVATE */
int
private_rmIndexFromSerial (const RMnode *r)
{
    /* scan through the serialized list and return the index of the
     * first entry containing "r".  if we don't find such an entry,
     * return -1. 
     */
    
    int i;
    
    for (i = 0; i < serialized_size; i++)
	if (serialized_list[i].node == r)
	    return(i);

    return(-1);
}


/* PRIVATE */
RMnode *
private_rmNodeFromSerial (int index)
{
    /* scan through the serialized list and return the index of the
     * first entry containing the index "index".  if we don't find such an
     * entry,  return NULL.
     */
    int i;
    
    for (i = 0; i < serialized_size; i++)
	if (serialized_list[i].index == index)
	    return(serialized_list[i].node);

    return(NULL);
}


#if 0
/* PRIVATE 
 *
 * not needed till we do dynamic allocation for serial table
 */
void
private_rmFreeSerializedList ()
{
    /* free serialized list */

}
#endif


typedef struct
{
    RMstate *s;
    int      indx;
} RMserialState;

/* really this is an inverse stack */
static RMserialState *rStateStack = NULL;
static int            rStateStackDepth = 0;

/* PRIVATE */
RMserialState *
private_rmSerialStateNew ()
{
    RMserialState *t;

    t = (RMserialState *)malloc(sizeof(RMserialState));

    if (RM_ASSERT(t,"rmSerialStateNew() unable to malloc a new state node ") == RM_WHACKED)
	return(NULL);
    
    t->indx = 0;
    t->s = NULL;

    return(t);
}

/* PRIVATE */
void
private_rmSerialStateInit(void)
{
    if (rStateStack != NULL)
	private_rmDeleteSerialState();

    rStateStack = private_rmSerialStateNew();
    rStateStackDepth = 0;
}

/* PRIVATE */
int
private_rmDeleteSerialState (void)
{
    int i;
    
    for (i = 0; i < rStateStackDepth; i++)
	free((void *)(rStateStack[i].s));

    free((void *)(rStateStack));
    rStateStack = NULL;
    rStateStackDepth = 0;

    return(RM_CHILL);
}


/* PRIVATE */
RMstate *
private_rmStateFromSerial (int indx)
{
/*    return(rStateStack[indx-1].s); huh? why -1? */
    return(rStateStack[indx].s); 
}


/* PRIVATE */
int
private_pushSerialState (RMstate *s)
{
    RMstate *t;
    RMserialState *ts;
    
    t = rmStateNew();
    rmStateCopy(s, t);

    rStateStack = realloc((void *)rStateStack, (sizeof(RMserialState) * (rStateStackDepth + 1)));
    ts = rStateStack + rStateStackDepth;
    
    ts->s = t;
    ts->indx = rStateStackDepth;
    
    rStateStackDepth++;

    return(RM_CHILL);
}


/* PRIVATE */
int
private_rmBuildSerialState (RMpipe *p,
			    RMnode *r,
			    RMstate *last,
			    int init)

{
    int      i, stateChange = 0;
    RMstate *s = rmStateNew();

    if (init == 1)
    {
	private_rmStateInit(p, s, (RMenum)GL_RENDER, NULL, NULL, NULL, NULL);
	/* could do some checking to free up old stack.. */
	rStateStackDepth = 0;
    }

    /* drag along the previous one? */
    if (rStateStackDepth != 0)
	rmStateCopy(last, s);
    
    private_collectAndApplyMatrices (s, r, NULL, GL_RENDER,
				     &stateChange, RM_FALSE);
    stateChange |= private_updateSceneParms(r, s, RM_FALSE, 0, NULL, NULL);

    if (stateChange == 1)	/* add to stack */
    {
	RMserialState *t;
	
	rStateStack = realloc((void *)rStateStack, (sizeof(RMserialState) * (rStateStackDepth + 1)));
	t = rStateStack + rStateStackDepth;

	t->s = s;
	t->indx = rStateStackDepth;
	
	rStateStackDepth++;
    }

    /* then recurse */
    for (i = 0; i < private_rmNodeGetNumChildren(r); i++)
	private_rmBuildSerialState(p, private_rmNodeGetIthChild(r, i), s, 0);

    
    if (stateChange == 0)
	rmStateDelete(s);

    return(RM_CHILL);
}
/* EOF */
