/* nodegen.c: generates C code with interface description and 
 * field value defaults for each VRML node. The data is read from
 * the node catalog file nodes.catalog. */

//
// 21-feb-2001
//       . output in the 'xrml' namespace.
//       . "vrml" renamed into "xrml"
//
// 09-feb-2001
//       . assign member functions now also update timestamp
//       (timestamp was not updated when directly assigning)
//
// 20-dec-2000:
//       . typeId also initialised for non-leaf node classes
//
// 14-dec-2000
//	. validate_[exposed]field functions replaced:
//		. synchronize_<fieldId> member function for [M|S}FNode valued fields
//		. assign_<fieldId> member functions check value range, do proper
//		assignement, and call synchronize() for [M|F]SNode valued fields
//		. static assign_<nodeId>_<fieldId> functions that check whter SFNode*
//		node argument is of proper type and call assign_<fieldId> member
//	. previous "do-all" synchronize() member omitted
//	. eventOut value is now also member of the class + there are now
//	two post_<eventOut>() member functions replacing previous <eventOut>() function:
//	one with and one without value argument
//	. value argument of post_eventOut, set_[exposed]field and process_<eventIn> 
//	functions are now const references, except for SFNode*.
//	. instantiate() returns 'this' for named nodes without enclosing proto and
//	calls synchronize() for [S|M}FNode values.
//	. [exposed]fields, eventIns, eventOuts declared at the highest level where
//	they are defined: post_<eventOut>, assign<[exposed]field>, and
//	set_<exposedField> at base classes declared virtual and prints error message.
//	True implementation only for leaf classes (since only there the complete
//	interface is available).
//	. status bits indicate what fields/eventOuts have been updated since
//	last inspection. They are declared only for leaf classes. Manipulation by
//	a user is through xxx_is_updated() and xxx_clear_update() member functions,
//	which for base classes are declared virtual and print a "stub called" error
//	message.
//	. Can also handle nodes without interface members now
// 	. Better handling of utility data in constructor/destructor by means of
//	new util_[init|clone|destruct]() member functions.
//	. new comment delimiters in header file indicate where manual editing
//	should be restricted, so the headers can be updated automatically later
//	on without tedious manual merging.
//	. major refactoring of the nodegen program itself.
//

#include <strstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vrml.H"
#include "nodeCatalog.H"
#include "parser.H"
#include "error.H"
#include "version.H"

/* catalog of known node types */
static array<SFNode *> nodeCatalog(10);


// prints values in C++ syntax
static void cpr(FILE *fp, SFBool& v) 	{ fprintf(fp, "%d", (bool)v ? 1 : 0); }
static void cpr(FILE *fp, SFString& v) 	{ fprintf(fp, "\"%s\"", (char *)v); }
static void cpr(FILE *fp, SFInt32& v) 	{ fprintf(fp, "%d", (int)v); }
static void cpr(FILE *fp, SFFloat& v) 	{ fprintf(fp, "%g", (float)v); }
static void cpr(FILE *fp, SFTime& v) 	{ fprintf(fp, "%g", (double)v); }
static void cpr(FILE *fp, SFVec2f& v) 	{ fprintf(fp, "SFVec2f(%g,%g)", v.s, v.t); }
static void cpr(FILE *fp, SFVec3f& v) 	{ fprintf(fp, "SFVec3f(%g,%g,%g)", v.x, v.y, v.z); }
static void cpr(FILE *fp, SFColor& v) 	{ fprintf(fp, "SFColor(%g,%g,%g)", v.r, v.g, v.b); }
static void cpr(FILE *fp, SFRotation& v){ fprintf(fp, "SFRotation(%g,%g,%g,%g)", v.x, v.y, v.z, v.radians); }
static void cpr(FILE *fp, SFImage& v) 	{
  fprintf(fp, "SFImage(%d,%d,%d)", v.width, v.height, v.num_components);
  if (v.width * v.height > 0)
    fprintf(stderr, "num. pixels in image > 0!!!\n");
}

static void cpr(FILE *fp, interfaceDeclaration* decl)
{
  fprintf(fp, "\t%-12.12s  %-10.10s  %-12s\t", 
	  interfaceKindName(decl->kind), fieldTypeName(decl->type), decl->Id);
  if (decl->kind==kfield || decl->kind==kexposedField) {
    char buf[1000];
    ostrstream ost(buf,1000);
    fieldValue val(decl->type, (void *)decl->value);
    ost << val;
    fprintf(fp, "%s", buf);
  }
  fprintf(fp, "\n");
}

// Generates C++ code for initialising a value with the default value
// in the interfaceDeclaration.
// Limitations:
// . doesn't handle non-nil SFNode or MFNode default values.
// . doens't handle SFImage type values with non-empty image
static void GenerateDefaultValueCode(FILE *fp, interfaceDeclaration *decl)
{
// prints array in C++ syntax
#define CPRARRAY(type, typestr, stypestr)				\
    {									\
      type *p = (type *)decl->value;					\
      if (p->size == 0)							\
	fprintf(fp, "  %s = %s(0);\n", decl->Id, typestr);		\
      else {								\
	fprintf(fp, "  const %s _%s[%d] = {", stypestr, decl->Id, p->size);	\
	for (int i=0; i<p->size; i++) {					\
	  if (i>0) fprintf(fp, ", ");					\
	  cpr(fp, (*p)[i]);						\
	}								\
	fprintf(fp, "};\n  %s = %s(%d,(%s *)_%s);\n",			\
		decl->Id, typestr, p->size, stypestr, decl->Id);	\
      }									\
    }

#define CPRSIMPLE(type)							\
  {									\
    fprintf(fp, "  %s = ", decl->Id);					\
    cpr(fp, *((type *)(decl->value)));					\
    fprintf(fp, ";\n"); 						\
  }

  switch (decl->type) {
  case tSFFloat:	CPRSIMPLE(SFFloat); break;
  case tSFInt32:	CPRSIMPLE(SFInt32); break;
  case tSFTime:		CPRSIMPLE(SFTime); break;
  case tSFString:	CPRSIMPLE(SFString); break;
  case tSFBool:		CPRSIMPLE(SFBool); break;
  case tSFColor:	CPRSIMPLE(SFColor); break;
  case tSFImage:	CPRSIMPLE(SFImage); break;
  case tSFNode:	       
    if (decl->kind != keventOut) {
      fprintf(fp, "  %s = (%s *)0; \t_%s = (SFNode *)0;\n", 
	      decl->Id,
	      decl->nodeClassId ? decl->nodeClassId : "SFNode", 
	      decl->Id);
    } else {
      fprintf(fp, "  %s = (SFNode *)0;\n", decl->Id);
    }
    if ((SFNode *)(decl->value)) {
      cpr(fp, decl);
      fprintf(stderr, "non-null default SFNode * value !!!\n");
    }
    break;
  case tSFRotation:	CPRSIMPLE(SFRotation); break;
  case tSFVec2f:	CPRSIMPLE(SFVec2f); break;
  case tSFVec3f:	CPRSIMPLE(SFVec3f); break;
  case tMFColor:	CPRARRAY(MFColor, "MFColor", "SFColor"); break;
  case tMFFloat:	CPRARRAY(MFFloat, "MFFloat", "SFFloat"); break;
  case tMFInt32:	CPRARRAY(MFInt32, "MFInt32", "SFInt32"); break;
  case tMFRotation:	CPRARRAY(MFRotation, "MFRotation", "SFRotation"); break;
  case tMFString:	CPRARRAY(MFString, "MFString", "SFString"); break;
  case tMFTime:		CPRARRAY(MFTime, "MFTime", "SFTime"); break;
  case tMFVec2f:	CPRARRAY(MFVec2f, "MFVec2f", "SFVec2f"); break;
  case tMFVec3f:	CPRARRAY(MFVec3f, "MFVec3f", "SFVec3f"); break;
  case tMFNode:
    {
      MFNode *p = (MFNode *)decl->value;
      if (p->size > 0) {
	cpr(fp, decl);
	fprintf(stderr, "non-empty default MFNode value !!!\n");
      } else {
	if (decl->kind != keventOut) {
	  fprintf(fp, "  %s = array<%s*>(0); \t_%s = MFNode(0);\n", 
		  decl->Id,
		  decl->nodeClassId ? decl->nodeClassId : "SFNode", 
		  decl->Id);
	} else {
	  fprintf(fp, "  %s = MFNode(0);\n", decl->Id);
	}
      }
      break;
    }
  default:		Fatal(-1, "GenerateDefaultValueCode", "invalid type '%d'", decl->type);   
  }
}

// returns true if node has a member with Id 'id'.
static bool IsMemberOfNode(char *id, SFNode *node)
{
  for (int i=0; i<node->nrInterfaceDeclarations; i++) {
    interfaceDeclaration *decl = &node->interface[i];
    if (strcmp(decl->Id, id) == 0)
      return true;
  }
  return false;
}

// returns true if node has a parent with 'id'.member.
static bool IsAncestorMember(char *id, SFNode *node)
{
  for (int i=0; i<node->parentNodes.size; i++)
    if (IsMemberOfNode(id, node->parentNodes[i]) || IsAncestorMember(id, node->parentNodes[i]))
      return true;
  return false;
}

// inserts the file src into dest
static void InsertFile(FILE *dest, FILE *src)
{
  int c;
  while ((c = getc(src)) != EOF)
    putc(c, dest);
}

static void GenerateParentNodeDecl(FILE *fp, SFNode *node)
{
  // produce parent node class declaration: we assume that node superclasses have only
  // one common ancestor: SFNode. 'public virtual SFNode' is required for
  // top node superclasses and for the leaf node classes but not for
  // the intermediate node superclasses.
  int count = 0;
  if (!node->isParent || node->parentNodes.size == 0) {
    fprintf(fp, " public virtual SFNode");
    count++;
  }
  for (int i=0; i<node->parentNodes.size; i++) {
    fprintf(fp, "%c public %s", 
	    count>0 ? ',' : ' ',
	    node->parentNodes[i]->typeId);
    count++;
  }
}

static void GenerateStatusBitDecl(FILE *fp, SFNode *node)
{
  if (node->isParent)
    // status bits are only declared for leaf node classes, where the
    // full interface is known
    return;	

  interfaceDeclaration *decl;
  int i;
  int count=0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField || decl->kind == keventOut)
      count++;
  }

  // Update status: bits are set by assign_xxx and post_xxx member functions
  // of leaf node classes.
  if (count > 0) {
    fprintf(fp, "  // field/eventOut update status bits\n  // Use xxx_is_updated() and xxx_clear_update() to inspect/reset.\n");
    fprintf(fp, "  enum status_bits {\n");
    int n = 0;
    for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
      if (decl->kind == kfield || decl->kind == kexposedField || decl->kind == keventOut) {
	fprintf(fp, "    %s_updated = 0x%x%c\n", 
		decl->Id, 1<<n, n<count-1 ? ',' : ' ');
	n++;
      }
    }
    fprintf(fp, "  };\n");
    fprintf(fp, "  \n");
  }
}

static void GeneratePrivateHeader(FILE *fp, SFNode *node)
{
  if (node->isParent)
    return;	// no private members for base classes

  fprintf(fp, "private:\n");

  // clone() for a leaf node class calls SFNode::clone() (see SFNode.C),
  // ensures that the interfaceMembers of a newly created node
  // point to its own values, copies src field and eventOut values,
  // and calls clone_util() ember functions for the leaf node class
  // and all parents.
  // Only leaf nodes have such a clone() function because only leaf
  // nodes have the interface defined with them.
  fprintf(fp, "  //\n");
  fprintf(fp, "  void clone_here(const %s&);\n", node->typeId);
  fprintf(fp, "  void destruct(void);\n");
  fprintf(fp, "  \n");

  GenerateStatusBitDecl(fp, node);
}

// Wee keep two copies of [S|M]Node values: raw and "resolved":
// Raw children node values directly correspond to what is read from the VRML 
// file. They may be PROTO interface declaration nodes for instance.
// synchronize() methods "resolve" them: produce node points and arrays of
// the proper node type (e.g. Material for nodes in a Appearance.material field).
// (synchronize_xxx() basically calls the raw nodes "firstnode()" method and casts to
// the requested SFNode sub-class).
static void GenerateRawStateDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  fprintf(fp, "  // raw children nodes (can be PROTO) + synchronize methods:\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField) {
      if (decl->type == tSFNode || decl->type == tMFNode) {
	char *comment = "";
	if (IsAncestorMember(decl->Id, node))
	  comment = "//  ";
	if (decl->type == tSFNode)
	  fprintf(fp, "  %s%s \t_%s;\n", comment, "SFNode*", decl->Id);
	else if (decl->type == tMFNode)
	  fprintf(fp, "  %s%s \t_%s;\n", comment, "MFNode", decl->Id);
	fprintf(fp, "  %svoid \tsynchronize_%s(void);\n", comment, decl->Id);
      }
    }
  }
  fprintf(fp, "\n");
}

static void GenerateProtectedEventOutDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  fprintf(fp, "  // raw eventOut posting:\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == keventOut) {
      fprintf(fp, "  void post_%s(void);\n",
	      decl->Id);
    }
  }
  fprintf(fp, "\n");
}

static void GenerateProtectedHeader(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  int nrrawstatevals = 0;
  int nreventouts = 0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField) {
      if (IsAncestorMember(decl->Id, node)) continue;
      if (decl->type == tSFNode || decl->type == tMFNode)
	nrrawstatevals++;
    }
    if (decl->kind == keventOut)
      if (!node->isParent)
	nreventouts++;
  }
  if (nrrawstatevals + nreventouts == 0)
    return;	// no protected members

  fprintf(fp, "protected:\n");

  if (nrrawstatevals > 0)
    GenerateRawStateDecl(fp, node);

  if (nreventouts > 0)
    GenerateProtectedEventOutDecl(fp, node);
}

static void GenerateStateValueDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  // Resolved children nodes and other state values: this is what the user
  // needs: everything of the same type and classes as described in the
  // VRML standard.
  int numfields = 0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField) {
      numfields ++;
    }
  }
  if (numfields <= 0)
    return;

  fprintf(fp, "  // state values:\n  // Do not assign directly unless you know very well what you are doing.\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField) {
      if (IsAncestorMember(decl->Id, node))
	fprintf(fp, "  //");

      if (decl->type == tSFNode) {
	char buf[100];
	sprintf(buf, "%s*", 
		decl->nodeClassId ? decl->nodeClassId : "SFNode");
	fprintf(fp, "  %-16s \t%s;\n", 
		buf, decl->Id);
      } else if (decl->type == tMFNode)
	fprintf(fp, "  array<%s*> \t%s;\n", 
		(decl->nodeClassId ? decl->nodeClassId : "SFNode"), 
		decl->Id);
      else
	fprintf(fp, "  %-16.16s \t%s;\n", fieldTypeName(decl->type), decl->Id);
    }
  }
  fprintf(fp, "  \n");
}

static void GenerateEventInDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  // eventIn functions: call these to update the state of the node. They
  // corresond to eventIns as described in the VRML standard + set_.... 
  // eventIns for each exposedField.
  int count = 0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == keventIn)
      count++;
  }
  if (count <= 0)
    return;

  fprintf(fp, "  // eventIns:\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == keventIn) {
      // can be overriden in subclasses
      char buf[100];
      sprintf(buf, "%svoid", 
	      node->isParent ? "virtual " : "");
      fprintf(fp, "  %-16s \t%s(%s%s%s);\n", 
	      buf, decl->Id,
	      decl->type==tSFNode ? "" : "const ",
	      fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
    }
  }
  fprintf(fp, "  \n");
}

static void GenerateSetExposedFieldDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  // set_<exposedField>() eventIns: need the interfaceMembers, so
  // can only be implemented for leaf node classes.
  int count = 0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kexposedField)
      count++;
  }
  if (count <= 0)
    return;

  fprintf(fp, "  // set_<exposedField> eventIns: with forwarding and eventOut generation\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kexposedField) {
      char buf[100];
      sprintf(buf, "%svoid", 
	      node->isParent ? "virtual " : "");
      fprintf(fp, "  %-16s \tset_%s(%s%s%s);\n", 
	      buf, decl->Id,
	      decl->type==tSFNode ? "" : "const ",
	      fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
    }
  }
  fprintf(fp, "  \n");
}

static void GenerateFieldAssignDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  int numfields = 0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField)
      numfields ++;
  }
  if (numfields <= 0)
    return;

  fprintf(fp, "  // field assignement: no forwarding or eventOut generation!\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField) {
      char buf[100];
      sprintf(buf, "%sbool", 
	      node->isParent ? "virtual " : "");
      fprintf(fp, "  %-16s \tassign_%s(%s%s%s);\n",
	      buf, decl->Id, 
	      decl->type==tSFNode ? "" : "const ",
	      fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
    }
  }
  fprintf(fp, "  \n");
}

static void GeneratePublicEventOutDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  // eventOuts: call these to let the node generate an eventOut.
  // implementation needs the interfaceMembers, so can only be done
  // for leaf nodes.
  int nreventOuts = 0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == keventOut)
      nreventOuts++;
  }
  if (nreventOuts <= 0)
    return;

  fprintf(fp, "  // eventOuts:\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == keventOut) {
      if (IsAncestorMember(decl->Id, node))
	fprintf(fp, "  //");
      fprintf(fp, "  %s%s            \t%s;\n",
	      fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "",
	      decl->Id);
      char buf[100];
      sprintf(buf, "%svoid", 
	      node->isParent ? "virtual " : "");
      fprintf(fp, "  %-16s \tpost_%s(%s%s%s val);\n",
	      buf, decl->Id,
	      decl->type==tSFNode ? "" : "const ",
	      fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
    }
  }
  fprintf(fp, "  \n");
}

static void GenerateUpdateStatusDecl(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  // update status inspection and reset
  int count=0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField || decl->kind == keventOut)
      count++;
  }
  if (count <= 0)
    return;

  fprintf(fp, "  // update status\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField || decl->kind == keventOut) {
      if (node->isParent) {
	fprintf(fp, "  %-16s \t%s_is_updated(void);\n",
		"virtual bool", decl->Id);
	fprintf(fp, "  %-16s \t%s_clear_update(void);\n",
		"virtual void", decl->Id);
      } else {
	fprintf(fp, "  %-16s \t%s_is_updated(void) { return (status & %s_updated); }\n",
		"inline bool", decl->Id, decl->Id);
	fprintf(fp, "  %-16s \t%s_clear_update(void) { status &= ~%s_updated; }\n",
		"inline void", decl->Id, decl->Id);
      }
    }
  }
  fprintf(fp, "  \n");
}

static void GeneratePublicHeader(FILE *fp, SFNode *node)
{
  fprintf(fp, "public:\n");

  // constructors and destructor
  fprintf(fp, "  //\n");
  fprintf(fp, "  %sint major_version(void), minor_version(void);\n", node->isParent ? "virtual " : "");
  fprintf(fp, "  %sbool is_leaf_class(void);\n", node->isParent ? "virtual " : "");
  //  fprintf(fp, "  PoolDecl(%s, \"%s\")\n", node->typeId, node->typeId);
  fprintf(fp, "  \n");

  fprintf(fp, "  %s();\n", node->typeId);
  if (!node->isParent) {
    fprintf(fp, "  %s(const %s&);\n", node->typeId, node->typeId);
    fprintf(fp, "  %s& operator=(const %s&);\n", node->typeId, node->typeId);
    fprintf(fp, "  virtual ~%s();\n", node->typeId);
  }
  fprintf(fp, "  \n");

  fprintf(fp, "  //\n");
  // see vrml.H
  if (!node->isParent) {
    fprintf(fp, "  %-16s \tinstantiate(Proto * =0);\n", "SFNode*");
    // no "do-all" synchronize() member function needed anymore since 12-dec-2000
    //  fprintf(fp, "  virtual double  \tsynchronize(void);\n");
  }
  char buf[100];
  sprintf(buf, "%svoid", node->isParent? "virtual " : "");
  fprintf(fp, "  %-16s \trender(void);\n", buf);
  fprintf(fp, "  \n");

  GenerateStateValueDecl(fp, node);
  GenerateEventInDecl(fp, node);
  GenerateSetExposedFieldDecl(fp, node);
  GenerateFieldAssignDecl(fp, node);
  GeneratePublicEventOutDecl(fp, node);
  GenerateUpdateStatusDecl(fp, node);
}

// marks start and end of utility includes and utility member declaration sections
// in header.
static char* UtilityIncludesStartDelim(SFNode *node)
{
  return "// utility includes";
}

static char* UtilityIncludesEndDelim(SFNode *node)
{
  return "// end of utility includes";
}

static char* UtilityMembersStartDelim(SFNode *node)
{
  return "  // utility members";
}

static char* UtilityMembersEndDelim(SFNode *node)
{
  static char buf[100];
  sprintf(buf, "// end of utility members");
  return buf;
}

static void GenerateHeader(FILE *fp, SFNode *node, FILE *util_includes =0, FILE *util_members =0)
{
  // The header file for a node class implementation contains declarations
  // of both automatically generated members and "utility" members that
  // need to be implemented manually.
  //
  // Manual editing work shall be restricted to the "utility includes" section
  // and the remainder of the automatically generated header file after the
  // "utility members" comment. The "util_init()", "util_clone()" and "util_destruct()"
  // members shall not be removed however. In this way, automatic future updates
  // of the generated header will be possible without the need to manually merge
  // all non-automatically generated code (using proper merge scripts).

  fprintf(fp, "// %s.H: %s nodes.\n", node->typeId, node->typeId);
  fprintf(fp, "\n");
  
  fprintf(fp, "// This file has been generated using nodegen %d.%d%s.\n",
	  MAJOR_VERSION, MINOR_VERSION, RELEASE);
  fprintf(fp, "// Restrict manual editing to the sections delimited by\n");
  fprintf(fp, "// . the comments \"utility includes\" and \"end of utility includes\"\n");
  fprintf(fp, "// . the comments \"utility members\" and \"end of utility members\"\n");
  fprintf(fp, "\n");

  fprintf(fp, "#ifndef _%s_H_\n#define _%s_H_\n\n", node->typeId, node->typeId);

  // The header files do not depend on vrmlP.H: we want to hide the 
  // internals of the node interface, event ourting, etc... to the user
  // of this VRML library.
  fprintf(fp, "#include \"xrml.H\"\n");

  // include parent node header files
  for (int i=0; i<node->dependNodes.size; i++) {
    fprintf(fp, "#include \"%s.H\"\n", node->dependNodes[i]->typeId);
  }
  fprintf(fp, "\n");

  // utility includes
  fprintf(fp, "%s\n", UtilityIncludesStartDelim(node));
  if (util_includes)
    InsertFile(fp, util_includes);
  fprintf(fp, "%s\n", UtilityIncludesEndDelim(node));
  fprintf(fp, "\n");

  fprintf(fp, "namespace xrml {\n");
  fprintf(fp, "#define %sTypeId \t\"%s\"\n", node->typeId, node->typeId);
  fprintf(fp, "extern bool Is%s(SFNode*);\n",
	  node->typeId);
  fprintf(fp, "\n");

  fprintf(fp, "class %s:", node->typeId);
  GenerateParentNodeDecl(fp, node);
  fprintf(fp, " {\n");

  GeneratePrivateHeader(fp, node);
  GenerateProtectedHeader(fp, node);
  GeneratePublicHeader(fp, node);
  
  fprintf(fp, "%s\n", UtilityMembersStartDelim(node));
  if (util_members)
    InsertFile(fp, util_members);
  else {
    fprintf(fp, "  \n");

    fprintf(fp, "protected:\n");
    fprintf(fp, "  inline void util_init(void) { }\n");
    fprintf(fp, "  inline void util_clone(const %s& src) { }\n", node->typeId);
    fprintf(fp, "  inline void util_destruct(void) { }\n");

    fprintf(fp, "};\n\n");
  }

  fprintf(fp, "%s\n", UtilityMembersEndDelim(node));
  fprintf(fp, "} // namespace xrml\n");
  fprintf(fp, "#endif /*_%s_H_*/\n", node->typeId);
}

static void GeneratePublicSource(FILE *fp, SFNode *node)
{
  // The public source file contains all the stuff that cannot be generated
  // automatically. By hiding the internals of the node interface and
  // event processing, we protect manual implementations against
  // future changes in the internals that require the private sources
  // to be regenerated.
  //
  // A good solution if you can't implement the routines here or other utility
  // members without access to the internals is to move the implementations
  // to a second, new, private source file:
  // . the primary private source file can be replaced automatically without
  // loss of manual work
  // . the secondary private sources will need editing but will never
  // be overwritten automatically.
  // . the public sources never need re-editing after a change of
  // the internals and do not need to be regenerated

  interfaceDeclaration *decl;
  int i;

  fprintf(fp, "/* %s.C: %s nodes (public source) */\n", node->typeId, node->typeId);
  fprintf(fp, "\n");
  fprintf(fp, "#include \"%s.H\"\n", node->typeId);
  fprintf(fp, "\n");

  fprintf(fp, "namespace xrml {\n");
  // eventIns: avoid accessing the interface members directly!
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == keventIn) {
      fprintf(fp, "void %s::%s(%s%s%s)\n", 
	      node->typeId, decl->Id,
	      decl->type==tSFNode ? "" : "const ",
	      fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
      fprintf(fp, "{\n");
      fprintf(fp, "  Warning(NULL, \"%s::%s(): not yet implemented\");\n",
	      node->typeId, decl->Id);
      fprintf(fp, "}\n");
      fprintf(fp, "\n");
    }
  }

  // render function
  fprintf(fp, "void %s::render(void)\n", node->typeId);
  fprintf(fp, "{\n");
  fprintf(fp, "  Warning(NULL, \"%s::render(): not yet implemented\");\n", node->typeId);
  fprintf(fp, "}\n");
  fprintf(fp, "\n");

  fprintf(fp, "}  // namespace xrml\n");
}

// top-down
static void CallUtilInit(FILE *fp, SFNode *node)
{
 for (int i=0; i<node->parentNodes.size; i++)
    CallUtilInit(fp, node->parentNodes[i]);
  fprintf(fp, "  %s::util_init();\n", node->typeId);
}

// top-down
static void CallUtilClone(FILE *fp, SFNode *node)
{
  for (int i=0; i<node->parentNodes.size; i++)
    CallUtilClone(fp, node->parentNodes[i]);
  fprintf(fp, "  %s::util_clone(src);\n", node->typeId);  
}

// bottom-up
static void CallUtilDestruct(FILE *fp, SFNode *node)
{
  fprintf(fp, "  %s::util_destruct();\n", node->typeId);
  for (int i=0; i<node->parentNodes.size; i++)
    CallUtilDestruct(fp, node->parentNodes[i]);
}

static void GenerateEventInPrivateSource(FILE *fp, SFNode* node, interfaceDeclaration *decl, int index)
{
  // eventIn processing "frontends", needed because member functions cannot be
  // passed to nodeInterface constructors.
  // (the process_xxx() member function itself is implemented in the public source)

  if (node->isParent)	// only for leaf node classes
    return;

  if (decl->kind == keventIn) {
    fprintf(fp, "static void process_%s_%s(SFNode* node, const fieldValue& value)\n",
	    node->typeId, decl->Id);
    fprintf(fp, "{\n");
    fprintf(fp, "  %s* %s_node = dynamic_cast<%s*>(node);\n", node->typeId, node->typeId, node->typeId);
    fprintf(fp, "  if (!%s_node)\n", node->typeId);
    fprintf(fp, "    Fatal(-1, \"process_%s_%s\", \"called for a non-%s node\");\n",
	    node->typeId, decl->Id, node->typeId);
    fprintf(fp, "  %s_node->%s((%s%s%s)value);\n",
	    node->typeId, decl->Id, 
	    decl->type == tSFNode ? "" : "const ",
	    fieldTypeName(decl->type),
	    decl->type == tSFNode ? "*&" : "&");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");
  }
}

static void GenerateUpdateStatusSource(FILE *fp, SFNode *node, interfaceDeclaration* decl)
{
  // update status inquiry and reset: implemented inline in header for leaf node 
  // classes, stub needed for base classes.
  if (!node->isParent)
    return;

  fprintf(fp, "bool %s::%s_is_updated(void)", node->typeId, decl->Id);
  fprintf(fp, "{\n");
  fprintf(fp, "  stub(\"%s::%s_is_updated\");\n", node->typeId, decl->Id);
  fprintf(fp, "  return true;\n");
  fprintf(fp, "}\n\n");

  fprintf(fp, "void %s::%s_clear_update(void)", node->typeId, decl->Id);
  fprintf(fp, "{\n");
  fprintf(fp, "  stub(\"%s::%s_clear_update\");\n", node->typeId, decl->Id);
  fprintf(fp, "}\n\n");
}

static void GenerateFieldSynchronizeSource(FILE *fp, SFNode *node, interfaceDeclaration *decl)
{
  // synchronize() member functions for SFNode or MFNode field values.
  // Implemented for the base class where the field value is defined.
  if (!(decl->type==tSFNode || decl->type==tMFNode) || IsAncestorMember(decl->Id, node))
    return;

  fprintf(fp, "void %s::synchronize_%s(void)\n", node->typeId, decl->Id);
  fprintf(fp, "{\n");
  if (decl->type == tSFNode) {
    fprintf(fp, "  %s = (_%s) ? dynamic_cast<%s*>(_%s->firstnode()) : 0;\n",
	    decl->Id, decl->Id, decl->nodeClassId ? decl->nodeClassId : "SFNode", decl->Id);
  }
  if (decl->type == tMFNode) {
    fprintf(fp, "  if (_%s.size > %s.size) %s.grow(_%s.size - %s.size);\n",
	    decl->Id, decl->Id, decl->Id, decl->Id, decl->Id);
    fprintf(fp, "  for (int i=%s.size=0; i<_%s.size; i++)\n",
	    decl->Id, decl->Id);
    fprintf(fp, "    %s.append(dynamic_cast<%s*>(_%s[i]->firstnode()));\n",
	    decl->Id, decl->nodeClassId ? decl->nodeClassId : "SFNode", decl->Id);
  }
  fprintf(fp, "}\n\n");
}

static void GenerateFieldAssignMemberSource(FILE *fp, SFNode *node, interfaceDeclaration *decl, int index)
{
  // Assign member functions: stubs for base classes and real implementation
  // only for leaf classes. The assign member functions check the value being
  // passed, do proper assignement and call synchronize() for [S|M]FNode value
  // types.
  fprintf(fp, "bool %s::assign_%s(%s%s%s value)\n", 
	  node->typeId, decl->Id,
	  decl->type==tSFNode ? "" : "const ",
	  fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
  fprintf(fp, "{\n");
  if (node->isParent) {
    fprintf(fp, "  stub(\"%s::assign_%s\");\n", node->typeId, decl->Id);
    fprintf(fp, "  return false;\n");
  } else {
    if (decl->type == tSFNode || decl->type == tMFNode) {
      fprintf(fp, "  %s%s oldval = _%s;\n", fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "", decl->Id);
      fprintf(fp, "  _%s = value;\n", decl->Id);
      fprintf(fp, "  if (checkValue(_%s, Is%s, \"%s.%s\", \"%s\")) {\n",
	      decl->Id,
	      decl->nodeClassId ? decl->nodeClassId : "SFNode",
	      node->typeId, decl->Id,
	      decl->nodeClassId ? decl->nodeClassId : "SFNode");
      fprintf(fp, "    synchronize_%s();\n", decl->Id);
      fprintf(fp, "    status |= %s_updated;\n", decl->Id);
      fprintf(fp, "    timestamp = interface[%d]->timestamp = world ? world->time : 0;\n", index);
      fprintf(fp, "    return true;\n");
      fprintf(fp, "  } else {\n");
      fprintf(fp, "    _%s = oldval;\n", decl->Id);
      fprintf(fp, "    return false;\n");
      fprintf(fp, "  }\n");
    } else {
      fprintf(fp, "  // TODO: value range check!\n");
      fprintf(fp, "  %s = value;\n", decl->Id);
      fprintf(fp, "  status |= %s_updated;\n", decl->Id);
      fprintf(fp, "  timestamp = interface[%d]->timestamp = world ? world->time : 0;\n", index);
      fprintf(fp, "  return true;\n");	// assume valid value by default
    }
  }
  fprintf(fp, "}\n\n");
}

static void GenerateFieldAssignCallerSource(FILE *fp, SFNode *node, interfaceDeclaration *decl)
{
  if (node->isParent)
    return;	// only for leaf node classes

  fprintf(fp, "static bool assign_%s_%s(SFNode* node, const fieldValue& value)\n{\n", 
	  node->typeId, decl->Id);
  fprintf(fp, "  %s* %s_node = dynamic_cast<%s*>(node);\n", node->typeId, node->typeId, node->typeId);
  fprintf(fp, "  if (!%s_node)\n", node->typeId);
  fprintf(fp, "    Fatal(-1, \"assign_%s_%s\", \"called for a non-%s node\");\n",
	  node->typeId, decl->Id, node->typeId);
  fprintf(fp, "  return %s_node->assign_%s((%s%s%s)value);\n", node->typeId, decl->Id,
	  decl->type ==tSFNode ? "" : "const ", fieldTypeName(decl->type), decl->type==tSFNode ? "*&" : "&");
  fprintf(fp, "}\n\n");
}

static void GenerateFieldPrivateSource(FILE *fp, SFNode *node, interfaceDeclaration *decl, int index)
{
  GenerateFieldSynchronizeSource(fp, node, decl);
  GenerateFieldAssignMemberSource(fp, node, decl, index);
  GenerateFieldAssignCallerSource(fp, node, decl);
  GenerateUpdateStatusSource(fp, node, decl);
}

static void GenerateExposedFieldPrivateSource(FILE *fp, SFNode *node, interfaceDeclaration *decl, int index)
{
  GenerateFieldPrivateSource(fp, node, decl, index);

  // set_<exposedField> eventIn: stub for base classes, real implementation for
  // leaf node classes
  fprintf(fp, "void %s::set_%s(%s%s%s val)\n", 
	  node->typeId, decl->Id,
	  decl->type==tSFNode ? "" : "const ",
	  fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
  fprintf(fp, "{\n");
  if (node->isParent) {
    fprintf(fp, "  stub(\"%s::set_%s\");\n", node->typeId, decl->Id);
  } else {
    fprintf(fp, "  %s%s tmpval(val);\n", fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "");
    fprintf(fp, "  fieldValue v(&tmpval);\n");
    fprintf(fp, "  interface[%d]->assign(this, v);\n", index);
  }
  fprintf(fp, "}\n\n");
}

static void GenerateEventOutPrivateSource(FILE *fp, SFNode *node, interfaceDeclaration *decl, int index)
{
  if (!node->isParent) {
    // proper posting of the eventOut
    fprintf(fp, "void %s::post_%s(void)\n", 
	    node->typeId, decl->Id);
    fprintf(fp, "{\n");
    fprintf(fp, "  interface[%d]->post(this, interface[%d]->value);\n", index, index);
    fprintf(fp, "}\n\n");
  }

  // assigns a value to the eventOut and post() above.
  fprintf(fp, "void %s::post_%s(%s%s%s val)\n",
	  node->typeId, decl->Id,
	  decl->type==tSFNode ? "" : "const ",
	  fieldTypeName(decl->type), decl->type==tSFNode ? "*" : "&");
  fprintf(fp, "{\n");
  if (node->isParent) {
    fprintf(fp, "  stub(\"%s::post_%s\");\n", node->typeId, decl->Id);    
  } else {
    fprintf(fp, "  %s = val;\n", decl->Id);
    fprintf(fp, "  status |= %s_updated;\n", decl->Id);
    fprintf(fp, "  post_%s();\n", decl->Id);
  }
  fprintf(fp, "}\n\n");

  GenerateUpdateStatusSource(fp, node, decl);
}

static void GenerateInterfaceConstructorSource(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  if (node->nrInterfaceDeclarations <= 0) {
    fprintf(fp, "  interfaceMember** the_interface = 0;\n");    
  } else {
    fprintf(fp, "  interfaceMember* the_interface[%d] = {\n", node->nrInterfaceDeclarations);
    for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
      switch (decl->kind) {
      case keventIn:
	fprintf(fp, "    new eventIn(\"%s\", t%s, process_%s_%s),\n",
		decl->Id, fieldTypeName(decl->type), node->typeId, decl->Id);
	break;

      case kexposedField:
	if (decl->type == tSFNode || decl->type == tMFNode)
	  fprintf(fp, "    new exposedField(\"%s\", fieldValue(&_%s), assign_%s_%s),\n",
		  decl->Id, decl->Id, node->typeId, decl->Id);
	else
	  fprintf(fp, "    new exposedField(\"%s\", fieldValue(&%s), assign_%s_%s),\n",
		  decl->Id, decl->Id, node->typeId, decl->Id);
	break;

      case kfield:
	if (decl->type == tSFNode || decl->type == tMFNode)
	  fprintf(fp, "    new field(\"%s\", fieldValue(&_%s), assign_%s_%s),\n",
		  decl->Id, decl->Id, node->typeId, decl->Id);
	else
	  fprintf(fp, "    new field(\"%s\", fieldValue(&%s), assign_%s_%s),\n",
		  decl->Id, decl->Id, node->typeId, decl->Id);	
	break;

      case keventOut:
	fprintf(fp, "    new eventOut(\"%s\", fieldValue(&%s)),\n",
		decl->Id, decl->Id);
	break;

      default:
	fprintf(stderr, "Dummy interface member in node %s!!!\n", node->typeId);
	break;      
      }
    }
    fprintf(fp, "  };\n");
  }	// nrInterfaceMembers > 0

  fprintf(fp, "  SFNode::construct(%sTypeId, %d, the_interface);\n\n",
	  node->typeId, node->nrInterfaceDeclarations);
}

static void GenerateDefaultConstructorSource(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  // default constructor
  fprintf(fp, "%s::%s()\n", node->typeId, node->typeId);
  fprintf(fp, "{\n");

  if (!node->isParent) {
    // node interface members: only defined for leaf nodes as only leaf
    // nodes have all the field values.
    GenerateInterfaceConstructorSource(fp, node);
  } else {
    fprintf(fp, "  typeId = \"%s\";\n", node->typeId);
  }

  // default values for the fields:
  int numstatusbits = 0;
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == keventOut) {
      decl->value = default_fieldValue(decl->type);	// default value for given type
    }
    if (decl->kind == kfield || decl->kind == kexposedField || decl->kind == keventOut) {
       GenerateDefaultValueCode(fp, decl);
       numstatusbits++;
    }
  }

  if (!node->isParent) {
    // utility data initialisation: only for leaf node classes
    fprintf(fp, "  \n");
    CallUtilInit(fp, node);
  }
  fprintf(fp, "}\n\n");
}

static void GenerateDestructorSource(FILE *fp, SFNode *node)
{
  if (node->isParent)
    return;	// destructor for parent classes needs nothing to do

  // destructor:
  fprintf(fp, "void %s::destruct(void)\n", node->typeId);
  fprintf(fp, "{\n");
  CallUtilDestruct(fp, node);
  fprintf(fp, "  SFNode::destruct();\n");
  fprintf(fp, "}\n\n");

  fprintf(fp, "%s::~%s()\n",
	  node->typeId, node->typeId);
  fprintf(fp, "{\n");
  fprintf(fp, "  destruct();\n");
  fprintf(fp, "}\n\n");
}

static void GenerateCopyConstructorSource(FILE *fp, SFNode *node)
{
  interfaceDeclaration *decl;
  int i;

  if (node->isParent)
    return;	// only for leaf classes

  // clone(): makes interface members point to the new nodes own state and
  // copies the source state values. All values (members of
  // node superclasses or not) are handled at the leaf node class level.
  fprintf(fp, "void %s::clone_here(const %s& src)\n",
	  node->typeId, node->typeId);
  fprintf(fp, "{\n");
  // duplicate source interface members
  fprintf(fp, "  SFNode::clone(src);\n");
  fprintf(fp, "  \n");

  // make field or exposedField interface members point to the new nodes
  // own state (SFNode::clone() make exact copies of the source interface
  // members, but can't do this work because SFNode doesn't know the
  // leaf node state values).
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField || decl->kind == keventOut)
      fprintf(fp, "  interface[%d]->value = fieldValue(&%s%s);\n",
	      i,
	      (decl->kind!=keventOut && (decl->type == tSFNode || decl->type==tMFNode)) ? "_" : "",
	      decl->Id);
  }
  fprintf(fp, "  \n");

  // copy field/eventOut values from source
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField) {
      fprintf(fp, "  %s = src.%s;", decl->Id, decl->Id);
      if (decl->type==tSFNode || decl->type==tMFNode)
	fprintf(fp, "\t  _%s = src._%s;", decl->Id, decl->Id);
      fprintf(fp, "\n");
    }
    if (decl->kind == keventOut) {
      fprintf(fp, "  %s = src.%s;\n", decl->Id, decl->Id);
    }
  }

  // call clone_util() for this and all parent nodes
  fprintf(fp, "  \n");
  CallUtilClone(fp, node);
  fprintf(fp, "}\n\n");

  // copy constructor
  fprintf(fp, "%s::%s(const %s& src)\n",
	  node->typeId, node->typeId, node->typeId);
  fprintf(fp, "{\n");
  fprintf(fp, "  clone_here(src);\n");
  fprintf(fp, "}\n\n");

  // assignement operator
  fprintf(fp, "%s& %s::operator=(const %s& src)\n", 
	  node->typeId, node->typeId, node->typeId);
  fprintf(fp, "{\n");
  fprintf(fp, "  if (this != &src) {\n");
  fprintf(fp, "    destruct();\n");
  fprintf(fp, "    clone_here(src);\n");
  fprintf(fp, "  }\n");
  fprintf(fp, "  return *this;\n");
  fprintf(fp, "}\n\n");

  // instantiate: create new instance of 'this'. Let interface member
  // links and routes etc... point to the 'master' PROTO (if non-nil).
  // instantiate_interface() is a member of the SFNode base class and
  // will recursively instantiate children nodes.
  fprintf(fp, "SFNode* %s::instantiate(Proto *enclosing_proto)\n", node->typeId);
  fprintf(fp, "{\n");
  fprintf(fp, "  if (nameIndex>=0)\n");
  fprintf(fp, "    // named node: return pointer to pre-made instance\n");
  fprintf(fp, "    return enclosing_proto ? enclosing_proto->namedNodes[nameIndex] : this;\n");
  fprintf(fp, "  \n");
  fprintf(fp, "  %s* instance = new %s(*this);\n", node->typeId, node->typeId);
  fprintf(fp, "  instance->instantiate_interface(enclosing_proto);\n");
  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    if (decl->kind == kfield || decl->kind == kexposedField) {
      if (decl->type == tSFNode || decl->type == tMFNode) {
	fprintf(fp, "  instance->synchronize_%s();\n", decl->Id);
      }
    }
  }
  fprintf(fp, "  return instance;\n");
  fprintf(fp, "}\n\n");
}

// 2000-01-21: process_eventIn and validate_[exposed]field are now static functions
//	with node and fieldValue argument instead of private member functions due
//	to problems to cast the functions to member functions of the virtual base class.
static void GeneratePrivateSource(FILE *fp, SFNode *node)
{
  // private source: everything that depends on the node interface and
  // event processing internals (vrmlP.H). Mostly automatically
  // generated. Shall never be edited manually.

  interfaceDeclaration *decl;
  int i;

  fprintf(fp, "// %sP.C: %s nodes (private source).\n", node->typeId, node->typeId);
  fprintf(fp, "// Automatically generated with nodegen %d.%d%s - do not edit.\n",
	  MAJOR_VERSION, MINOR_VERSION, RELEASE);
  fprintf(fp, "\n");

  fprintf(fp, "#include \"%s.H\"\n", node->typeId);
  fprintf(fp, "#include \"xrmlP.H\"\n");
  fprintf(fp, "\n");

  fprintf(fp, "namespace xrml {\n");
  fprintf(fp, "int %s::major_version(void) { return %d; }\n", node->typeId, MAJOR_VERSION);
  fprintf(fp, "int %s::minor_version(void) { return %d; }\n", node->typeId, MINOR_VERSION);
  fprintf(fp, "bool %s::is_leaf_class(void) { return %s; }\n", node->typeId, node->isParent ? "false" : "true");
  fprintf(fp, "\n");

  //  fprintf(fp, "POOL* %s::pool = 0;\n", node->typeId);
  //  fprintf(fp, "\n");

  // test whether the node is of the given type.
  fprintf(fp, "bool Is%s(SFNode* node)\n", node->typeId);
  fprintf(fp, "{\n");
  fprintf(fp, "  return (dynamic_cast<%s*>(node) != 0);\n", node->typeId);
  fprintf(fp, "}\n\n");

  for (i=0, decl=node->interface; i<node->nrInterfaceDeclarations; i++, decl++) {
    switch (decl->kind) {
    case keventIn:
      GenerateEventInPrivateSource(fp, node, decl, i); break;
    case kfield:
      GenerateFieldPrivateSource(fp, node, decl, i); break;
    case kexposedField:
      GenerateExposedFieldPrivateSource(fp, node, decl, i); break;
    case keventOut:
      GenerateEventOutPrivateSource(fp, node, decl, i); break;
    default:
      Fatal(-1, "GeneratePrivateSource", "Invalid field kind %d\n", decl->kind);
    }
  }

  GenerateDefaultConstructorSource(fp, node);
  GenerateDestructorSource(fp, node);
  GenerateCopyConstructorSource(fp, node);
  fprintf(fp, "}  // namespace xrml\n");
}

static bool Backup(char *filename)
{
  FILE *fp = fopen(filename, "r");
  if (fp) {
    char cmd[200];
    sprintf(cmd, "mv %s %s.bak", filename, filename);
    fprintf(stderr, "%s ...\n", cmd);
    system(cmd);
    return true;
  }
  return false;
}

static void Extract(FILE *from, char *delim1, char *delim2, FILE *to)
{
  #define bufsize 1000
  char buf[bufsize];
  bool insection = false;
  while (fgets(buf, bufsize, from)) {
    if (!insection && strncmp(buf, delim1, strlen(delim1))==0)
      insection = true;
    else if (insection && strncmp(buf, delim2, strlen(delim2))==0)
      return;	// finished
    else if (insection)
      fputs(buf, to);
  }
}

static FILE* ExtractUtilIncludes(FILE *fp, SFNode *node)
{
  char fname[100];
  sprintf(fname, "%s.utinc", node->typeId);
  fprintf(stderr, "Extracting utility includes to %s ...\n", fname);
  FILE *utinc = fopen(fname, "w");
  char *delim1 = UtilityIncludesStartDelim(node);
  char *delim2 = UtilityIncludesEndDelim(node);
  Extract(fp, delim1, delim2, utinc);
  fclose(utinc);
  return fopen(fname, "r");
}

static FILE* ExtractUtilMembers(FILE *fp, SFNode *node)
{
  char fname[100];
  sprintf(fname, "%s.utmem", node->typeId);
  fprintf(stderr, "Extracting utility member code to %s ...\n", fname);
  FILE *utmem = fopen(fname, "w");
  char *delim1 = UtilityMembersStartDelim(node);
  char *delim2 = UtilityMembersEndDelim(node);
  Extract(fp, delim1, delim2, utmem);
  fclose(utmem);
  return fopen(fname, "r");
}

bool generate_private, generate_public, generate_header;

static void GenerateNodeCode(SFNode *node)
{
  char fname[100];
  FILE *fp;

  if (generate_header) {
    FILE *util_includes=0, *util_members=0;
    sprintf(fname, "%s.H", node->typeId);
    if (Backup(fname)) {
      char bakfname[100];
      sprintf(bakfname, "%s.H.bak", node->typeId);
      FILE *bak = fopen(bakfname, "r");
      if (!bak)
	Fatal(1, "GenerateNodeCode", "Can't open the backup file I just generated!!!");
      util_includes = ExtractUtilIncludes(bak, node);
      util_members = ExtractUtilMembers(bak, node);
      fclose(bak);
    }
    fprintf(stderr, "Generating %s ...\n", fname);
    fp = fopen(fname, "w");
    GenerateHeader(fp, node, util_includes, util_members);
    fclose(fp);
    if (util_includes) fclose(util_includes);
    if (util_members) fclose(util_members);
  }

  if (generate_public) {
    sprintf(fname, "%s.C", node->typeId);
    Backup(fname);
    fprintf(stderr, "Generating %s ...\n", fname);
    fp = fopen(fname, "w");
    GeneratePublicSource(fp, node);
    fclose(fp);
  }

  if (generate_private) {
    sprintf(fname, "%sP.C", node->typeId);
    Backup(fname);

    sprintf(fname, "%sP.C", node->typeId);
    fprintf(stderr, "Generating %s ...\n", fname);
    fp = fopen(fname, "w");
    GeneratePrivateSource(fp, node);
    fclose(fp);
  }
}

void DumpNodeCatalog(void)
{
  for (int i=0; i<nodeCatalog.size; i++)
    GenerateNodeCode(nodeCatalog[i]);
}

void RegisterBuiltin(SFNode *node)
{
  nodeCatalog.append(node);
}

SFNode *LookupBuiltin(char *typeId)
{
  for (int i=0; i<nodeCatalog.size; i++)
    if (strcmp(typeId, nodeCatalog[i]->typeId) == 0)
      return nodeCatalog[i];
  return 0;
}

static void AddDependency(SFNode *child, SFNode *parent)
{
  int i;
  for (i=0; i<child->dependNodes.size; i++)
    if (child->dependNodes[i] == parent)
      return;
  child->dependNodes.append(parent);
}

// the node is considered an ancestor of itself.
static bool IsAncestor(SFNode* child, SFNode* parent)
{
  if (strcmp(child->typeId, parent->typeId)==0)
    return true;
  for (int i=0; i<child->parentClassIds.size; i++)
    if (IsAncestor(LookupBuiltin(child->parentClassIds[i]), parent))
      return true;
  return false;
}

static void AddParent(SFNode* child, SFNode* parent)
{
  // remove ancestors of parent from child->parentNodes
  int i;
  array<SFNode*> tmp = child->parentNodes;
  for (i=0; i<tmp.size; i++)
    if (IsAncestor(parent, tmp[i]))
      tmp[i] = 0;
  child->parentNodes.size = 0;
  for (i=0; i<tmp.size; i++)
    if (tmp[i] != 0)
      child->parentNodes.append(tmp[i]);
  
  // check whether parent is ancestor of any already recorded parent node,
  for (i=0; i<child->parentNodes.size; i++)
    if (IsAncestor(child->parentNodes[i], parent))
      return;

  // if not, add the new parent node
  child->parentNodes.append(parent);
  AddDependency(child, parent);
}

// constructs list of parent nodes (duplicates etc... resolved)
// and list of all nodes upon which the implementation of the node class
// depends.
static void ResolveDependencies(SFNode *node)
{
  int i;

  for (i=0; i<node->parentClassIds.size; i++) {	
    SFNode *n = LookupBuiltin(node->parentClassIds[i]);
    if (!n)
      Fatal(1, NULL, "node %s parent %s type not found", 
	    node->typeId, node->parentClassIds[i]);
    else {
      AddParent(node, n);
      n->isParent = true;
    }
  }

  for (i=0; i<node->nrInterfaceDeclarations; i++) {	
    interfaceDeclaration *decl = &node->interface[i];
    if (!decl->nodeClassId) {
      if ((decl->kind==kexposedField || decl->kind==kfield) && 
	  (decl->type==tSFNode || decl->type==tMFNode))
	Warning(NULL, "No node %s.%s SF|MFNode class field type given", 
		node->typeId, decl->Id);
      continue;
    }

    SFNode *n = LookupBuiltin(decl->nodeClassId);
    if (!n)
      Fatal(1, NULL, "node %s.%s SF|MFNode class field %s type not found", 
	    node->typeId, decl->Id, decl->nodeClassId);
    else
      AddDependency(node, n);
  }
}

static void ResolveDependencies(void)
{
  for (int i=0; i<nodeCatalog.size; i++)
    ResolveDependencies(nodeCatalog[i]);
}

char *progname;

static void usage(void)
{
  fprintf(stderr, "Usage: %s catalog-file-name \"private\"|\"public\"|\"header\"|\"everything\" typeIds|\"all\"\n", progname);
}

int main(int argc, char **argv)
{
  int i;

  progname = argv[0];
  generate_private = generate_public = generate_header = false;

  if (argc<4) {
    usage();
    return 1;
  }

  VRMLParse(argv[1]);
  ResolveDependencies();

  for (i=2; i<argc; i++) {
    if (strcmp(argv[i], "private") == 0)
      generate_private = true;
    else if (strcmp(argv[i], "public") == 0)
      generate_public = true;
    else if (strcmp(argv[i], "header") == 0)
      generate_header = true;
    else if (strcmp(argv[i], "everything") == 0)
      generate_private = generate_public = generate_header = true;
    else
      break;
  }

  if (i >= argc) {
    usage();
    return 1;
  }

  if (strcmp(argv[i], "all") == 0)
    DumpNodeCatalog();
  else { 
    for (; i<argc; i++) {
      SFNode *node = LookupBuiltin(argv[i]);
      if (!node)
	fprintf(stderr, "ERROR: no builtin type %s node found.\n", argv[i]);
      else
	GenerateNodeCode(node);
    }
  }

  return 0;  
}
