/*
 *  Copyright (c) 2004-2013 Gert Wollny <gw.fossdev@gmail.com>
 *
 *  This program 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *  As an exception to this license, "NEC C&C Research Labs" may use
 *  this software under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <glib.h>
#include <glib-object.h>

#include <mialm/miaxmlio.h>


/**
 * SECTION:miaxmlio
 * @Short_description: XML in-and output routines for landmark related data 
 * @Title: XML manipulation of landmarks 
 * 
 * The functions provided in tis section are used to load ans store landmarks and 
 * related information from and to XML trees.
 * 
 */


#define SAX_UNKNOWN -1
#define SAX_FINISH  -3

static int status = 0; 

static void
SAXwarning (void *UNUSED(user_data), const char *msg, ...)
{
	va_list args;

	va_start (args, msg);
	g_logv ("XML", G_LOG_LEVEL_WARNING, msg, args);
	va_end (args);
}

static void
SAXerror (void *UNUSED(user_data), const char *msg, ...)
{
	va_list args;

	va_start (args, msg);
	g_logv ("XML", G_LOG_LEVEL_CRITICAL, msg, args);
	va_end (args);
}

static void
SAXfatalError (void *UNUSED(user_data), const char *msg, ...)
{
	va_list args;

	va_start (args, msg);
	g_logv ("XML", G_LOG_LEVEL_ERROR, msg, args);
	va_end (args);
	status = -1; 
}

static void
SAXstartDocument (void *UNUSED(user_data))
{
	// TODO replace this by a GObject based structure

}

static void
SAXendDocument (void *user_data)
{
	ParserState *state = (ParserState *) user_data;
	g_assert (state->pss);
		
	if (state->pss->parent) 
	{
		SAXwarning (user_data, "error parsing file");
	}
}

static void
SAXcharacters (void *user_data, const xmlChar * ch, int len)
{
	ParserState *state = (ParserState *) user_data;
	g_assert (state->pss);

	if (state->pss->ch_callback)
	{
		state->pss->ch_callback (state, ch, len);
	}
}

static void
SAXstartElement (void *user_data, const xmlChar * name,
		 const xmlChar ** attrs)
{
	gint i;
	const ParserTags *element;
	ParserStateStack *self;

	ParserState *state = (ParserState *) user_data;
	if (state->pss->unknown_depth)
	{
		state->pss->unknown_depth++;
		return; 
	}

	element = state->pss->parser_tags;
	
	self = g_new0 (ParserStateStack, 1);

	self->parent = state->pss;
	state->pss = self;

	for (i = 0; element[i].tag_name != NULL; ++i)
		if (!xmlStrcmp (name, (const xmlChar *)element[i].tag_name))
		{
			self->property = g_string_new((const gchar *)name);
			if (element[i].ch_callback) {
				self->ch_callback = element[i].ch_callback;
				self->ch = g_string_new("");
			}
			self->end_callback = element[i].end_callback;
			
			
			
			if (element[i].start_callback)
				element[i].start_callback (state, attrs);
			
			self->parser_tags = element[i].parser_tags; 
			return;
		}
		
	/* element not found */
	SAXwarning (user_data, "Unknown element %s", name);
	state->pss->unknown_depth = 1;
}

static void
SAXendElement (void *user_data, const xmlChar * UNUSED(name))
{

	ParserState *state = (ParserState *) user_data;
	ParserStateStack *self;

	if (state->pss->unknown_depth)
	{
		state->pss->unknown_depth--;
		return;
	}
	self = state->pss;
	
	if (self->end_callback) 
		self->end_callback (state, self->property->str);
	
	if (self->ch_callback)
		g_string_free(state->pss->ch, TRUE);
	
	g_string_free(self->property, TRUE);

	state->pss = state->pss->parent;
	g_free (self);
}


/**
 * xmlio_end_string:
 * @state: holds the current state of the parser 
 * @property: the property that is currently read from the XML input 
 *
 * Store the read string as property in the currently active entity 
 */


void 
xmlio_end_string(ParserState * state, const gchar *property)
{
	GValue value = G_VALUE_INIT;
	GObject *obj = G_OBJECT (state->pss->parent->data);

	g_value_init (&value, G_TYPE_STRING);
	g_value_set_string(&value, state->pss->ch->str);
	g_object_set_property (obj, property, &value);
}


/**
 * xml_write_float:
 * @root:  root node to add thenew data to 
 * @ns: XML name space identified 
 * @tag: node tag to be used to store the data 
 * @f: floating point value to be stored  
 *
 * Store the value @f as new node of @root with tag @tag in namespace @ns
 *
 * Returns: TRUE if successful, FALSE otherwise. 
 */

gboolean
xml_write_float (xmlNodePtr root, xmlNsPtr ns, const gchar * tag, gfloat f)
{
	gchar text[1024];
	g_snprintf (text, 1023, "%f", f);
	return (xmlNewTextChild (root, ns, (const xmlChar *)tag, (const xmlChar *)text) != NULL);
}


/**
 * xmlio_end_float:
 * @state: holds the current state of the parser 
 * @property: the property that is currently read from the XML input 
 *
 * Store the read floating point value as property in the currently active entity 
 */

void 
xmlio_end_float(ParserState * state, const gchar *property)
{
	GValue value = G_VALUE_INIT;
	gfloat f = atof (state->pss->ch->str);
	GObject *obj = G_OBJECT (state->pss->parent->data);
	g_assert (obj);
	
	g_value_init (&value, G_TYPE_FLOAT);
	g_value_set_float (&value, f);
	g_object_set_property(obj, property, &value);
}


/**
 * xmlio_get_string:
 * @state:  current XML parser state 
 * @ch: character string 
 * @len: length of the input character string 
 *
 * Append the string @ch to the currently read string in the parser state 
 *
 */
void
xmlio_get_string (ParserState * state, const xmlChar * ch, int len)
{
	g_assert (state->pss);
	g_assert (state->pss->ch);
	g_string_insert_len (state->pss->ch,
			     state->pss->ch->len - 1, (const gchar *)ch, len);
}

/**
 * xml_sax_parse:
 * @filename:  filename of the input XML file
 * @tags: A table of tags with their corresponding callback map 
 * @pdata: (closure): Private data passed to the parser state 
 *
 * Parse the given XML file by using the given callback table to handle the 
 * tags accordingly by calling xmlSAXUserParseFile from the libxml2 library. 
 *
 * Returns: 0 in the case of success and the error number returned by xmlSAXUserParseFile
 *
 */
int 
xml_sax_parse (const gchar * filename, const ParserTags * tags,
	       gpointer pdata)
{
	int result; 
	
	ParserState state = {
	      pss:NULL,
	      data:pdata
	};

	xmlSAXHandler sax_handler = {
	      startDocument:SAXstartDocument,
	      endDocument:SAXendDocument,
	      characters:SAXcharacters,
	      startElement:SAXstartElement,
	      endElement:SAXendElement,
	      warning:SAXwarning,
	      error:SAXerror,
	      fatalError:SAXfatalError
	};
	
	state.pss = g_new0 (ParserStateStack, 1);
	
	state.pss->parser_tags = tags;
	
	result = xmlSAXUserParseFile (&sax_handler, &state, filename);
	g_free(state.pss);
	return result; 
}
