/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Cyril Concolato
 *			Copyright (c) Telecom ParisTech 2004-2012
 *					All rights reserved
 *
 *  This file is part of GPAC / SVG Scene Graph Generator sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "svggen.h"

SVGGenAttribute *NewSVGGenAttribute()
{
	SVGGenAttribute *att;
	GF_SAFEALLOC(att, SVGGenAttribute)
	return att;
}

void deleteSVGGenAttribute(SVGGenAttribute **p)
{
	xmlFree((*p)->svg_name);
	xmlFree((*p)->svg_type);
	gf_free(*p);
	*p = NULL;
}

SVGGenAttrGrp *NewSVGGenAttrGrp()
{
	SVGGenAttrGrp *tmp;
	GF_SAFEALLOC(tmp, SVGGenAttrGrp)
	tmp->attrs = gf_list_new();
	tmp->attrgrps = gf_list_new();
	return tmp;
}

SVGGenElement *NewSVGGenElement()
{
	SVGGenElement *elt;
	GF_SAFEALLOC(elt, SVGGenElement);
	if (elt) {
		elt->attributes = gf_list_new();
		elt->generic_attributes = gf_list_new();
	}
	return elt;
}

void deleteSVGGenElement(SVGGenElement **p)
{
	u32 i;
	xmlFree((*p)->svg_name);
	for (i = 0; i < gf_list_count((*p)->attributes); i++) {
		SVGGenAttribute *a = gf_list_get((*p)->attributes, i);
		deleteSVGGenAttribute(&a);
	}
	gf_list_del((*p)->attributes);
	gf_free(*p);
	*p = NULL;
}

static GF_List *sortElements(GF_List *elements)
{
	u32 i, j;
	GF_List *sorted_elements = gf_list_new();

	for (i = 0; i< gf_list_count(elements); i++) {
		u8 is_added = 0;
		SVGGenElement *elt = gf_list_get(elements, i);
		for (j = 0; j < gf_list_count(sorted_elements); j++) {
			SVGGenElement *selt = gf_list_get(sorted_elements, j);
			if (strcmp(elt->svg_name, selt->svg_name) < 0) {
				gf_list_insert(sorted_elements, elt, j);
				is_added = 1;
				break;
			}
		}
		if (!is_added) gf_list_add(sorted_elements, elt);
	}

	gf_list_del(elements);
	return sorted_elements;
}

static GF_List *sortAttrGrp(GF_List *attgrps)
{
	u32 i, j;
	GF_List *sorted_attgrps = gf_list_new();

	for (i = 0; i< gf_list_count(attgrps); i++) {
		u8 is_added = 0;
		SVGGenAttrGrp *grp = gf_list_get(attgrps, i);
		for (j = 0; j < gf_list_count(sorted_attgrps); j++) {
			SVGGenAttrGrp *sgrp = gf_list_get(sorted_attgrps, j);
			if (strcmp(grp->name, sgrp->name) < 0) {
				gf_list_insert(sorted_attgrps, grp, j);
				is_added = 1;
				break;
			}
		}
		if (!is_added) gf_list_add(sorted_attgrps, grp);
	}

	gf_list_del(attgrps);
	return sorted_attgrps;
}

static GF_List *sortAttr(GF_List *atts)
{
	u32 i, j;
	GF_List *sorted_atts = gf_list_new();

	for (i = 0; i< gf_list_count(atts); i++) {
		u8 is_added = 0;
		SVGGenAttribute *att = gf_list_get(atts, i);
		for (j = 0; j < gf_list_count(sorted_atts); j++) {
			SVGGenAttribute *satt = gf_list_get(sorted_atts, j);
			if (strcmp(att->svg_name, satt->svg_name) < 0) {
				gf_list_insert(sorted_atts, att, j);
				is_added = 1;
				break;
			}
		}
		if (!is_added) gf_list_add(sorted_atts, att);
	}

	gf_list_del(atts);
	return sorted_atts;
}

void svgNameToImplementationName(xmlChar *svg_name, char implementation_name[50]) {
	char *tmp;
	strcpy(implementation_name, svg_name);
	tmp = implementation_name;
	while ( (tmp = strchr(tmp, '.')) ) {
		*tmp='_';
		tmp++;
	}
	tmp = implementation_name;
	while ( (tmp = strchr(tmp, '-')) ) {
		*tmp='_';
		tmp++;
	}
	tmp = implementation_name;
	while ( (tmp = strchr(tmp, ':')) ) {
		*tmp='_';
		tmp++;
	}
}

static Bool isGenericAttributesGroup(char *name)
{
	if (!strcmp(name, "svg.Core.attr") ||
	        !strcmp(name, "svg.CorePreserve.attr") ||
	        !strcmp(name, "svg.External.attr") ||
	        !strcmp(name, "svg.Properties.attr") ||
	        !strcmp(name, "svg.Media.attr") ||
	        !strcmp(name, "svg.MediaClip.attr") ||
	        !strcmp(name, "svg.Opacity.attr") ||
	        !strcmp(name, "svg.FocusHighlight.attr") ||
	        !strcmp(name, "svg.Focus.attr") ||
	        !strcmp(name, "svg.AnimateCommon.attr") ||
	        !strcmp(name, "svg.XLinkEmbed.attr") ||
	        !strcmp(name, "svg.XLinkRequired.attr") ||
	        !strcmp(name, "svg.XLinkReplace.attr") ||
//		!strcmp(name, "svg.ContentType.attr") ||
	        !strcmp(name, "svg.AnimateTiming.attr") ||
	        !strcmp(name, "svg.AnimateTimingNoMinMax.attr") ||
	        !strcmp(name, "svg.AnimateBegin.attr") ||
	        !strcmp(name, "svg.AnimateTimingNoFillNoMinMax.attr") ||
	        !strcmp(name, "svg.AnimateSync.attr") ||
	        !strcmp(name, "svg.AnimateSyncDefault.attr") ||
	        !strcmp(name, "svg.AnimateAttributeCommon.attr") ||
	        !strcmp(name, "svg.AnimateToCommon.attr") ||
	        !strcmp(name, "svg.AnimateValueCommon.attr") ||
	        !strcmp(name, "svg.AnimateAdditionCommon.attr") ||
	        !strcmp(name, "svg.AnimateTypeCommon.attr") ||
	        !strcmp(name, "svg.Conditional.attr") ||
//		!strcmp(name, "svg.XY.attr") ||
	        !strcmp(name, "svg.Transform.attr")) {
		return 1;
	} else {
		return 0;
	}
}

static Bool setGenericAttributesFlags(char *name, SVGGenElement *e)
{
	Bool ret = 1;
	if (!strcmp(name, "svg.Core.attr") ||
	        !strcmp(name, "svg.CorePreserve.attr") ||
	        !strcmp(name, "svg.External.attr")) {
		e->has_svg_generic = 1;
		e->has_xml_generic = 1;
	} else if (!strcmp(name, "svg.Properties.attr")) {
		e->has_properties = 1;
		e->has_media_properties = 1;
	} else if (!strcmp(name, "svg.Media.attr")) {
		e->has_media_properties = 1;
	} else if (!strcmp(name, "svg.Opacity.attr")) {
		e->has_opacity_properties = 1;
	} else if (!strcmp(name, "svg.FocusHighlight.attr") ||
	           !strcmp(name, "svg.Focus.attr")) {
		e->has_focus = 1;
	} else if (!strcmp(name, "svg.AnimateCommon.attr") ||
	           !strcmp(name, "svg.XLinkEmbed.attr") ||
	           !strcmp(name, "svg.XLinkRequired.attr") ||
	           !strcmp(name, "svg.XLinkReplace.attr")) { //||
//			   !strcmp(name, "svg.ContentType.attr")) {
		e->has_xlink = 1;
	} else if (!strcmp(name, "svg.AnimateTiming.attr") ||
	           !strcmp(name, "svg.AnimateTimingNoMinMax.attr") ||
	           !strcmp(name, "svg.AnimateBegin.attr") ||
	           !strcmp(name, "svg.AnimateTimingNoFillNoMinMax.attr")) {
		e->has_timing = 1;
	} else if (!strcmp(name, "svg.AnimateSync.attr") ||
	           !strcmp(name, "svg.AnimateSyncDefault.attr")) {
		e->has_sync= 1;
	} else if (!strcmp(name, "svg.AnimateAttributeCommon.attr") ||
	           !strcmp(name, "svg.AnimateToCommon.attr") ||
	           !strcmp(name, "svg.AnimateValueCommon.attr") ||
	           !strcmp(name, "svg.AnimateAdditionCommon.attr") ||
	           !strcmp(name, "svg.AnimateTypeCommon.attr")) {
		e->has_animation = 1;
	} else if (!strcmp(name, "svg.Conditional.attr")) {
		e->has_conditional = 1;
	} else if (!strcmp(name, "svg.Transform.attr")) {
		e->has_transform = 1;
	} else if (!strcmp(name, "svg.XY.attr")) {
		e->has_xy = 1;
	} else {
		ret = 0;
	}
	return ret;
}

static void flattenAttributeGroup(SVGGenAttrGrp attgrp, SVGGenElement *e, Bool all);

static void flattenAttributeGroups(GF_List *attrgrps, SVGGenElement *e, Bool all)
{
	u32 i;
	for (i = 0; i < gf_list_count(attrgrps); i ++) {
		SVGGenAttrGrp *ag = gf_list_get(attrgrps, i);
		flattenAttributeGroup(*ag, e, all);
	}
}

static void flattenAttributeGroup(SVGGenAttrGrp attgrp, SVGGenElement *e, Bool all)
{
	u32 i;

	if (isGenericAttributesGroup(attgrp.name) && !all) {
		setGenericAttributesFlags(attgrp.name, e);
		flattenAttributeGroups(attgrp.attrgrps, e, 1);
		for (i = 0; i < gf_list_count(attgrp.attrs); i++) {
			gf_list_add(e->generic_attributes, gf_list_get(attgrp.attrs, i));
		}
	} else {
		flattenAttributeGroups(attgrp.attrgrps, e, all);
		for (i = 0; i < gf_list_count(attgrp.attrs); i++) {
			if (all)
				gf_list_add(e->generic_attributes, gf_list_get(attgrp.attrs, i));
			else
				gf_list_add(e->attributes, gf_list_get(attgrp.attrs, i));
		}
	}
}

SVGGenAttribute *findAttribute(SVGGenElement *e, char *name)
{
	u32 i;
	for (i = 0; i < gf_list_count(e->attributes); i++) {
		SVGGenAttribute *a = gf_list_get(e->attributes, i);
		if (!strcmp(a->svg_name, name)) return a;
	}
	for (i = 0; i < gf_list_count(e->generic_attributes); i++) {
		SVGGenAttribute *a = gf_list_get(e->generic_attributes, i);
		if (!strcmp(a->svg_name, name)) return a;
	}
	return NULL;
}

static u32 countAttributesAllInGroup(SVGGenAttrGrp *ag)
{
	u32 i, ret = 0;
	for (i = 0; i < gf_list_count(ag->attrgrps); i ++) {
		SVGGenAttrGrp *agtmp = gf_list_get(ag->attrgrps, i);
		ret += countAttributesAllInGroup(agtmp);
	}
	ret += gf_list_count(ag->attrs);
	return ret;
}

/* XML related functions */
xmlNodeSetPtr findNodes( xmlXPathContextPtr ctxt, xmlChar * path )
{
	xmlXPathObjectPtr res = NULL;

	if ( ctxt->node != NULL && path != NULL ) {
		xmlXPathCompExprPtr comp;

		xmlDocPtr tdoc = NULL;
		xmlNodePtr froot = ctxt->node;

		comp = xmlXPathCompile( path );
		if ( comp == NULL ) {
			return NULL;
		}

		if ( ctxt->node->doc == NULL ) {
			/* if one XPaths a node from a fragment, libxml2 will
			   refuse the lookup. this is not very usefull for XML
			   scripters. thus we need to create a temporary document
			   to make libxml2 do it's job correctly.
			 */
			tdoc = xmlNewDoc( NULL );

			/* find refnode's root node */
			while ( froot != NULL ) {
				if ( froot->parent == NULL ) {
					break;
				}
				froot = froot->parent;
			}
			xmlAddChild((xmlNodePtr)tdoc, froot);

			ctxt->node->doc = tdoc;
		}

		res = xmlXPathCompiledEval(comp, ctxt);

		xmlXPathFreeCompExpr(comp);

		if ( tdoc != NULL ) {
			/* after looking through a fragment, we need to drop the
			   fake document again */
			xmlSetTreeDoc(froot,NULL);
			froot->doc = NULL;
			tdoc->children = NULL;
			tdoc->last     = NULL;
			froot->parent  = NULL;
			ctxt->node->doc = NULL;

			xmlFreeDoc( tdoc );
		}
	}
	if (res && res->type == XPATH_NODESET)
		return res->nodesetval;
	else
		return NULL;
}


/* definition of GPAC groups of SVG attributes */

void setAttributeType(SVGGenAttribute *att)
{
	if (!att->svg_type) { /* if the type is not given in the RNG, we explicitely set it */
		if (!strcmp(att->svg_name, "textContent")) {
			strcpy(att->impl_type, "SVG_TextContent");
		} else if (!strcmp(att->svg_name, "class")) {
			strcpy(att->implementation_name, "_class");
			strcpy(att->impl_type, "SVG_String");
		} else if (!strcmp(att->svg_name, "visibility")) {
			strcpy(att->impl_type, "SVG_Visibility");
		} else if (!strcmp(att->svg_name, "display")) {
			strcpy(att->impl_type, "SVG_Display");
		} else if (!strcmp(att->svg_name, "stroke-linecap")) {
			strcpy(att->impl_type, "SVG_StrokeLineCap");
		} else if (!strcmp(att->svg_name, "stroke-dasharray")) {
			strcpy(att->impl_type, "SVG_StrokeDashArray");
		} else if (!strcmp(att->svg_name, "stroke-linejoin")) {
			strcpy(att->impl_type, "SVG_StrokeLineJoin");
		} else if (!strcmp(att->svg_name, "font-style")) {
			strcpy(att->impl_type, "SVG_FontStyle");
		} else if (!strcmp(att->svg_name, "font-weight")) {
			strcpy(att->impl_type, "SVG_FontWeight");
		} else if (!strcmp(att->svg_name, "text-anchor")) {
			strcpy(att->impl_type, "SVG_TextAnchor");
		} else if (!strcmp(att->svg_name, "fill")) {
			strcpy(att->impl_type, "SMIL_Fill");
		} else if (!strcmp(att->svg_name, "fill-rule")) {
			strcpy(att->impl_type, "SVG_FillRule");
		} else if (!strcmp(att->svg_name, "font-family")) {
			strcpy(att->impl_type, "SVG_FontFamily");
		} else if (!strcmp(att->svg_name, "calcMode")) {
			strcpy(att->impl_type, "SMIL_CalcMode");
		} else if (!strcmp(att->svg_name, "values")) {
			strcpy(att->impl_type, "SMIL_AnimateValues");
		} else if (!strcmp(att->svg_name, "keyTimes")) {
			strcpy(att->impl_type, "SMIL_KeyTimes");
		} else if (!strcmp(att->svg_name, "keySplines")) {
			strcpy(att->impl_type, "SMIL_KeySplines");
		} else if (!strcmp(att->svg_name, "keyPoints")) {
			strcpy(att->impl_type, "SMIL_KeyPoints");
		} else if (!strcmp(att->svg_name, "from") ||
		           !strcmp(att->svg_name, "to") ||
		           !strcmp(att->svg_name, "by")) {
			strcpy(att->impl_type, "SMIL_AnimateValue");
		} else if (!strcmp(att->svg_name, "additive")) {
			strcpy(att->impl_type, "SMIL_Additive");
		} else if (!strcmp(att->svg_name, "accumulate")) {
			strcpy(att->impl_type, "SMIL_Accumulate");
		} else if (!strcmp(att->svg_name, "begin") ||
		           !strcmp(att->svg_name, "end")
		          ) {
			strcpy(att->impl_type, "SMIL_Times");
		} else if (!strcmp(att->svg_name, "clipBegin") ||
		           !strcmp(att->svg_name, "clipEnd")
		          ) {
			strcpy(att->impl_type, "SVG_Clock");
		} else if (!strcmp(att->svg_name, "min") ||
		           !strcmp(att->svg_name, "max") ||
		           !strcmp(att->svg_name, "dur") ||
		           !strcmp(att->svg_name, "repeatDur")
		          ) {
			strcpy(att->impl_type, "SMIL_Duration");
		} else if (!strcmp(att->svg_name, "repeat")) {
			strcpy(att->impl_type, "SMIL_Repeat");
		} else if (!strcmp(att->svg_name, "restart")) {
			strcpy(att->impl_type, "SMIL_Restart");
		} else if (!strcmp(att->svg_name, "repeatCount")) {
			strcpy(att->impl_type, "SMIL_RepeatCount");
		} else if (!strcmp(att->svg_name, "attributeName")) {
			strcpy(att->impl_type, "SMIL_AttributeName");
		} else if (!strcmp(att->svg_name, "type")) {
			strcpy(att->impl_type, "SVG_TransformType");
		} else if (!strcmp(att->svg_name, "font-size")) {
			strcpy(att->impl_type, "SVG_FontSize");
		} else if (!strcmp(att->svg_name, "viewBox")) {
			strcpy(att->impl_type, "SVG_ViewBox");
		} else if (!strcmp(att->svg_name, "preserveAspectRatio")) {
			strcpy(att->impl_type, "SVG_PreserveAspectRatio");
		} else if (!strcmp(att->svg_name, "zoomAndPan")) {
			strcpy(att->impl_type, "SVG_ZoomAndPan");
		} else if (!strcmp(att->svg_name, "path")) {
			strcpy(att->impl_type, "SVG_PathData");
		} else if (!strcmp(att->svg_name, "image-rendering")) {
			strcpy(att->impl_type, "SVG_RenderingHint");
		} else if (!strcmp(att->svg_name, "color-rendering")) {
			strcpy(att->impl_type, "SVG_RenderingHint");
		} else if (!strcmp(att->svg_name, "text-rendering")) {
			strcpy(att->impl_type, "SVG_RenderingHint");
		} else if (!strcmp(att->svg_name, "shape-rendering")) {
			strcpy(att->impl_type, "SVG_RenderingHint");
		} else if (!strcmp(att->svg_name, "pointer-events")) {
			strcpy(att->impl_type, "SVG_PointerEvents");
		} else if (!strcmp(att->svg_name, "vector-effect")) {
			strcpy(att->impl_type, "SVG_VectorEffect");
		} else if (!strcmp(att->svg_name, "vector-effect")) {
			strcpy(att->impl_type, "SVG_VectorEffect");
		} else if (!strcmp(att->svg_name, "display-align")) {
			strcpy(att->impl_type, "SVG_DisplayAlign");
		} else if (!strcmp(att->svg_name, "text-align")) {
			strcpy(att->impl_type, "SVG_TextAlign");
		} else if (!strcmp(att->svg_name, "propagate")) {
			strcpy(att->impl_type, "XMLEV_Propagate");
		} else if (!strcmp(att->svg_name, "defaultAction")) {
			strcpy(att->impl_type, "XMLEV_DefaultAction");
		} else if (!strcmp(att->svg_name, "phase")) {
			strcpy(att->impl_type, "XMLEV_Phase");
		} else if (!strcmp(att->svg_name, "syncBehavior")) {
			strcpy(att->impl_type, "SMIL_SyncBehavior");
		} else if (!strcmp(att->svg_name, "syncBehaviorDefault")) {
			strcpy(att->impl_type, "SMIL_SyncBehavior");
		} else if (!strcmp(att->svg_name, "attributeType")) {
			strcpy(att->impl_type, "SMIL_AttributeType");
		} else if (!strcmp(att->svg_name, "playbackOrder")) {
			strcpy(att->impl_type, "SVG_PlaybackOrder");
		} else if (!strcmp(att->svg_name, "timelineBegin")) {
			strcpy(att->impl_type, "SVG_TimelineBegin");
		} else if (!strcmp(att->svg_name, "xml:space")) {
			strcpy(att->impl_type, "XML_Space");
		} else if (!strcmp(att->svg_name, "snapshotTime")) {
			strcpy(att->impl_type, "SVG_Clock");
		} else if (!strcmp(att->svg_name, "version")) {
			strcpy(att->impl_type, "SVG_String");
		} else if (!strcmp(att->svg_name, "gradientUnits")) {
			strcpy(att->impl_type, "SVG_GradientUnit");
		} else if (!strcmp(att->svg_name, "baseProfile")) {
			strcpy(att->impl_type, "SVG_String");
		} else if (!strcmp(att->svg_name, "focusHighlight")) {
			strcpy(att->impl_type, "SVG_FocusHighlight");
		} else if (!strcmp(att->svg_name, "initialVisibility")) {
			strcpy(att->impl_type, "SVG_InitialVisibility");
		} else if (!strcmp(att->svg_name, "overlay")) {
			strcpy(att->impl_type, "SVG_Overlay");
		} else if (!strcmp(att->svg_name, "transformBehavior")) {
			strcpy(att->impl_type, "SVG_TransformBehavior");
		} else if (!strcmp(att->svg_name, "rotate")) {
			strcpy(att->impl_type, "SVG_Rotate");
		} else if (!strcmp(att->svg_name, "font-variant")) {
			strcpy(att->impl_type, "SVG_FontVariant");
		} else if (!strcmp(att->svg_name, "lsr:enabled")) {
			strcpy(att->impl_type, "SVG_Boolean");
		} else if (!strcmp(att->svg_name, "spreadMethod")) {
			strcpy(att->impl_type, "SVG_SpreadMethod");
		} else if (!strcmp(att->svg_name, "gradientTransform")) {
			strcpy(att->impl_type, "SVG_Transform_Full");
		} else if (!strcmp(att->svg_name, "editable")) {
			strcpy(att->impl_type, "SVG_Boolean");
		} else if (!strcmp(att->svg_name, "choice")) {
			strcpy(att->impl_type, "LASeR_Choice");
		} else if (!strcmp(att->svg_name, "size") ||
		           !strcmp(att->svg_name, "delta")) {
			strcpy(att->impl_type, "LASeR_Size");
		} else if (!strcmp(att->svg_name, "syncReference")) {
			strcpy(att->impl_type, "XMLRI");
		} else {
			/* For all other attributes, we use String as default type */
			strcpy(att->impl_type, "SVG_String");
			fprintf(stdout, "Warning: using type SVG_String for attribute %s.\n", att->svg_name);
		}
	} else { /* for some attributes, the type given in the RNG needs to be overriden */
		if (!strcmp(att->svg_name, "color")) {
			strcpy(att->impl_type, "SVG_Paint");
		} else if (!strcmp(att->svg_name, "viewport-fill")) {
			strcpy(att->impl_type, "SVG_Paint");
		} else if (!strcmp(att->svg_name, "syncTolerance")) {
			strcpy(att->impl_type, "SMIL_SyncTolerance");
		} else if (!strcmp(att->svg_name, "syncToleranceDefault")) {
			strcpy(att->impl_type, "SMIL_SyncTolerance");
		} else if (!strcmp(att->svg_name, "transform")) {
			strcpy(att->impl_type, "SVG_Transform");
		} else if (!strcmp(att->svg_name, "gradientTransform")) {
			strcpy(att->impl_type, "SVG_Transform");
		} else if (!strcmp(att->svg_name, "focusable")) {
			strcpy(att->impl_type, "SVG_Focusable");
		} else if (!strcmp(att->svg_name, "event") || !strcmp(att->svg_name, "ev:event")) {
			strcpy(att->impl_type, "XMLEV_Event");
		} else if (!strcmp(att->svg_type, "IRI.datatype")) {
			strcpy(att->impl_type, "XMLRI");
		} else if (!strcmp(att->svg_type, "IDREF.datatype")) {
			strcpy(att->impl_type, "XML_IDREF");
		} else if (strstr(att->svg_type, "datatype")) {
			char *tmp;
			sprintf(att->impl_type, "SVG_%s", att->svg_type);
			tmp = att->impl_type;
			while ( (tmp = strstr(tmp, "-")) ) {
				*tmp='_';
				tmp++;
			}
			tmp = att->impl_type;
			while ( (tmp = strstr(tmp, ".")) ) {
				*tmp='_';
				tmp++;
			}
			tmp = att->impl_type;;
			if ( (tmp = strstr(tmp, "datatype")) ) {
				tmp--;
				*tmp = 0;
			}
		}
	}
}

void getAttributeType(xmlDocPtr doc, xmlXPathContextPtr xpathCtx,
                      xmlNodePtr attributeNode, SVGGenAttribute *a)
{

	xmlNodeSetPtr refNodes;
	xpathCtx->node = attributeNode;
	refNodes = findNodes(xpathCtx, ".//rng:ref");
	if (refNodes->nodeNr == 0) {
		//a->svg_type = xmlStrdup("0_ref_type");
	} else if (refNodes->nodeNr == 1) {
		xmlNodePtr ref = refNodes->nodeTab[0];
		a->svg_type = xmlStrdup(xmlGetProp(ref, "name"));
	} else {
		//a->svg_type = xmlStrdup("N_ref_type");
	}
}

void getRealAttributes(xmlDocPtr doc, xmlXPathContextPtr xpathCtx, xmlNodePtr newCtxNode,
                       GF_List *attributes)
{
	xmlNodeSetPtr attributeNodes;
	int k;
	u32 j;

	xpathCtx->node = newCtxNode;
	attributeNodes = findNodes(xpathCtx, ".//rng:attribute");
	for (k = 0; k < attributeNodes->nodeNr; k++)	{
		Bool already_exists = 0;
		xmlNodePtr attributeNode = attributeNodes->nodeTab[k];
		if (attributeNode->type == XML_ELEMENT_NODE) {
			SVGGenAttribute *a = NewSVGGenAttribute();
			a->svg_name = xmlGetProp(attributeNode, "name");
			a->optional = xmlStrEqual(attributeNode->parent->name, "optional");
			svgNameToImplementationName(a->svg_name, a->implementation_name);
			getAttributeType(doc, xpathCtx, attributeNode, a);
			setAttributeType(a);
			for (j=0; j<gf_list_count(attributes); j++) {
				SVGGenAttribute *ta = gf_list_get(attributes, j);
				if (xmlStrEqual(ta->svg_name, a->svg_name)) {
					already_exists = 1;
					break;
				}
			}
			if (already_exists) {
				deleteSVGGenAttribute(&a);
			} else {
				//fprintf(stdout, "Adding attribute %s to element %s\n",a->svg_name, e->svg_name);
				gf_list_add(attributes, a);
			}
		}
	}
}

SVGGenAttrGrp *getOneGlobalAttrGrp(xmlDocPtr doc, xmlXPathContextPtr xpathCtx, xmlChar *name)
{
	SVGGenAttrGrp *attgrp = NULL;
	xmlNodeSetPtr attrGrpDefNodes;
	xmlChar *expr;
	u32 j;
	int i, l;

	/* attributes group already resolved */
	for (j = 0; j < gf_list_count(globalAttrGrp); j++) {
		SVGGenAttrGrp *attgrp = gf_list_get(globalAttrGrp, j);
		if (!strcmp(attgrp->name, name)) {
			return attgrp;
		}
	}

	/* new attributes group */
	expr = xmlStrdup("//rng:define[@name=\"");
	expr = xmlStrcat(expr, name);
	expr = xmlStrcat(expr, "\" and not(rng:empty) and not(rng:notAllowed)]");
	attrGrpDefNodes = findNodes(xpathCtx, expr);
	if (!attrGrpDefNodes->nodeNr) {
		fprintf(stdout, "Warning: found 0 non-empty or allowed definition for the Group of Attributes: %s\n", name);
		return NULL;
	}
	attgrp = NewSVGGenAttrGrp();
	attgrp->name = gf_strdup(name);
	svgNameToImplementationName(attgrp->name, attgrp->imp_name);
	gf_list_add(globalAttrGrp, attgrp);

	for (i = 0; i < attrGrpDefNodes->nodeNr; i++) {
		xmlNodePtr attrGrp = attrGrpDefNodes->nodeTab[i];
		getRealAttributes(doc, xpathCtx, attrGrp, attgrp->attrs);

		{
			xmlNodeSetPtr refNodes;
			xpathCtx->node = attrGrp;
			refNodes = findNodes(xpathCtx, ".//rng:ref");
			for (l = 0; l < refNodes->nodeNr; l++) {
				xmlNodePtr ref = refNodes->nodeTab[l];
				xmlChar *rname = xmlGetProp(ref, "name");
				if (xmlStrstr(rname, ".attr")) {
					SVGGenAttrGrp *g2 = getOneGlobalAttrGrp(doc, xpathCtx, rname);
					if (g2) {
						gf_list_add(attgrp->attrgrps, g2);
					}
				}
			}
		}
	}
	return attgrp;
}

void getAllGlobalAttrGrp(xmlDocPtr doc, xmlXPathContextPtr xpathCtx)
{
	xmlNodeSetPtr elementNodes = findNodes(xpathCtx, "//rng:define");
	int k;
	for (k = 0; k < elementNodes->nodeNr; k++)	{
		xmlNodePtr elementNode = elementNodes->nodeTab[k];
		if (elementNode->type == XML_ELEMENT_NODE) {
			xmlChar *name = NULL;
			name = xmlGetProp(elementNode, "name");
			if (xmlStrstr(name, ".attr")) {
				getOneGlobalAttrGrp(doc, xpathCtx, name);
			}
		}
	}
}

GF_List *getElements(xmlDocPtr doc, xmlXPathContextPtr xpathCtx)
{
	xmlChar *expr;
	GF_List *elements = gf_list_new();
	xmlNodeSetPtr ATNodes;
	xmlNodeSetPtr refNodes, elementNodes = findNodes(xpathCtx, "//rng:element");
	int k, j;
	u32 i;

	for (k = 0; k < elementNodes->nodeNr; k++)	{
		xmlNodePtr elementNode = elementNodes->nodeTab[k];
		if (elementNode->type == XML_ELEMENT_NODE) {
			SVGGenElement *e = NewSVGGenElement();
			e->svg_name = xmlStrdup(xmlGetProp(elementNode, "name"));
			//fprintf(stdout, "\n\tElement %s\n", e->svg_name);

			svgNameToImplementationName(e->svg_name, e->implementation_name);

			/* getting the <define name="element.AT"/> */
			expr = xmlStrdup("//rng:define[@name=\"");
			if (!xmlStrcmp(e->svg_name, "polygon") || !xmlStrcmp(e->svg_name, "polyline")) {
				expr = xmlStrcat(expr, "polyCommon");
			} else {
				expr = xmlStrcat(expr, e->svg_name);
			}
			expr = xmlStrcat(expr, ".AT\"]");
			ATNodes = findNodes(xpathCtx, expr);
			if (ATNodes->nodeNr) {

				/* dealing with attributes defined in groups of attributes */
				xpathCtx->node = ATNodes->nodeTab[0];
				refNodes = findNodes(xpathCtx, ".//rng:ref");
				for (j = 0; j <refNodes->nodeNr; j++) {
					xmlNodePtr refNode = refNodes->nodeTab[j];
					char *name = xmlGetProp(refNode, "name");
					for (i = 0; i < gf_list_count(globalAttrGrp); i++) {
						SVGGenAttrGrp *a = gf_list_get(globalAttrGrp, i);
						if (!strcmp(a->name, name)) {
							if (isGenericAttributesGroup(a->name)) {
								setGenericAttributesFlags(a->name, e);
								flattenAttributeGroup(*a, e, 1);
							} else {
								flattenAttributeGroup(*a, e, 0);
							}
							break;
						}
					}
				}

				/* dealing with attributes defined directly here */
				getRealAttributes(doc, xpathCtx, ATNodes->nodeTab[0], e->attributes);
			}

			/* checking if this element is already present in the list of possible elements
			   and if not, adding it */
			{
				Bool found = 0;
				for (i=0; i<gf_list_count(elements); i++) {
					SVGGenElement *etmp = gf_list_get(elements, i);
					if (!strcmp(etmp->svg_name, e->svg_name)) {
						found = 1;
						break;
					}
				}
				if (!found) gf_list_add(elements, e);
			}
		}
	}
	return elements;
}

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

	char sPath[GF_MAX_PATH];

	if (!type) {
#ifdef LOCAL_SVG_NODES
		sprintf(sPath, "nodes_svg.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
#else
		if (generation_mode == 1)
			sprintf(sPath, "..%c..%c..%c..%cinclude%cgpac%cnodes_svg_sa.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
		else if (generation_mode == 2)
			sprintf(sPath, "..%c..%c..%c..%cinclude%cgpac%cnodes_svg_sani.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
		else if (generation_mode == 3)
			sprintf(sPath, "..%c..%c..%c..%cinclude%cgpac%cnodes_svg_da.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);

#endif
	} else if (type==1) {
#ifdef LOCAL_SVG_NODES
		sprintf(sPath, "svg_nodes.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
#else
		if (generation_mode == 1)
			sprintf(sPath, "..%c..%c..%c..%csrc%cscenegraph%csvg_nodes_sa.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
		else if (generation_mode == 2)
			sprintf(sPath, "..%c..%c..%c..%csrc%cscenegraph%csvg_nodes_sani.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
		else if (generation_mode == 3)
			sprintf(sPath, "..%c..%c..%c..%csrc%cscenegraph%csvg_nodes_da.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
#endif
	} else {
#ifdef LOCAL_SVG_NODES
		sprintf(sPath, "lsr_tables.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
#else
		if (generation_mode == 1)
			sprintf(sPath, "..%c..%c..%c..%csrc%claser%clsr_tables_sa.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
		else if (generation_mode == 2)
			sprintf(sPath, "..%c..%c..%c..%csrc%claser%clsr_tables_sani.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
		else if (generation_mode == 3)
			sprintf(sPath, "..%c..%c..%c..%csrc%claser%clsr_tables.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);

#endif
	}

	f = gf_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 SVGGen for GPAC Version %s\n*/\n\n", asctime(gmtime(&rawtime)), GPAC_VERSION);
	}

	if (!type) {
		if (generation_mode == 1) {
			fprintf(f, "#ifndef _GF_SVG_SA_NODES_H\n");
			fprintf(f, "#define _GF_SVG_SA_NODES_H\n\n");
		} else if (generation_mode == 2) {
			fprintf(f, "#ifndef _GF_SVG_SANI_NODES_H\n");
			fprintf(f, "#define _GF_SVG_SANI_NODES_H\n\n");
		} else if (generation_mode == 3) {
			fprintf(f, "#ifndef _GF_SVG_NODES_H\n");
			fprintf(f, "#define _GF_SVG_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");
		if (generation_mode == 1) fprintf(f, "\n\n#endif\t\t/*_GF_SVG_SA_NODES_H*/\n\n");
		if (generation_mode == 2) fprintf(f, "\n\n#endif\t\t/*_GF_SVG_SANI_NODES_H*/\n\n");
		if (generation_mode == 3) fprintf(f, "\n\n#endif\t\t/*_GF_SVG_NODES_H*/\n\n");
	} else {
		fprintf(f, "\n");
	}
	gf_fclose(f);
}

void generateAttributes(FILE *output, GF_List *attributes, Bool inDefine)
{
	u32 i;
	for (i = 0; i<gf_list_count(attributes); i++) {
		SVGGenAttribute *att = (SVGGenAttribute *)gf_list_get(attributes, i);
		if (inDefine)
			if (i == gf_list_count(attributes) -1)
				fprintf(output, "\t%s %s;\n", att->impl_type, att->implementation_name);
			else
				fprintf(output, "\t%s %s; \\\n", att->impl_type, att->implementation_name);
		else
			fprintf(output, "\t%s %s;\n", att->impl_type, att->implementation_name);
	}
}

/*
u32 generateAttributesGroupInfo(FILE *output, char * elt_imp_name, SVGGenAttrGrp *attgrp, u32 i)
{
	u32 att_index = i;
	u32 k;
	for (k=0; k<gf_list_count(attgrp->attrgrps); k++) {
		SVGGenAttrGrp *ag = gf_list_get(attgrp->attrgrps, k);
		att_index = generateAttributesGroupInfo(output, elt_imp_name, ag, att_index);
	}
	for (k=0; k<gf_list_count(attgrp->attrs); k++) {
		SVGGenAttribute *at = gf_list_get(attgrp->attrs, k);
		generateAttributeInfo(output, elt_imp_name, at, att_index++);
	}
	return att_index;
}
*/

void replaceIncludes(xmlDocPtr doc, xmlXPathContextPtr xpathCtx)
{
	int k;
	xmlNodeSetPtr nodes;
	xmlXPathObjectPtr xpathObj;

	/* Get all the RNG elements */
	xpathObj = xmlXPathEvalExpression("//rng:include", xpathCtx);
	if(xpathObj == NULL || xpathObj->type != XPATH_NODESET) return;

	nodes = xpathObj->nodesetval;

	for (k = 0; k < nodes->nodeNr; k++)	{
		xmlNodePtr node = nodes->nodeTab[k];
		if (node->type == XML_ELEMENT_NODE) {
			xmlChar *href;
			xmlDocPtr sub_doc;

			href = xmlGetNoNsProp(node, "href");
			sub_doc = xmlParseFile(href);
			xmlReplaceNode(nodes->nodeTab[k], xmlDocGetRootElement(sub_doc));
		}
	}
	xmlXPathFreeObject(xpathObj);
}

int main(int argc, char **argv)
{
	xmlDocPtr doc = NULL;
	xmlXPathContextPtr xpathCtx = NULL;
	GF_List *svg_elements = NULL;

	xmlInitParser();
	LIBXML_TEST_VERSION

	doc = xmlParseFile(argv[1]);
	if (!doc) {
		printf("error: could not parse file %s\n", argv[1]);
		return -1;
	}

	xpathCtx = xmlXPathNewContext(doc);
	if(xpathCtx == NULL) {
		fprintf(stderr,"Error: unable to create new XPath context\n");
		xmlFreeDoc(doc);
		return(-1);
	}
	xmlXPathRegisterNs(xpathCtx, RNG_PREFIX, RNG_NS);
	xmlXPathRegisterNs(xpathCtx, RNGA_PREFIX, RNGA_NS);
	xmlXPathRegisterNs(xpathCtx, SVGA_PREFIX, SVGA_NS);

	replaceIncludes(doc, xpathCtx);
	xmlSaveFile("completerng_props.xml", doc);

	globalAttrGrp = gf_list_new();
	getAllGlobalAttrGrp(doc, xpathCtx);

	svg_elements = getElements(doc, xpathCtx);
	svg_elements = sortElements(svg_elements);

	if (argv[2] && !strcmp(argv[2], "-html")) {
		generate_table(svg_elements);
	} else {
		if (generation_mode == 1) generateSVGCode_V1(svg_elements);
		if (generation_mode == 2) generateSVGCode_V2(svg_elements);
		if (generation_mode == 3) generateSVGCode_V3(svg_elements);
	}

	xmlXPathFreeContext(xpathCtx);
	//xmlFreeDoc(doc);

	xmlCleanupParser();
	return 0;
}


