#include <xml/copynode.h>
#include <xml/attrnode.h>
#include <stdarg.h>

#define ___ {
#define ____ }

static void update_to_new_end (xml_GNode* node)
{
    g_return_if_fail (node);
    g_return_if_fail (node->text);
    
    while (node)
    
{
	if (0) g_printerr ("<updated %s old.end=%i <> .new.end=%i>\n", 
			   node->name, node->end, node->text->len);
	node->end = node->text->len;
	if (! node->parent) break;
	if (node->parent->text != node->text) break;
	node = node->parent;
    }
}
xml_GNode*
xml_node_rename (xml_GNode* node, const char* name)
{
    return xml_node_rename_to (node, g_strdup (name));
}
xml_GNode*
xml_node_rename_to (xml_GNode* node, char* name)
{
    if (! node) return node;
    g_free (node->name); node->name = name;
    return node;
}
xml_GNode* /* restrict */
xml_node_append_node_new (xml_GNode* node, char* name)
{
    xml_GNode* new1 = xml_g_node_new (name);
    if (node)
    
{
	new1->text = node->text;
	new1->off = new1->end = node->end;
	xml_g_node_append (node, new1);
    }
return new1; }
xml_GNode* /* restrict */
xml_node_append_node_new_data (xml_GNode* node, const char* name)
{
    return xml_node_append_node_new (node, g_strdup (name));
}
xml_GNode* /* self */
xml_node_append_string (xml_GNode* node, const char* string)
{
    if (!string || !node || !node->text) return node;
    if (node->end != node->text->len)
	g_printerr ("<%s .end=%i <> .txt.len=%i>\n", node->name,
		    node->end, node->text->len);
    g_return_val_if_fail (node->end == node->text->len, node);

    g_string_append (node->text, string);
    update_to_new_end (node);
    return node;
}
/*
   the format string may contain sgml-compressed node-declarations
   like <p/hello> is the same as <p>hello</p> and creates a <p>-node
   with a text snippet of "hello". As an extension, the nodename
   may be indirected itself with using <%s/hello>. To append a new
   node with some new text you can use <%s/%s> of course.
  
   formatting recognized:
   <%s/ .... xml_node_append_new
   <%#s/ ... xml_node_append_new_data
   <name/ .. xml_node_append_new_data_len
   %s        xml_node_append_string
   <%s%p/ .. additionally, copy the attributes list into the new node
   <%#s%p/ .. or here or even with <name%p/ (perhaps read: "properties")
  
   Calling xml_node_append is generally slightly slower than using
   a direct call since varargs are used and the format string gets
   interpreted char-by-char. It gets faster whenever two append
   arguments are passed since the update_to_new_end is called only
   once which walks the tree up and updates node->len for each. 
   That part is more time-consuming than varargs-building requires.
 */    
xml_GNode*
xml_node_append (xml_GNode* node, const char* format, ...)
{
    if (! format) return node;
    ___ va_list args;
    va_start (args, format);

    for ( ; *format ; format++)
    
{
	if (*format == '%')
	
{
	    if (*++format == 's')
	    
{
		const gchar* str = va_arg (args, const gchar*);
		g_return_val_if_fail (str, 0);
		g_string_append (node->text, str);
	    }
}
else if (*format == '<')
{
	    node->end = node->text->len;
	    if (! memcmp (format, "<%s/", 4) || 
		! memcmp (format, "<%s%p/", 7))
	    
{
		gchar* nodename = va_arg (args, gchar*);
		if (! nodename) nodename = strdup (":");
		node = xml_node_append_node_new (node, nodename); 
		if (format[3] != '%')
		    format += 3;
		else
		
{
		    xml_AttrList* list = va_arg (args, xml_AttrList*);
		    if (list) node->attributes = xml_attr_list_copy (list); 
		    format += 5;
		}
}
else if (! memcmp (format, "<%#s/", 5) || ! memcmp (format, "<%#s%p/", 7))
{
		const gchar* nodename = va_arg (args, const gchar*);
		if (! nodename) nodename = ":";
		node = xml_node_append_node_new_data (node, nodename); 
		if (format[4] != '%')
		    format += 4;
		else
		
{
		    xml_AttrList* list = va_arg (args, xml_AttrList*);
		    if (list) node->attributes = xml_attr_list_copy (list); 
		    format += 6;
		}
}
else
{
		const gchar* nodename = ++format;
		while (*format && *format != '/') format++;
		if (format-nodename < 3 ||
		    format[-2] != '%' || format[-1] != 'p')
		
{
		    node = xml_node_append_node_new (
			node, g_strndup (nodename, format-nodename)); 
		}
else
{
		    node = xml_node_append_node_new (
			node, g_strndup (nodename, format-2-nodename)); 
		    ___ xml_AttrList* list = va_arg (args, xml_AttrList*);
		    node->attributes = xml_attr_list_copy (list); ____;
		}
if (!*format) break; }
}
else if (*format == '>')
{
	    node->end = node->text->len;
	    node = node->parent; 
	}
else
{
	    g_string_append_c (node->text, *format);
	}
}
va_end (args); ____; update_to_new_end (node); return node; }
/* ______________________________________________________________________ */
static xml_GNode* node_copy (xml_GNode* node, GString* text, gssize diff)
{
    if (! node || ! text) return 0;
    ___ xml_GNode* new1 = xml_g_node_new_data (node->name);
    new1->off = node->off + diff;
    new1->end = node->end + diff;
    new1->text = text ? text : node->text;
    new1->attributes = xml_attr_list_copy (node->attributes);
    return new1;
    ____;
}
static void append_children (xml_GNode* parent, xml_GNode* node, gssize diff)
{
    if (! parent || ! node) return;
    
    ___ xml_GNode* last = parent->children;
    if (last) 
{ while (last->next) last = last->next; }
/* node* is in fact a node-list */ for (; node ; node = node->next)
{
	xml_GNode* new1 = node_copy (node, parent->text, diff);
	xml_g_node_insert_after (parent, last, new1);
	append_children (new1, node->children, diff);
	last = new1;
    }
____; }
xml_GNode*
xml_node_append_copy (xml_GNode* tree, xml_GNode* node, const gchar* newline)
{
    if (! tree || ! node || !node->text) return 0;
    g_return_val_if_fail (tree->text, 0);
    ___ gssize diff = tree->text->len - node->off;

    g_string_append_len (tree->text, node->text->str + node->off, 
			 node->end - node->off);

    ___ xml_GNode* new1 = node_copy (node, tree->text, diff);
    xml_g_node_append (tree, new1);
    append_children (new1, node->children, diff);
    new1->end = new1->text->len;
    if (newline && tree->text) g_string_append (tree->text, newline);
    update_to_new_end (tree);
    return new1;
    ____;____;
}
xml_GNode*
xml_node_shadow_copy (xml_GNode* tree, xml_GNode* node)
{
    if (! tree || ! node) return 0;
    ___ xml_GNode* new1 = node_copy (node, node->text, 0);
    xml_g_node_append (tree, new1);
    append_children (new1, node->children, 0);
    return new1;
    ____;
}
xml_GNode*
xml_node_shadow_list (xml_GNode* tree, xml_GList* list, gboolean free_1)
{
    if (! tree || ! list) return tree;

    ___ xml_GList* next;
    for (; list ; list = next)
    
{
	next = list->next;
	xml_node_shadow_copy (tree, list->data.node);
	if (free_1) xml_g_list_free_1 (list);
    }
; ____; return tree; }
xml_GNode*
xml_node_append_list (xml_GNode* tree, xml_GList* list, gboolean free_1)
{
    if (! tree || ! list) return tree;

    for (; list ; list = xml_g_list_free_head(list))
	xml_node_append_copy (tree, list->data.node, 0);

    return tree;
}
xml_GNode*
xml_tree_from_list (GString* text, xml_GList* list, gboolean free_1)
{
    xml_GNode* tree = xml_g_node_new (0);
    if (! text) text = g_string_new (0);
    tree->text = text; tree->off = tree->end = text->len;
    return xml_node_append_list (tree, list, free_1);
}
/* 
   Local variables:
   c-file-style: "stroustrup"
   End:
 */