/*
** 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.
**
** parser management
*/

#include "common.h"

#ifdef USE_EXPAT
#include <expat.h>
#define IKSEMEL_EXPAT
#endif

#include "iksemel.h"


static void dom_tstart(iksparser *prs, char *tag, char **atts);
static void dom_tend(iksparser *prs, char *tag);
static void jab_tstart(iksparser *prs, char *tag, char **atts);
static void jab_tend(iksparser *prs, char *tag);
static void hook_cdata(iksparser *prs, char *data, int len);


iksparser *iks_sax_new(void *udata, iksTagStart *ts, iksTagEnd *te, iksCharData *cd)
{
	iksparser *prs;

	prs = malloc(sizeof(iksparser));
	if(!prs) return(NULL);
	memset(prs, 0, sizeof(iksparser));
#ifdef USE_EXPAT
	prs->expat = XML_ParserCreate(NULL);
	if(!prs->expat)
	{
		free(prs);
		return NULL;
	}
	XML_SetUserData(prs->expat, (void *)prs);
	XML_SetElementHandler(prs->expat, (XML_StartElementHandler)ts, (XML_EndElementHandler)te);
	XML_SetCharacterDataHandler(prs->expat, (XML_CharacterDataHandler)cd);
#endif
	prs->udata = udata;
	prs->udata2 = udata;
	prs->tagStart = ts;
	prs->tagEnd = te;
	prs->charData = cd;

	return prs;
}


iksparser *iks_dom_new(iks **iksptr)
{
	iksparser *prs;

	prs = iks_sax_new(NULL, (iksTagStart *)dom_tstart, (iksTagEnd *)dom_tend, (iksCharData *)hook_cdata);
	if(!prs) return(NULL);
	prs->udata = prs;
	prs->iksptr = iksptr;
	return prs;
}


iksparser *iks_jabber_new(void *udata, iksJPak *jp)
{
	iksparser *prs;

	prs = iks_sax_new(NULL, (iksTagStart *)jab_tstart, (iksTagEnd *)jab_tend, (iksCharData *)hook_cdata);
	if(!prs) return(NULL);
	prs->udata = prs;
	prs->udata2 = udata;
	prs->jPak = jp;
	return prs;
}


void iks_set_logging(iksparser *prs, iksJLog *jl)
{
	prs->jLog = jl;
}


#ifdef USE_EXPAT
int iks_parse(iksparser *prs, char *data, int len, int finish)
{
	if(XML_Parse(prs->expat, data, len, 0))
		return IKS_RET_OK;

	return IKS_RET_BADXML;
}
#endif


void iks_parser_delete(iksparser *prs)
{
#ifdef USE_EXPAT
	XML_ParserFree(prs->expat);
#endif
	if(prs->conn) iks_disconnect(prs);
	free(prs);
}


/*****  Internal Hooks  *****/

static void insert_attribs(iksparser* p, iks *x, char **atts)
{
	int i=0;

	if(!x || !atts) return;
	while(atts[i])
	{
		iks_insert_attrib(x, atts[i], atts[i+1]);
		i += 2;
	}
}


static void dom_tstart(iksparser *prs, char *tag, char **atts)
{
	iks *x;

	/* FIX ME: handle PIs in parser */
	if(tag[0] == '?') return;

	if(prs->current)
		x = iks_insert(prs->current, tag);
	else
		x = iks_new(tag);
	insert_attribs(prs, x, atts);
	prs->current = x;
}


static void jab_tstart(iksparser *prs, char *tag, char **atts)
{
	iks *x;

	/* FIX ME: handle PIs in parser */
	if(tag[0] == '?') return;

	if(prs->current)
	{
		x = iks_insert(prs->current, tag);
		insert_attribs(prs, x, atts);
		prs->current = x;
	}
	else
	{
		x = iks_new(tag);
		insert_attribs(prs, x, atts);
		if(strcmp(tag, "stream:stream") == 0)
			CALL_JPAK(iks_packet(x));
		else
			prs->current = x;
	}
}


static void dom_tend(iksparser *prs, char *tag)
{
	iks *x;

	x = iks_parent(prs->current);
	if(x)
		prs->current = x;
	else
		*(prs->iksptr) = prs->current;
}


static void jab_tend(iksparser *prs, char *tag)
{
	iks *x;

	if(!prs->current)
	{
		/* got </stream:stream> */
		CALL_JPAK(NULL);
		return;
	}

	x = iks_parent(prs->current);
	if(!x) CALL_JPAK(iks_packet(prs->current));
	prs->current = x;
}


static void hook_cdata(iksparser *prs, char *data, int len)
{
	if(prs->current)
		iks_insert_cdata(prs->current, data, len);
}
