/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005
 *					All rights reserved
 *
 *  This file is part of GPAC / X3D Scene Graph Generator sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <gpac/m4_tools.h>
#include <time.h>

#define COPYRIGHT "/*\n *			GPAC - MPEG-4 Systems C Development Kit\n *\n *			Copyright (c) Jean Le Feuvre 2000-2005\n *					All rights reserved\n *\n *  This file is part of GPAC / X3D Scene Graph sub-project\n *\n *  GPAC is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2, or (at your option)\n *  any later version.\n *\n *  GPAC is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.	\n *\n *  You should have received a copy of the GNU General Public License\n *  along with GNU Make; see the file COPYING.  If not, write to\n *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n */\n"

static char *CurrentLine;

void PrintUsage()
{
	printf("X3DGen [skip_file]\n"
			"\nGPAC X3D Scene Graph generator\n"
			"\n"
			"\nskip_file: txt file with list of nodes to leave unimplemented"
			"Generated Files are directly updated in the GPAC distribution - do NOT try to change this\n\n"
			"Written by Jean Le Feuvre - (c) 2000-2005\n"
			);
}

//a node field
typedef struct
{
	char type[50];
	//SFxxx, MFxxx
	char familly[50];
	//name
	char name[1000];
	//default value
	char def[100];
	//bounds
	u32 hasBounds;
	char b_min[20];
	char b_max[20];
} X3DField;

//NDTs

//a BIFS node
typedef struct
{
	char name[1000];
	//NDT info. NDT are created in alphabetical order
	Chain *NDT;
	Chain *Fields;
	u8 hasDefault;
	char Child_NDT_Name[1000];
	u8 skip_impl;

} X3DNode;


void skip_sep(char *sep)
{
	//skip separaors
	while (*CurrentLine && strchr(sep, *CurrentLine)) {
		CurrentLine = CurrentLine + 1;
		//end of line - no token
		if (*CurrentLine == '\n') return;
	}
}

//note that we increment the line no matter what
u32 GetNextToken(char *token, char *sep)
{
	u32 i , j = 0;

	strcpy(token, "");
	
	//skip separaors
	while (*CurrentLine && strchr(sep, *CurrentLine)) {
		CurrentLine = CurrentLine + 1;
		j ++;
		//end of line - no token
		if (*CurrentLine == '\n') return 0;
	}

	//copy token untill next blank
	i=0;
	while (1) {
		//bad line
		if (! *CurrentLine) {
			token[i] = 0;
			return 0;
		}
		//end of token or end of line
		if (strchr(sep, *CurrentLine) || (*CurrentLine == '\n') ) {
			token[i] = 0;
			CurrentLine = CurrentLine + 1;
			return i;
		} else {
			token[i] = *CurrentLine;
		}
		CurrentLine = CurrentLine + 1;
		i++;
		j++;
	}
	return 1;
}

X3DField *BlankField()
{
	X3DField *n = malloc(sizeof(X3DField));
	memset(n, 0, sizeof(X3DField));
	return n;
}


X3DNode *BlankNode()
{
	X3DNode *n = malloc(sizeof(X3DNode));
	memset(n, 0, sizeof(X3DNode));
	n->NDT = NewChain();
	n->Fields = NewChain();
	return n;
}

u8 IsNDT(Chain *NDTs, char *famName)
{
	u32 i;
	char *ndtName;
	for (i=0; i<ChainGetCount(NDTs); i++) {
		ndtName = ChainGetEntry(NDTs, i);
		//remove SF / MF as we don't need that
		if (!strcmp(ndtName+2, famName+2)) return 1;
	}
	return 0;
}

void CheckInTable(char *token, Chain *NDTs)
{
	u32 i;
	char *p;
	for (i=0; i<ChainGetCount(NDTs); i++) {
		p = ChainGetEntry(NDTs, i);
		if (!strcmp(p, token)) return;
	}
	p = malloc(strlen(token)+1);
	strcpy(p, token);
	ChainAddEntry(NDTs, p);
}

/*type: 0: header, 1: source*/
FILE *BeginFile(u32 type)
{
	FILE *f;

	char sPath[M4_MAX_PATH];

	if (!type) {
		sprintf(sPath, "..%c..%c..%cinclude%cgpac%cm4_x3d_nodes.h", M4_PATH_SEPARATOR, M4_PATH_SEPARATOR, M4_PATH_SEPARATOR, M4_PATH_SEPARATOR, M4_PATH_SEPARATOR);
	} else {
		sprintf(sPath, "..%c..%c..%cM4Systems%cSceneGraph%cx3d_nodes.c", M4_PATH_SEPARATOR, M4_PATH_SEPARATOR, M4_PATH_SEPARATOR, M4_PATH_SEPARATOR, M4_PATH_SEPARATOR);
	}
	
	f = fopen(sPath, "wt");
	fprintf(f, "%s\n", COPYRIGHT);

	{
		time_t rawtime;
		time(&rawtime);
		fprintf(f, "\n/*\n\tDO NOT MOFIFY - File generated on GMT %s\n\tBY X3DGen for GPAC Version %s\n*/\n\n", asctime(gmtime(&rawtime)), M4_VERSION);
	}

	if (!type) {
		fprintf(f, "#ifndef _M4_X3D_NODES_H\n");
		fprintf(f, "#define _M4_X3D_NODES_H\n\n");
		fprintf(f, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
	}
	return f;
}

void EndFile(FILE *f, u32 type)
{
	if (!type) {
		fprintf(f, "#ifdef __cplusplus\n}\n#endif\n\n");
		fprintf(f, "\n\n#endif\t\t/*_M4_X3D_NODES_H*/\n\n");
	}
	fclose(f);
}

void TranslateToken(char *token)
{
	if (!strcmp(token, "+I") || !strcmp(token, "I")) {
		strcpy(token, "M4_MAX_FLOAT");
	}
	else if (!strcmp(token, "-I")) {
		strcpy(token, "M4_MIN_FLOAT");
	}
}



void WriteNodesFile(Chain *BNodes, Chain *NDTs)
{
	FILE *f;
	u32 i, j;
	X3DNode *n;
	X3DField *bf;
	f = BeginFile(0);

	fprintf(f, "#include <gpac/m4_scenegraph_vrml.h>\n\n");

	//write all tags
	fprintf(f, "\n\nenum {\n");
	
	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (i)
			fprintf(f, ",\n\tTAG_X3D_%s", n->name);
		else
			fprintf(f, "\tTAG_X3D_%s = TAG_RANGE_FIRST_X3D", n->name);
	}
	fprintf(f, ",\n\tTAG_LastImplementedX3D\n};\n\n");

	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (n->skip_impl) continue;
		fprintf(f, "typedef struct _tagX3D%s\n{\n", n->name);
		fprintf(f, "\tBASE_NODE\n");

		/*write children field*/
		for (j=0; j<ChainGetCount(n->Fields); j++) {
			bf = ChainGetEntry(n->Fields, j);
			if (!stricmp(bf->name, "addChildren") || !strcmp(bf->name, "removeChildren")) continue;
			if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) {
				fprintf(f, "\tVRML_CHILDREN\n");
				break;
			}
		}
		for (j=0; j<ChainGetCount(n->Fields); j++) {
			bf = ChainGetEntry(n->Fields, j);
			
			if (!strcmp(bf->name, "addChildren") || !strcmp(bf->name, "removeChildren")) continue;
			if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) continue;

			if (strstr(bf->familly, "Node")) {		
				//this is a POINTER to a node 
				if (strstr(bf->familly, "SF")) {
					fprintf(f, "\tSFNode *%s;\t/*%s*/\n", bf->name, bf->type);
				} else {
					//this is a POINTER to a chain
					fprintf(f, "\tChain *%s;\t/*%s*/\n", bf->name, bf->type);
				}
			} else {
				fprintf(f, "\t%s %s;\t/*%s*/\n", bf->familly, bf->name, bf->type);
			}
			if (!strcmp(bf->type, "eventIn")) 
				fprintf(f, "\tvoid (*on_%s)(SFNode *pThis);\t/*eventInHandler*/\n", bf->name);
		}
		fprintf(f, "} X_%s;\n\n\n", n->name);
	}

	EndFile(f, 0);

}

void WriteNodeFields(FILE *f, X3DNode *n)
{
	u32 i;
	X3DField *bf;

	fprintf(f, "\nstatic u32 %s_get_field_count(SFNode *node, u8 dummy)\n{\n\treturn %d;\n}\n\n", n->name, ChainGetCount(n->Fields));
	fprintf(f, "static M4Err %s_get_field(SFNode *node, FieldInfo *info)\n{\n\tswitch (info->fieldIndex) {\n", n->name);
	for (i=0;i<ChainGetCount(n->Fields); i++) {
		bf = ChainGetEntry(n->Fields, i);

		fprintf(f, "\tcase %d:\n", i);
		
		fprintf(f, "\t\tinfo->name = \"%s\";\n", bf->name);

		//skip all eventIn
		if (!strcmp(bf->type, "eventIn")) {
			fprintf(f, "\t\tinfo->eventType = ET_EventIn;\n");
			fprintf(f, "\t\tinfo->on_event_in = ((X_%s *)node)->on_%s;\n", n->name, bf->name);
		}
		else if (!strcmp(bf->type, "eventOut")) {
			fprintf(f, "\t\tinfo->eventType = ET_EventOut;\n");
		}
		else if (!strcmp(bf->type, "field")) {
			fprintf(f, "\t\tinfo->eventType = ET_Field;\n");
		}
		else {
			fprintf(f, "\t\tinfo->eventType = ET_ExposedField;\n");
		}

		if (strstr(bf->familly, "Node")) {
			if (strstr(bf->familly, "MF")) {
				fprintf(f, "\t\tinfo->fieldType = FT_MFNode;\n");
			} else {
				fprintf(f, "\t\tinfo->fieldType = FT_SFNode;\n");
			}
			//always remove the SF or MF, as all NDTs are SFXXX
			fprintf(f, "\t\tinfo->NDTtype = NDT_SF%s;\n", bf->familly+2);
			fprintf(f, "\t\tinfo->far_ptr = & ((X_%s *)node)->%s;\n", n->name, bf->name);
		} else {
			//no ext type
			fprintf(f, "\t\tinfo->fieldType = FT_%s;\n", bf->familly);
			fprintf(f, "\t\tinfo->far_ptr = & ((X_%s *) node)->%s;\n", n->name, bf->name);
		}
		fprintf(f, "\t\treturn M4OK;\n");
	}
	fprintf(f, "\tdefault:\n\t\treturn M4BadParam;\n\t}\n}\n\n");

}

void WriteNodeCode(Chain *BNodes, FILE *vrml_code)
{
	char token[20], tok[20];
	char *store;
	u32 i, j, k, go;
	X3DField *bf;
	X3DNode *n;

	fprintf(vrml_code, "\n#include <gpac/m4_x3d_nodes.h>\n");
	fprintf(vrml_code, "\n#include <gpac/intern/m4_scenegraph_dev.h>\n");
	fprintf(vrml_code, "\n/*for NDT tag definitions*/\n#include <gpac/m4_mpeg4_nodes.h>\n");

	for (k=0; k<ChainGetCount(BNodes); k++) {
		n = ChainGetEntry(BNodes, k);
		if (n->skip_impl) continue;
		fprintf(vrml_code, "\n/*\n\t%s Node deletion\n*/\n\n", n->name);
		fprintf(vrml_code, "static void %s_Del(SFNode *node)\n{\n\tX_%s *p = (X_%s *) node;\n", n->name, n->name, n->name);

		for (i=0; i<ChainGetCount(n->Fields); i++) {
			bf = ChainGetEntry(n->Fields, i);
			//nothing on child events
			if (!strcmp(bf->name, "addChildren")) continue;
			if (!strcmp(bf->name, "removeChildren")) continue;
			
			//delete all children node
			if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) {
				fprintf(vrml_code, "\tDestroyVRMLParent(node);\t\n");
				continue;
			}

			//delete ALL fields that must be deleted: this includes eventIn and out since 
			//all fields are defined in the node
			if (!strcmp(bf->familly, "MFInt")
				|| !strcmp(bf->familly, "MFFloat")
				|| !strcmp(bf->familly, "MFDouble")
				|| !strcmp(bf->familly, "MFBool")
				|| !strcmp(bf->familly, "MFInt32")
				|| !strcmp(bf->familly, "MFColor")
				|| !strcmp(bf->familly, "MFRotation")
				|| !strcmp(bf->familly, "MFString")
				|| !strcmp(bf->familly, "MFTime")
				|| !strcmp(bf->familly, "MFVec2f")
				|| !strcmp(bf->familly, "MFVec3f")
				|| !strcmp(bf->familly, "MFVec4f")
				|| !strcmp(bf->familly, "MFVec2d")
				|| !strcmp(bf->familly, "MFVec3d")
				|| !strcmp(bf->familly, "MFURL")
				|| !strcmp(bf->familly, "MFScript")
				|| !strcmp(bf->familly, "SFString")
				|| !strcmp(bf->familly, "SFURL")
				|| !strcmp(bf->familly, "SFImage")
				
				) {
				fprintf(vrml_code, "\t%s_Del(p->%s);\n", bf->familly, bf->name);
			} else if (strstr(bf->familly, "Node")) {		
				//this is a POINTER to a node 
				if (strstr(bf->familly, "SF")) {
					fprintf(vrml_code, "\tNode_Unregister((SFNode *) p->%s, node);\t\n", bf->name);
				} else {
					//this is a POINTER to a chain
					fprintf(vrml_code, "\tNodeList_Delete((Chain *) p->%s, node);\t\n", bf->name);
				}
			}
		}
		/*avoids gcc warnings in case no field to delete*/
		fprintf(vrml_code, "\tSFNode_Delete((SFNode *)p);\n}\n\n");

		//node fields
		WriteNodeFields(vrml_code, n);

		//
		//		Constructor
		//

		fprintf(vrml_code, "\n\nstatic SFNode *%s_Create()\n{\n\tX_%s *p;\n\tSAFEALLOC(p, sizeof(X_%s));\n", n->name, n->name, n->name);
		fprintf(vrml_code, "\tif(!p) return NULL;\n");
		fprintf(vrml_code, "\tNode_Setup((SFNode *)p, TAG_X3D_%s);\n", n->name);

		for (i=0; i<ChainGetCount(n->Fields); i++) {
			bf = ChainGetEntry(n->Fields, i);
			//setup all children node
			if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) {
				fprintf(vrml_code, "\tSetupVRMLParent((SFNode *) p);\n");
				break;
			}
			else if ( strstr(bf->familly, "Node") && strncmp(bf->type, "event", 5) ) {		
				//this is a POINTER to a node 
				if (strstr(bf->familly, "MF")) {
					//this is a POINTER to a chain
					fprintf(vrml_code, "\tp->%s = NewChain();\t\n", bf->name);
				}
			}
			/*special case for SFCommandBuffer: we also create a command list*/
			if (!stricmp(bf->familly, "SFCommandBuffer")) {
				fprintf(vrml_code, "\tp->%s.commandList = NewChain();\t\n", bf->name);
			}
		}

		//setup pointers
		fprintf(vrml_code, "\n#ifdef NODE_USE_POINTERS\n\n");
		fprintf(vrml_code, "\t((SFNode *)p)->sgprivate->name = \"%s\";\n", n->name);
		fprintf(vrml_code, "\t((SFNode *)p)->sgprivate->node_del = %s_Del;\n", n->name);
		fprintf(vrml_code, "\t((SFNode *)p)->sgprivate->get_field_count = %s_get_field_count;\n", n->name);
		fprintf(vrml_code, "\t((SFNode *)p)->sgprivate->get_field = %s_get_field;\n", n->name);

		//check if we have a child node
		for (i=0; i<ChainGetCount(n->Fields); i++) {
			bf = ChainGetEntry(n->Fields, i);
			if ( !strcmp(bf->name, "children") || 
					( !strstr(bf->type, "event") && strstr(bf->familly, "MF") && strstr(bf->familly, "Node")) ) {

				sprintf(n->Child_NDT_Name, "NDT_SF%s", bf->familly+2);
				break;
			}
		}
		fprintf(vrml_code, "\n#endif\n\n");

		fprintf(vrml_code, "\n\t/*default field values*/\n");
		
		for (i=0; i<ChainGetCount(n->Fields); i++) {
			bf = ChainGetEntry(n->Fields, i);

			//nothing on eventIn or Out
			if (!strcmp(bf->type, "eventIn")) continue;
			if (!strcmp(bf->type, "eventOut")) continue;

			if (!strcmp(bf->def, "")) continue;

			//no default on nodes
			if (strstr(bf->familly, "Node")) continue;
			//extract default falue
			
			//
			//		SF Fields
			//
			
			//SFBool
			if (!strcmp(bf->familly, "SFBool")) {
				if (!strcmp(bf->def, "1") || !strcmp(bf->def, "TRUE")) 
					fprintf(vrml_code, "\tp->%s = 1;\n", bf->name);
			}
			//SFFloat
			else if (!strcmp(bf->familly, "SFFloat")) {
				fprintf(vrml_code, "\tp->%s = FLT2FIX(%s);\n", bf->name, bf->def);
			}
			//SFDouble
			else if (!strcmp(bf->familly, "SFDouble")) {
				fprintf(vrml_code, "\tp->%s = (SFDouble) %s;\n", bf->name, bf->def);
			}
			//SFTime
			else if (!strcmp(bf->familly, "SFTime")) {
				fprintf(vrml_code, "\tp->%s = %s;\n", bf->name, bf->def);
			}
			//SFInt32
			else if (!strcmp(bf->familly, "SFInt32")) {
				fprintf(vrml_code, "\tp->%s = %s;\n", bf->name, bf->def);
			}
			//SFColor
			else if (!strcmp(bf->familly, "SFColor")) {
				CurrentLine = bf->def;
				GetNextToken(token, " ");
				TranslateToken(token);

				fprintf(vrml_code, "\tp->%s.red = FLT2FIX(%s);\n", bf->name, token);
				GetNextToken(token, " ");
				fprintf(vrml_code, "\tp->%s.green = FLT2FIX(%s);\n", bf->name, token);
				GetNextToken(token, " ");
				fprintf(vrml_code, "\tp->%s.blue = FLT2FIX(%s);\n", bf->name, token);
			}
			//SFVec2f
			else if (!strcmp(bf->familly, "SFVec2f")) {
				CurrentLine = bf->def;
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.x = FLT2FIX(%s);\n", bf->name, token);
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.y = FLT2FIX(%s);\n", bf->name, token);
			}
			//SFVec2d
			else if (!strcmp(bf->familly, "SFVec2d")) {
				CurrentLine = bf->def;
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.x = (SFDouble) %s;\n", bf->name, token);
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.y = (SFDouble) %s;\n", bf->name, token);
			}
			//SFVec3f
			else if (!strcmp(bf->familly, "SFVec3f")) {
				CurrentLine = bf->def;
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.x = FLT2FIX(%s);\n", bf->name, token);
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.y = FLT2FIX(%s);\n", bf->name, token);
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.z = FLT2FIX(%s);\n", bf->name, token);
			}
			//SFVec3d
			else if (!strcmp(bf->familly, "SFVec3d")) {
				CurrentLine = bf->def;
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.x = (SFDouble) %s;\n", bf->name, token);
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.y = (SFDouble) %s;\n", bf->name, token);
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.z = (SFDouble) %s;\n", bf->name, token);
			}
			//SFVec4f & SFRotation
			else if (!strcmp(bf->familly, "SFVec4f") || !strcmp(bf->familly, "SFRotation")) {
				CurrentLine = bf->def;
				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.x = FLT2FIX(%s);\n", bf->name, token);

				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.y = FLT2FIX(%s);\n", bf->name, token);

				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.z = FLT2FIX(%s);\n", bf->name, token);

				GetNextToken(token, " ");
				TranslateToken(token);
				fprintf(vrml_code, "\tp->%s.q = FLT2FIX(%s);\n", bf->name, token);
			}
			//SFString
			else if (!strcmp(bf->familly, "SFString")) {
				fprintf(vrml_code, "\tp->%s.buffer = malloc(sizeof(char) * %d);\n", bf->name, strlen(bf->def)+1);
				fprintf(vrml_code, "\tstrcpy(p->%s.buffer, \"%s\");\n", bf->name, bf->def);
			}
		
			//
			//		MF Fields
			//
			//MFFloat
			else if (!strcmp(bf->familly, "MFFloat")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, " ,")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFFloat)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, " ,")) go = 0;
					TranslateToken(token);
					fprintf(vrml_code, "\tp->%s.vals[%d] = FLT2FIX(%s);\n", bf->name, j, token);
					j+=1;
				}
			}
			//MFDouble
			else if (!strcmp(bf->familly, "MFDouble")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, " ,")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFFloat)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, " ,")) go = 0;
					TranslateToken(token);
					fprintf(vrml_code, "\tp->%s.vals[%d] = (SFDouble) %s;\n", bf->name, j, token);
					j+=1;
				}
			}
			//MFVec2f
			else if (!strcmp(bf->familly, "MFVec2f")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFVec2f)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].x = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].y = FLT2FIX(%s);\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFVec2d
			else if (!strcmp(bf->familly, "MFVec2d")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFVec2f)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].x = (SFDouble) %s;\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].y = (SFDouble) %s;\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFVec3f
			else if (!strcmp(bf->familly, "MFVec3f")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFVec3f)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].x = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].y = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].z = FLT2FIX(%s);\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFVec3d
			else if (!strcmp(bf->familly, "MFVec3d")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFVec3f)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].x = (SFDouble) %s;\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].y = (SFDouble) %s;\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].z = (SFDouble) %s;\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFVec4f & MFRotation
			else if (!strcmp(bf->familly, "MFVec4f") || !strcmp(bf->familly, "MFRotation")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(M4Vec4)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].x = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].y = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].z = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d].q = FLT2FIX(%s);\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFInt32
			else if (!strcmp(bf->familly, "MFInt32")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFInt32)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " ");
					fprintf(vrml_code, "\tp->%s.vals[%d] = %s;\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFColor
			else if (!strcmp(bf->familly, "MFColor")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFColor)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " ");
					fprintf(vrml_code, "\tp->%s.vals[%d].red = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					fprintf(vrml_code, "\tp->%s.vals[%d].green = FLT2FIX(%s);\n", bf->name, j, tok);
					GetNextToken(tok, " ");
					fprintf(vrml_code, "\tp->%s.vals[%d].blue = FLT2FIX(%s);\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFString
			else if (!strcmp(bf->familly, "MFString")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFString)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " \"");
					fprintf(vrml_code, "\tp->%s.vals[%d] = malloc(sizeof(char) * %d);\n", bf->name, j, strlen(tok)+1);
					fprintf(vrml_code, "\tstrcpy(p->%s.vals[%d], \"%s\");\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}
			//MFTime
			else if (!strcmp(bf->familly, "MFTime")) {
				j = 0;
				CurrentLine = bf->def;
				while (GetNextToken(token, ",")) j++;
				j+=1;
				fprintf(vrml_code, "\tp->%s.vals = malloc(sizeof(SFTime)*%d);\n", bf->name, j);
				fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j);
				j = 0;
				go = 1;
				CurrentLine = bf->def;
				while (go) {
					if (!GetNextToken(token, ",")) go = 0;
					store = CurrentLine;
					CurrentLine = token;
					GetNextToken(tok, " \"");
					TranslateToken(tok);
					fprintf(vrml_code, "\tp->%s.vals[%d] = %s;\n", bf->name, j, tok);
					j+=1;
					CurrentLine = store;
				}
			}

			//other nodes
			else if (!strcmp(bf->familly, "SFImage")) {
				//we currently only have SFImage, with NO texture so do nothing
			}
			//unknown init (for debug)
			else {
				fprintf(vrml_code, "UNKNOWN FIELD (%s);\n", bf->familly);

			}
		}
		fprintf(vrml_code, "\treturn (SFNode *)p;\n}\n\n");

	}

	fprintf(vrml_code, "\n\n\n");

	//creator function
	fprintf(vrml_code, "SFNode *X3D_CreateNode(u32 NodeTag)\n{\n\tswitch (NodeTag) {\n");
	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (!n->skip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:\n\t\treturn %s_Create();\n", n->name, n->name);
	}
	fprintf(vrml_code, "\tdefault:\n\t\treturn NULL;\n\t}\n}\n\n");

	fprintf(vrml_code, "#ifndef NODE_USE_POINTERS\n");

	fprintf(vrml_code, "const char *X3D_GetNodeName(u32 NodeTag)\n{\n\tswitch (NodeTag) {\n");
	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (!n->skip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:\n\t\treturn \"%s\";\n", n->name, n->name);
	}
	fprintf(vrml_code, "\tdefault:\n\t\treturn \"Unknown Node\";\n\t}\n}\n\n");

	fprintf(vrml_code, "void X3D_Node_Del(SFNode *node)\n{\n\tswitch (node->sgprivate->tag) {\n");
	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (!n->skip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:\n\t\t%s_Del(node); return;\n", n->name, n->name);
	}
	fprintf(vrml_code, "\tdefault:\n\t\treturn;\n\t}\n}\n\n");

	fprintf(vrml_code, "u32 X3DNode_GetFieldCount(SFNode *node)\n{\n\tswitch (node->sgprivate->tag) {\n");
	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (!n->skip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:return %s_get_field_count(node, 0);\n", n->name, n->name);
	}
	fprintf(vrml_code, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n");

	fprintf(vrml_code, "M4Err X3DNode_GetField(SFNode *node, FieldInfo *field)\n{\n\tswitch (node->sgprivate->tag) {\n");
	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (!n->skip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s: return %s_get_field(node, field);\n", n->name, n->name);
	}
	fprintf(vrml_code, "\tdefault:\n\t\treturn M4BadParam;\n\t}\n}\n\n");

	fprintf(vrml_code, "\n#endif\n\n");

	fprintf(vrml_code, "\nu32 X3D_GetTagByName(const char *node_name)\n{\n\tif(!node_name) return 0;\n");
	for (i=0; i<ChainGetCount(BNodes); i++) {
		n = ChainGetEntry(BNodes, i);
		if (!n->skip_impl) fprintf(vrml_code, "\tif (!stricmp(node_name, \"%s\")) return TAG_X3D_%s;\n", n->name, n->name);
	}
	fprintf(vrml_code, "\treturn 0;\n}\n\n");

}


static u32 IsNodeInTable(X3DNode *node, char *NDTName)
{
	u32 i;
	char *ndt;

	for (i=0; i<ChainGetCount(node->NDT); i++) {
		ndt = ChainGetEntry(node->NDT, i);
		if (!strcmp(ndt, NDTName)) return 1;
	}
	return 0;
}

static u32 GetNDTCount(char *NDTName, Chain *XNodes)
{
	u32 i, nodeCount;
	X3DNode *n;
	nodeCount = 0;
	for (i=0; i<ChainGetCount(XNodes); i++) {
		n = ChainGetEntry(XNodes, i);
		if (!IsNodeInTable(n, NDTName)) continue;
		nodeCount++;
	}
	return nodeCount;
}

/*write NDTs*/
void WriteNDT(FILE *f, Chain *XNodes, Chain *NDTs)
{
	u32 i, j, count;
	Bool first;
	X3DNode *n;
	char *NDTName;
	fprintf(f, "\n\n/* NDT X3D */\n\n");

	//for all NDTs
	for (i=0; i<ChainGetCount(NDTs); i++) {
		NDTName = ChainGetEntry(NDTs, i);
		count = GetNDTCount(NDTName, XNodes);
		if (!count) continue;
		
		fprintf(f, "#define %s_X3D_Count\t%d\n", NDTName, count);
		fprintf(f, "static const u32 %s_X3D_TypeToTag[%d] = {\n", NDTName, count);
		first = 1;
		//browse each node.
		for (j=0; j<ChainGetCount(XNodes); j++) {
			n = ChainGetEntry(XNodes, j);
			if (!IsNodeInTable(n, NDTName)) continue;
			
			if (first) {
				fprintf(f, " TAG_X3D_%s", n->name);
				first = 0;
			} else {
				fprintf(f, ", TAG_X3D_%s", n->name);
			}
		}
		fprintf(f, "\n};\n\n");
	}

	//NodeTag complete translation
	fprintf(f, "\n\n\nBool X3D_IsNodeInTable(u32 NDT_Tag, u32 NodeTag)\n{\n\tconst u32 *types;\n\tu32 count, i;\n\tif (!NodeTag) return 0;\n\ttypes = NULL; count = 0;\n");

	fprintf(f, "\tswitch (NDT_Tag) {\n");
	for (i=0; i<ChainGetCount(NDTs); i++) {
		NDTName = ChainGetEntry(NDTs, i);
		count = GetNDTCount(NDTName, XNodes);
		if (!count) continue;
		fprintf(f, "\tcase NDT_%s:\n\t\ttypes = %s_X3D_TypeToTag; count = %s_X3D_Count; break;\n", NDTName, NDTName, NDTName);
	}
	fprintf(f, "\tdefault:\n\t\treturn 0;\n\t}\n");

	fprintf(f, "\tfor(i=0; i<count; i++) { if (types[i]==NodeTag) return 1;}\n");
	fprintf(f, "\treturn 0;\n}\n");
}

void ParseTemplateFile(FILE *nodes, Chain *BNodes, Chain *NDTs)
{
	char sLine[2000];
	char token[100];
	char *p;
	X3DNode *n;
	X3DField *f;
	u32 j, i, k;

	//get lines one by one
	n = NULL;
	while (!feof(nodes)) {
		fgets(sLine, 2000, nodes);
		//skip comment and empty lines
		if (sLine[0] == '#') continue;
		if (sLine[0] == '\n') continue;

		CurrentLine = sLine;
		
		//parse the line till end of line
		while (GetNextToken(token, " \t")) {

			//this is a new node
			if (!strcmp(token, "PROTO") ) {
				n = BlankNode();
				ChainAddEntry(BNodes, n);

				//get its name
				GetNextToken(n->name, " \t[");

				//extract the NDTs
				GetNextToken(token, "\t[ %#=");
				if (strcmp(token, "NDT")) {
					printf("Corrupted template file\n");
					return;
				}
				while (1) {
					GetNextToken(token, "=, \t");
					//done with NDTs
					if (!token[0]) break;

					//update the NDT list
					CheckInTable(token, NDTs);
					p = malloc(strlen(token)+1);
					strcpy(p, token);
					ChainAddEntry(n->NDT, p);
				}
			}
			//this is NOT a field
			else if (token[0] == ']' || token[0] == '{' || token[0] == '}' ) {
				break;
			}
			//parse a field
			else {
				if (!n) {
					printf("Corrupted template file\n");
					return;
				}
				f = BlankField();
				ChainAddEntry(n->Fields, f);
				
				//get the field type
				strcpy(f->type, token);
				GetNextToken(f->familly, " \t");
				GetNextToken(f->name, " \t");
				//fix for our own code :(
				if (!strcmp(f->name, "tag")) strcpy(f->name, "_tag");

				//has default
				skip_sep(" \t");
				if (GetNextToken(token, "#\t")) {
					j=0;
					while (token[j] == ' ') j+=1;
					if (token[j] == '[') j+=1;
					if (token[j] == '"') j+=1;

					if (token[j] != '"' && token[j] != ']') {
						strcpy(f->def, token+j);
						j=1;
						while (j) {
							switch (f->def[strlen(f->def)-1]) {
							case ' ':
							case '"':
							case ']':
								f->def[strlen(f->def)-1] = 0;
								break;
							default:
								j=0;
								break;
							}
						}
					} else {
						strcpy(f->def, "");
					}
					if (!strcmp(f->familly, "SFFloat")) {
						if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) {
							strcpy(f->def, "M4_MAX_FLOAT");
						} else if (!strcmp(f->def, "-I")) {
							strcpy(f->def, "M4_MIN_FLOAT");
						}
					} else if (!strcmp(f->familly, "SFTime")) {
						if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) {
							strcpy(f->def, "M4_MAX_FLOAT");
						} else if (!strcmp(f->def, "-I")) {
							strcpy(f->def, "M4_MIN_FLOAT");
						}
					} else if (!strcmp(f->familly, "SFInt32")) {
						if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) {
							strcpy(f->def, "2 << 31");
						} else if (!strcmp(f->def, "-I")) {
							strcpy(f->def, "- (2 << 31)");
						}
					}
				}
				//has other
				while (GetNextToken(token, " \t#%=")) {
					switch (token[0]) {
					//bounds
					case 'b':
					case 'q':
					case 'a':
						printf("Corrupted X3D template file (quantization/animation not allowed)\n");
						ChainDeleteItem(n->Fields, f);
						free(f);
						return;
					default:
						break;
					}
				}
				/*we ignore these*/
				if (!stricmp(f->name, "bboxCenter") || !stricmp(f->name, "bboxSize")) {
					ChainDeleteItem(n->Fields, f);
					free(f);
				}
			}
		}
	}


	for (k=0; k<ChainGetCount(BNodes); k++) {
		n = ChainGetEntry(BNodes, k);

		for (i=0; i<ChainGetCount(n->Fields); i++) {
			f = ChainGetEntry(n->Fields, i);
			//nothing on events
			if (!strcmp(f->type, "eventIn")) continue;
			if (!strcmp(f->type, "eventOut")) continue;
			if (!strcmp(f->def, "")) continue;
			if (strstr(f->familly, "Node")) continue;
			n->hasDefault = 1;
		}
	}
}


void WriteNodeDump(FILE *f, X3DNode *n)
{
	X3DField *bf;
	u32 i;

	fprintf(f, "static const char *%s_FieldName[] = {\n", n->name);
	for (i=0; i<ChainGetCount(n->Fields); i++) {
		bf = ChainGetEntry(n->Fields, i);
		if (!i) {
			fprintf(f, " \"%s\"", bf->name);
		} else {
			fprintf(f, ", \"%s\"", bf->name);
		}
	}
	fprintf(f, "\n};\n\n");
}


void parse_profile(Chain *nodes, FILE *prof)
{
	char sLine[2000];
	X3DNode *n;
	Bool found;
	u32 i;

	while (!feof(prof)) {
		fgets(sLine, 2000, prof);
		//skip comment and empty lines
		if (sLine[0] == '#') continue;
		if (sLine[0] == '\n') continue;
		if (strstr(sLine, "Proximity")) 
			found = 0;
		found = 1;
		while (found) {
			switch (sLine[strlen(sLine)-1]) {
			case '\n':
			case '\r':
			case ' ':
				sLine[strlen(sLine)-1] = 0;
				break;
			default:
				found = 0;
				break;
			}
		}

//		if (0 && !stricmp(sLine, "Appearance") || !stricmp(sLine, "Shape") || !stricmp(sLine, "Sound2D") ) {
		if (0) {
			printf("Warning: cannot disable node %s (required in all BIFS profiles)\n", sLine);
		} else {
			found = 0;
			for (i=0; i<ChainGetCount(nodes); i++) {
				n = ChainGetEntry(nodes, i);
				if (!stricmp(n->name, sLine)) {
					n->skip_impl = 1;
					found = 1;
					break;
				}
			}
			if (!found) printf("cannot disable %s: node not found\n", sLine);
		}
	}
}

int main (int argc, char **argv)
{
	FILE *nodes, *pf;
	Chain *XNodes, *NDTs;
	X3DNode *n;
	X3DField *bf;
	u32 nb_nodes, nb_imp;

	nodes = fopen("templates_X3D.txt", "rt");
	if (!nodes) {
		fprintf(stdout, "cannot open \"templates_X3D.txt\" - aborting\n");
		return 0;
	}

	XNodes = NewChain();
	NDTs = NewChain();
	//all nodes are in the same list but we keep version info
	ParseTemplateFile(nodes, XNodes, NDTs);
	fclose(nodes);

	if (argc>1) {
		pf = fopen(argv[1], "rt");
		if (!pf) fprintf(stdout, "Cannot open profile file %s\n", argv[1]);
		else {
			parse_profile(XNodes, pf);
			fclose(pf);
		}
	}
	
	//write the nodes def
	WriteNodesFile(XNodes, NDTs);

	nodes = BeginFile(1);
	
	//write all nodes init stuff
	WriteNodeCode(XNodes, nodes);

	WriteNDT(nodes, XNodes, NDTs);

	EndFile(nodes, 1);

	//free NDTs
	while (ChainGetCount(NDTs)) {
		char *tmp = ChainGetEntry(NDTs, 0);
		free(tmp);
		ChainDeleteEntry(NDTs, 0);
	}
	DeleteChain(NDTs);

	nb_nodes = ChainGetCount(XNodes);
	nb_imp = 0;
	//free nodes
	while (ChainGetCount(XNodes)) {
		n = ChainGetEntry(XNodes, 0);
		if (!n->skip_impl) nb_imp++;
		ChainDeleteEntry(XNodes, 0);
		while (ChainGetCount(n->NDT)) {
			char *tmp = ChainGetEntry(n->NDT, 0);
			free(tmp);
			ChainDeleteEntry(n->NDT, 0);
		}
		DeleteChain(n->NDT);
		while (ChainGetCount(n->Fields)) {
			bf = ChainGetEntry(n->Fields, 0);
			free(bf);
			ChainDeleteEntry(n->Fields, 0);
		}
		DeleteChain(n->Fields);
		free(n);
	}
	DeleteChain(XNodes);

	fprintf(stdout, "Generation done: %d nodes implemented (%d nodes total)\n", nb_imp, nb_nodes);
	return 0;
}

