/*
 *  Copyright (C) 2004 Mathias Andre <mathias@openbrookes.org>
 *
 *  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 2 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.
 *
 */

#include <glib.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "nb_note.h"
#include "nb_note_list.h"

/* create and free object */
NoteList *
nb_note_list_new (gchar * filename)
{
	NoteList * self = g_malloc (sizeof (NoteList));
	self->notes = g_ptr_array_new ();
	self->filename = filename;

	return self;
}

void
nb_note_list_free (NoteList * self)
{
	g_ptr_array_free (self->notes, TRUE);
	g_free (self->filename);
}

/* manage notes */
void
nb_note_list_add (NoteList * self, Note * n)
{
	g_ptr_array_add (self->notes, n);
}

gboolean
nb_note_list_remove (NoteList * self, Note * n)
{
	return g_ptr_array_remove (self->notes, n);
}

gint
nb_note_list_get_note_index (NoteList * self, Note * n)
{
	gint i;

	for ( i = 0 ; i < nb_note_list_get_nb (self) ; i++ )
	{
		if ( g_ptr_array_index (self->notes, i) == n )
			return i;
	}

	return -1;
}

gint
nb_note_list_get_nb (NoteList * self)
{
	return self->notes->len;
}

/* read/write from/to xml doc */
static gboolean
nb_note_list_parse_xml_file (NoteList * self, xmlDocPtr * doc, xmlNodePtr * cur)
{
	*doc = xmlParseFile (self->filename);

	if ( *doc == NULL )
	{
		fprintf (stderr, "Document not parsed successfully.\n");
		return FALSE;
	}

	*cur = xmlDocGetRootElement (*doc);

	if ( *cur == NULL )
	{
		fprintf (stderr, "Empty document.\n");
		xmlFreeDoc (*doc);
		return FALSE;
	}

	if ( xmlStrcmp ((*cur)->name, (const xmlChar *) "notelist") )
	{
		fprintf (stderr, "Wrong document type, root node is not \"notelist\".\n");
		xmlFreeDoc (*doc);
		return FALSE;
	}

	*cur = (*cur)->xmlChildrenNode;

	return TRUE;
}

gboolean 
nb_note_list_load_xml_file (NoteList * self)
{
	xmlDocPtr doc;
	xmlNodePtr cur;

	if ( !nb_note_list_parse_xml_file (self, &doc, &cur) )
		return FALSE;

	/* loop every "note" node */
	while ( cur != NULL )
	{
		if ( !xmlStrcmp (cur->name, (const xmlChar *) "note") )
		{
			Note * n = NULL;
			n = nb_note_new_from_xml_doc (doc, cur);

			if ( n == NULL )
				fprintf (stderr, "Invalid note in file, skip note...\n");
			else
				nb_note_list_add (self, n);
		}
		cur = cur->next;
	}

	xmlFreeDoc (doc);

	return TRUE;
}

gchar *
nb_note_list_get_note_text_from_index (NoteList * self, gint index)
{
	xmlDocPtr doc;
	xmlNodePtr cur;

	if ( !nb_note_list_parse_xml_file (self, &doc, &cur) )
		return NULL;

	/* loop every "note" node */
	while ( cur != NULL )
	{
		if ( !xmlStrcmp (cur->name, (const xmlChar *) "note") )
		{
			gint i = atoi (xmlGetProp (cur, "index"));

			if ( i == index )
			{
				xmlChar * text;
				text = nb_note_get_text_from_file (doc, cur);

				xmlFreeDoc (doc);

				return text;
			}
		}
		cur = cur->next;
	}

	xmlFreeDoc (doc);

	return NULL;
}

gboolean
nb_note_list_update_xml_file (NoteList * self, Note * n, gint action)
{
	/* TODO: update this functon to handle the DTD */

	xmlDocPtr doc;
	xmlNodePtr cur;
	xmlNsPtr ns;
	int i = 0;


	doc = xmlParseFile (self->filename);

	if ( doc == NULL )
	{
		if ( action == ADD_NOTE )
		{
			printf ("No document found, creating one.\n");

			/* document is empty */
			doc = xmlNewDoc ("1.0");

			cur = xmlNewNode (NULL, "notelist");

			xmlDocSetRootElement (doc, cur);
		}
		else
		{
			fprintf (stderr, "Document not parsed successfully.\n");
			return FALSE;
		}
	}

	cur = xmlDocGetRootElement (doc);

	ns = xmlNewNs (cur, "http://www.notbook.org/ns", "notabene");

	if ( cur == NULL )
	{
		fprintf (stderr, "Empty document.\n");
		xmlFreeDoc (doc);
		return FALSE;
	}

	if ( xmlStrcmp (cur->name, (const xmlChar *) "notelist") )
	{
		fprintf (stderr, "Wrong document type, root node is not \"notelist\".\n");
		xmlFreeDoc (doc);
		return FALSE;
	}

	if ( action == ADD_NOTE )
		xmlAddChild (cur, nb_note_get_xml_pointer (n, ns, nb_note_list_get_nb (self)));
	else
	{
		gboolean reindex = FALSE;
		int index;

		cur = cur->xmlChildrenNode;

		index = nb_note_list_get_note_index (self, n);

		/* loop every "note" node */
		while ( cur != NULL )
		{
			if ( !xmlStrcmp (cur->name, (const xmlChar *) "note") )
			{

				i = atoi (xmlGetProp (cur, "index"));

				if ( reindex )
				{
					/* reindex the following notes after one has been removed */
					gchar * cindex = g_strdup_printf ("%d", i -1);

					xmlSetProp (cur, "index", cindex);
					/* TODO: check if we need to free cindex (probably)*/
				}
				else if ( i == index )
				{
					if ( action == UPDATE_NOTE )
						xmlReplaceNode (cur, nb_note_get_xml_pointer (n, ns, index));
					else if ( action == REMOVE_NOTE )
					{
						xmlNodePtr del = cur;

						if ( cur->prev != NULL )
							cur->prev->next = cur->next;
						if ( cur->next != NULL )
							cur->next->prev = cur->prev;

						xmlFreeNode (del);
						reindex = TRUE;
					}
				}
			}
			cur = cur->next;
		}
	}
	xmlSaveFormatFile (self->filename, doc, 1);
	xmlFreeDoc (doc);

	return TRUE;
}
