
#define _GNU_SOURCE		// for string.h

#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <string.h>

#include <assert.h>
#include <libxml/parser.h>

#include "gnokii_sync.h"
#include "entrydb.h"

/** 
 * XML parser's startElement fuction
 * Handles <entries> and <entry ...> elements
 */

static void gn_xml_startElement(void *conn, const xmlChar * name,
			 const xmlChar ** attrs)
{
	gnokii_sync_object *entry;
	int i;
	i = 0;
	int got_attrs = 0;
	unsigned int objtype;
	int mem_type;

	if (!strncmp(name, "entries", strlen("entries")))
		((gnokii_connection *) conn)->parser_state = START;

	if (!strncmp(name, "entry", strlen("entry"))) {
		((gnokii_connection *) conn)->parser_state = INSIDE_ENTRY;

		if (attrs) {
			entry = g_malloc(sizeof(gnokii_sync_object));
			entry->vobj = NULL;

			// extract attributes
			while (attrs[i] != NULL) {
				if (!strncmp
				    (attrs[i], "object_type",
				     strlen("object_type"))) {
					assert(attrs[i + 1]);
					got_attrs +=
					    sscanf(attrs[i + 1], "%d",
						   &objtype);
					entry->object_type =
					    (unsigned char) objtype;
				} else
				    if (!strncmp
					(attrs[i], "changed",
					 strlen("changed"))) {
					assert(attrs[i + 1]);
					got_attrs +=
					    sscanf(attrs[i + 1], "%d",
						   &(entry->changed));
				} else if (!strncmp
					   (attrs[i], "mem_type",
					    strlen("mem_type"))) {
					assert(attrs[i + 1]);
					got_attrs +=
					    sscanf(attrs[i + 1], "%d",
						   &mem_type);
					entry->mem_type = mem_type;
				} else if (!strncmp
					   (attrs[i], "location",
					    strlen("location"))) {
					assert(attrs[i + 1]);
					got_attrs +=
					    sscanf(attrs[i + 1], "%d",
						   &(entry->location));

				}
				i++;
				i++;

			}

			// got enough attributes
			assert(got_attrs == 4);

			// add new entry to entries list
			((gnokii_connection *) conn)->entries =
			    g_list_prepend(((gnokii_connection *) conn)->
					   entries, entry);

		}


	}

}

/** 
 * XML parser's characters fuction
 * Stores characters in the current entry.vobj string
 */
static void gn_xml_characters(void *conn, const xmlChar * ch, int len)
{
	char *tmp_str;

	if (((gnokii_connection *) conn)->parser_state == INSIDE_ENTRY
	    && ((gnokii_connection *) conn)->entries) {
		if (((gnokii_sync_object
		      *) (((GList *) (((gnokii_connection *) conn)->
				      entries))->data))->vobj == NULL)
			((gnokii_sync_object
			  *) (((GList *) (((gnokii_connection *) conn)->
					  entries))->data))->vobj =
	 strndup(ch, len);
		else {
			tmp_str = ((gnokii_sync_object
				    *) (((GList *) (((gnokii_connection *)
						     conn)->entries))->
					data))->vobj;
			((gnokii_sync_object
			  *) (((GList *) (((gnokii_connection *) conn)->
					  entries))->data))->vobj =
	 malloc(strlen(tmp_str) + len + 1);
			strcpy(((gnokii_sync_object
				 *) (((GList
				       *) (((gnokii_connection *) conn)->
					   entries))->data))->vobj,
			       tmp_str);
			strncpy(((gnokii_sync_object
				  *) (((GList
					*) (((gnokii_connection *) conn)->
					    entries))->data))->vobj +
				strlen(tmp_str), ch, len);
			free(tmp_str);
		}
	}
}

/** 
 * XML parser's endElement fuction
 * Exits from INSIDE_ENTRY state in parser
 */
static void gn_xml_endElement(void *conn, const xmlChar * name)
{


	if (!strncmp(name, "entry", strlen("entry")))
		((gnokii_connection *) conn)->parser_state = UNKNOWN;
}

/**
 * Loads entry list from configfile
 */
void gnokii_load_entries(gnokii_connection * conn)
{
	char *filename;
	filename =
	    g_strdup_printf("%s/gnokii-entries",
			    sync_get_datapath(conn->sync_pair));
	xmlSAXHandler handler = {
		NULL,		/* internalSubset */
		NULL,		/* isStandalone */
		NULL,		/* hasInternalSubset */
		NULL,		/* hasExternalSubset */
		NULL,		/* resolveEntity */
		NULL,		/* getEntity */
		NULL,		/* entityDecl */
		NULL,		/* notationDecl */
		NULL,		/* attributeDecl */
		NULL,		/* elementDecl */
		NULL,		/* unparsedEntityDecl */
		NULL,		/* setDocumentLocator */
		NULL,		/* startDocument */
		NULL,		/* endDocument */
		gn_xml_startElement,	/* startElement */
		gn_xml_endElement,	/* endElement */
		NULL,		/* reference */
		gn_xml_characters,	/* characters */
		NULL,		/* ignorableWhitespace */
		NULL,		/* processingInstruction */
		NULL,		/* comment */
		NULL,		/* xmlParserWarning */
		NULL,		/* xmlParserError */
		NULL,		/* xmlParserError */
		NULL,		/* getParameterEntity */
		NULL,		/* cdataBlock; */
		NULL,		/* externalSubset; */
		1, NULL, NULL,	/* startElementNs */
		NULL,		/* endElementNs */
		NULL		/* xmlStructuredErrorFunc */
	};
	// broken gnokii-entries is treated as empty
	if (xmlSAXUserParseFile(&handler, conn, filename) < 0) {
		printf
		    ("Parse error in gnokii-entries file or file is empty.\n");
	}

	g_free(filename);
}

/**
 * Saves entry list to configfile
 */
void gnokii_save_entries(gnokii_connection * conn)
{
	char *filename;
	FILE *f;
	filename =
	    g_strdup_printf("%s/gnokii-entries",
			    sync_get_datapath(conn->sync_pair));
	if ((f = fopen(filename, "w"))) {
		GList *l;
		// TODO save config file version (for different config file formats)
		// TODO find or create schema for this file 
		fprintf(f,
			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<entries>\n");
		for (l = g_list_first(conn->entries); l;
		     l = g_list_next(l)) {

			gnokii_sync_object *entry;
			entry = l->data;
			fprintf(f,
				"<entry object_type=\"%d\" changed=\"%d\" mem_type=\"%d\" location=\"%d\">%s</entry>\n",
				entry->object_type, entry->changed,
				entry->mem_type, entry->location,
				entry->vobj);
		}
		fprintf(f, "</entries>");
		fclose(f);
	}
	g_free(filename);
}

