/*
** iksemel (XML Parser for Jabber)
** Copyright (C) 2000-2001 Gurer Ozen <palpa@jabber.org>
**
** This code is free software; you can redistribute it and/or
** modify it under the terms of GNU Lesser General Public License.
**
** xml node management
*/

#include "common.h"
#include "iksemel.h"

/*****  Node Creating & Deleting  *****/

iks *iks_new(const char *name)
{
	ikspool *p;
	iks *x;

	p = iks_pool_new(1024);
	if(!p) return(NULL);

	x = iks_new_pool(p, name);
	if(!x) {
		iks_pool_delete(p);
		return NULL;
	}

	return x;
}


iks *iks_new_pool(ikspool *p, const char *name)
{
	iks *x;

	x = iks_pool_alloc(p, sizeof(iks));
	if(!x) return(NULL);
	memset(x, 0, sizeof(iks));

	x->p = p;
	x->type = IKS_TAG;
	if(name)
	{
		x->name = iks_pool_strdup(p, name);
		if(!x->name) return(NULL);
	}

	return x;
}


iks *iks_insert(iks *x, const char *name)
{
	iks *y;

	if(!x) return(NULL);

	y = iks_new_pool(x->p, name);
	if(!y) return(NULL);

	y->parent = x;
	if(!x->children) x->children=y;
	if(x->lastchild)
	{
		x->lastchild->next = y;
		y->prev = x->lastchild;
	}
	x->lastchild = y;

	return y;
}


iks *iks_insert_cdata(iks *x, const char *data, int len)
{
	iks *y;

	if(!x || !data) return(NULL);

	if(len == -1) len = strlen(data);

	y = x->lastchild;
	if(y && y->type == IKS_CDATA)
	{
		char *tmp;
		tmp = iks_pool_alloc(x->p, y->len + len + 1);
		if(!tmp) return(NULL);
		memcpy(tmp, y->cdata, y->len);
		memcpy(tmp + y->len, data, len);
		tmp[y->len + len] = '\0';
		y->cdata = tmp;
		y->len += len;
	}
	else
	{
		y = iks_insert(x, NULL);
		if(!y) return(NULL);
		y->type = IKS_CDATA;
		y->cdata = iks_pool_alloc(x->p, len + 1);
		if(!y->cdata) return(NULL);
		memcpy(y->cdata, data, len);
		y->cdata[len] = '\0';
		y->len = len;
	}

	return y;
}


iks *iks_insert_attrib(iks *x, const char *name, const char *value)
{
	iks *y;
	int len;

	if(!x || !name || !value) return(NULL);

	y = iks_new_pool(x->p, name);
	if(!y) return(NULL);

	len = strlen(value);
	y->type = IKS_ATTRIBUTE;
	y->cdata = iks_pool_alloc(x->p, len + 1);
	if(!y->cdata) return(NULL);
	memcpy(y->cdata, value, len);
	*(y->cdata + len) = '\0';
	y->len = len;

	y->parent = x;
	if(!x->attribs) x->attribs = y;
	if(x->lastattrib)
	{
		x->lastattrib->next = y;
		y->prev = x->lastattrib;
	}
	x->lastattrib = y;

	return y;
}


iks *iks_insert_node(iks *x, iks *y)
{
	y->parent = x;
	if(!x->children) x->children=y;
	if(x->lastchild)
	{
		x->lastchild->next = y;
		y->prev = x->lastchild;
	}
	x->lastchild = y;

	return y;
}


void iks_hide(iks *x)
{
	iks *y;

	if(!x) return;

	if(x->prev) x->prev->next = x->next;
	if(x->next) x->next->prev = x->prev;

	y = x->parent;
	if(y)
	{
		if(y->children == x) y->children = x->next;
		if(y->lastchild == x) y->lastchild = x->prev;
	}
}


void iks_delete(iks *x)
{
	if(x) iks_pool_delete(x->p);
}


/*****  Node Traversing  *****/

iks *iks_next(iks *x)
{
	if(x) return(x->next);
	return NULL;
}


iks *iks_prev(iks *x)
{
	if(x) return(x->prev);
	return NULL;
}


iks *iks_parent(iks *x)
{
	if(x) return(x->parent);
	return NULL;
}


iks *iks_child(iks *x)
{
	if(x) return(x->children);
	return NULL;
}


iks *iks_attrib(iks *x)
{
	if(x) return(x->attribs);
	return NULL;
}


iks *iks_find(iks *x, const char *name)
{
	iks *y;

	if(!x) return(NULL);

	/* FIX ME: for advanced searchs */

	y = x->children;
	while(y)
	{
		if(y->name && strcmp(y->name, name) == 0) return(y);
		y = y->next;
	}

	return NULL;
}


char *iks_find_cdata(iks *x, const char *name)
{
	iks *y;

	y = iks_find(x, name);
	if(!y) return(NULL);

	y = y->children;
	if(!y) return(NULL);

	return y->cdata;
}


char *iks_find_attrib(iks *x, const char *name)
{
	iks *y;

	if(!x) return(NULL);

	y = x->attribs;
	while(y)
	{
		if(iks_strcmp(y->name, name) == 0) return(y->cdata);
		y = y->next;
	}

	return NULL;
}


/*****  Node Information  *****/

ikspool *iks_pool(iks *x)
{
	if(x) return(x->p);
	return NULL;
}


enum ikstype iks_type(iks *x)
{
	if(x) return(x->type);
	return IKS_NONE;
}


char *iks_name(iks *x)
{
	if(x) return(x->name);
	return NULL;
}


char *iks_cdata(iks *x)
{
	if(x) return(x->cdata);
	return NULL;
}


int iks_cdata_size(iks *x)
{
	if(x) return(x->len);
	return 0;
}


int iks_has_children(iks *x)
{
	if(x && x->children) return(1);
	return 0;
}


int iks_has_attribs(iks *x)
{
	if(x && x->attribs) return(1);
	return 0;
}


/*****  Special  *****/

ikstr *iks_string(ikspool *p, iks *x)
{
	ikstr *s;
	int level=0, dir=0;
	iks *y;

	if(!x || x->type != IKS_TAG) return(NULL);

	s = iks_str_new(p);
	if(!s) return(NULL);
	p = s->p;

	while(1)
	{
		if(dir==0)
		{
			if(x->type == IKS_TAG)
			{
				iks *y;

				iks_str_add(s, "<");
				iks_str_add(s, x->name);
				y = x->attribs;
				while(y)
				{
					iks_spool(s, " ", y->name, "='", iks_escape(p, y->cdata, y->len), "'", s);
					y = y->next;
				}
				if(x->children)
				{
					iks_str_add(s, ">");
					x = x->children;
					level++;
					continue;
				}
				else
					iks_str_add(s, "/>");
			}
			else
				iks_str_add(s, iks_escape(p, x->cdata, x->len));
		}

		y = x->next;
		if(!y)
		{
			x = x->parent;
			level--;
			if(level >= 0)
			iks_spool(s, "</", x->name, ">", s);
			if(level < 1) break;
			dir = 1;
		}
		else
		{
			x = y;
			dir = 0;
		}
	}

	return s;
}
