/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 *
 *  Authors: Philip Van Hoof <pvanhoof@gnome.org>
 *
 *  Copyright 2004 Novell, Inc. (www.novell.com)
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of version 2 of the GNU General Public
 *  License as published by the Free Software Foundation.
 *
 *  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 Street #330, Boston, MA 02111-1307, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <glib/gi18n.h>
#ifdef USE_GTKFILECHOOSER
#  include <gtk/gtkfilechooser.h>
#  include <gtk/gtkfilechooserdialog.h>
#else
#  include <gtk/gtkfilesel.h>
#endif
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkstock.h>
#include <gtk/gtk.h>
#include <libedataserver/e-source.h>
#include <libedataserverui/e-source-selector.h>
#include <libecal/e-cal.h>
#include <calendar/gui/e-cal-popup.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libecal/e-cal-time-util.h>
#include <libedataserver/e-util.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlIO.h>
#include <libxml/xpath.h>
#include <string.h>

#include "e-util/e-error.h"
#include "calendar/common/authentication.c"

#include "format-handler.h"

static void 
add_string_to_rdf (xmlNodePtr node, const gchar *tag, const char *value);

/* Use { */

/* #include <calendar/gui/calendar-config-keys.h> */
/* #include <calendar/gui/calendar-config.h> */

/* } or { */
#define CALENDAR_CONFIG_PREFIX "/apps/evolution/calendar"
#define CALENDAR_CONFIG_TIMEZONE CALENDAR_CONFIG_PREFIX "/display/timezone"

static GConfClient *config = NULL;

static gchar *
calendar_config_get_timezone (void)
{

	gchar *retval = NULL;

	if (!config)
		config = gconf_client_get_default ();

	retval = gconf_client_get_string (config, CALENDAR_CONFIG_TIMEZONE, NULL);
	if (!retval) 
		retval = g_strdup ("UTC");

	return retval;
}
/* } */

enum { /* XML helper enum */
	ECALCOMPONENTTEXT,
	ECALCOMPONENTATTENDEE,
	CONSTCHAR
};

static void
display_error_message (GtkWidget *parent, GError *error)
{
	GtkWidget *dialog;

	dialog = gtk_message_dialog_new (GTK_WINDOW (parent), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
					 error->message);
	gtk_dialog_run (GTK_DIALOG (dialog));
	gtk_widget_destroy (dialog);
}

/* Some helpers for the xml stuff */
static void 
add_list_to_rdf (xmlNodePtr node, const gchar *tag, GSList *list_in, gint type)
{
	if (list_in) {
		GSList *list = list_in;

		while (list) {
			const char *str = NULL;

			switch (type) {
			case ECALCOMPONENTATTENDEE:
				str = ((ECalComponentAttendee*)list->data)->value;
				break;
			case ECALCOMPONENTTEXT:
				str = ((ECalComponentText*)list->data)->value;
				break;
			case CONSTCHAR:
			default:
				str = list->data;
				break;
			}

			add_string_to_rdf (node, tag, str);

			list = g_slist_next (list);
		}
	}
}

static void 
add_nummeric_to_rdf (xmlNodePtr node, const gchar *tag, gint *nummeric)
{
	if (nummeric) {
		gchar *value = g_strdup_printf ("%d", *nummeric);
		xmlNodePtr cur_node = xmlNewChild (node, NULL, tag, value);
		xmlSetProp (cur_node, "rdf:datatype", "http://www.w3.org/2001/XMLSchema#integer");
		g_free (value);
	}
}

static void
add_time_to_rdf (xmlNodePtr node, const gchar *tag, icaltimetype *time)
{
	if (time) {
		xmlNodePtr cur_node = NULL;
		struct tm mytm =  icaltimetype_to_tm (time);
		gchar *str = (gchar*) g_malloc (sizeof (gchar) * 200);
		gchar *tmp = NULL;

		/*
		 * Translator: the %FT%T is the thirth argument for a strftime function.
		 * It lets you define the formatting of the date in the rdf-file.
		 * Also check out http://www.w3.org/2002/12/cal/tzd 
		 * */
		e_utf8_strftime (str, 200, _("%FT%T"), &mytm);

		cur_node = xmlNewChild (node, NULL, tag, str);

		/* Not sure about this property */
		tmp = g_strdup_printf ("http://www.w3.org/2002/12/cal/tzd/%s#tz", calendar_config_get_timezone ());
		xmlSetProp (cur_node, "rdf:datatype", tmp);
		g_free (tmp);

		g_free (str);
	}
}


static void 
add_string_to_rdf (xmlNodePtr node, const gchar *tag, const char *value)
{
	if (value) {
		xmlNodePtr cur_node = NULL;
		cur_node = xmlNewChild (node, NULL, tag, value);
		xmlSetProp (cur_node, "rdf:datatype", "http://www.w3.org/2001/XMLSchema#string");
	}
}




static void
do_save_calendar_rdf (FormatHandler *handler, EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri)
{

	/* 
	 * According to some documentation about CSV, newlines 'are' allowed 
	 * in CSV-files. But you 'do' have to put the value between quotes.
	 * The helper 'string_needsquotes' will check for that 
	 *
	 * http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
	 * http://www.creativyst.com/cgi-bin/Prod/15/eg/csv2xml.pl
	 */

	ESource *primary_source;
	ECal *source_client;
	GError *error = NULL;
	GList *objects=NULL;
	GnomeVFSResult result;
	GnomeVFSHandle *handle;
	GnomeVFSURI *uri;
	gchar *temp = NULL;
	gboolean doit = TRUE;

	if (!dest_uri)
		return;

	primary_source = e_source_selector_peek_primary_selection (target->selector);

	/* open source client */
	source_client = auth_new_cal_from_source (primary_source, type);
	if (!e_cal_open (source_client, TRUE, &error)) {
		display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error);
		g_object_unref (source_client);
		g_error_free (error);
		return;
	}

	uri = gnome_vfs_uri_new (dest_uri);

	result = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_READ);
	if (result == GNOME_VFS_OK) 
		doit = e_error_run(GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET (target->selector))),
			 E_ERROR_ASK_FILE_EXISTS_OVERWRITE, dest_uri, NULL) == GTK_RESPONSE_OK;

	if (doit) {
		result = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_WRITE);
		if (result != GNOME_VFS_OK) {
			gnome_vfs_create (&handle, dest_uri, GNOME_VFS_OPEN_WRITE, TRUE, GNOME_VFS_PERM_USER_ALL);
			result = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_WRITE);
		} 
	}


	if (result == GNOME_VFS_OK && doit && e_cal_get_object_list_as_comp (source_client, "#t", &objects, NULL)) {
		xmlBufferPtr buffer=xmlBufferCreate();
		xmlDocPtr doc = xmlNewDoc((xmlChar *) "1.0");
		xmlNodePtr fnode = doc->children;

		doc->children = xmlNewDocNode (doc, NULL, "rdf:RDF", NULL);
		xmlSetProp (doc->children, "xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
		xmlSetProp (doc->children, "xmlns", "http://www.w3.org/2002/12/cal/ical#");

		fnode = xmlNewChild (doc->children, NULL, "Vcalendar", NULL);

		/* Should Evolution publicise these? */
		xmlSetProp (fnode, "xmlns:x-wr", "http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#");
		xmlSetProp (fnode, "xmlns:x-lic", "http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#");

		/* Not sure if it's correct like this */
		xmlNewChild (fnode, NULL, "prodid", "-//" PACKAGE_STRING "//iCal 1.0//EN");

		/* Assuming GREGORIAN is the only supported calendar scale */
		xmlNewChild (fnode, NULL, "calscale", "GREGORIAN");

		temp = calendar_config_get_timezone ();
		xmlNewChild (fnode, NULL, "x-wr:timezone", temp);
		g_free (temp);

		xmlNewChild (fnode, NULL, "method", "PUBLISH");

		xmlNewChild (fnode, NULL, "x-wr:relcalid", e_source_peek_uid (primary_source));

		xmlNewChild (fnode, NULL, "x-wr:calname", e_source_peek_name (primary_source));

		/* Version of this RDF-format */
		xmlNewChild (fnode, NULL, "version", "2.0");

		while (objects != NULL) {
			ECalComponent *comp = objects->data;
			const char *temp_constchar;
			gchar *tmp_str = NULL;
			GSList *temp_list;
			ECalComponentDateTime temp_dt;
			struct icaltimetype *temp_time;
			int *temp_int;
			ECalComponentText temp_comptext; 
			xmlNodePtr c_node = xmlNewChild (fnode, NULL, "component", NULL);
			xmlNodePtr node = xmlNewChild (c_node, NULL, "Vevent", NULL);

			/* Getting the stuff */
			e_cal_component_get_uid (comp, &temp_constchar);
			tmp_str = g_strdup_printf ("#%s", temp_constchar);
			xmlSetProp (node, "about", tmp_str);
			g_free (tmp_str);
			add_string_to_rdf (node, "uid",temp_constchar);

			e_cal_component_get_summary (comp, &temp_comptext);
			add_string_to_rdf (node, "summary",&temp_comptext?temp_comptext.value:NULL);

			e_cal_component_get_description_list (comp, &temp_list);
			add_list_to_rdf (node, "description", temp_list, ECALCOMPONENTTEXT);
			if (temp_list)
				e_cal_component_free_text_list (temp_list);

			e_cal_component_get_categories_list (comp, &temp_list);
			add_list_to_rdf (node, "categories", temp_list, CONSTCHAR);
			if (temp_list)
				e_cal_component_free_categories_list (temp_list);

			e_cal_component_get_comment_list (comp, &temp_list);
			add_list_to_rdf (node, "comment", temp_list, ECALCOMPONENTTEXT);

			if (temp_list)
				e_cal_component_free_text_list (temp_list);

			e_cal_component_get_completed (comp, &temp_time);
			add_time_to_rdf (node, "completed", temp_time);
			if (temp_time)
				e_cal_component_free_icaltimetype (temp_time);

			e_cal_component_get_created (comp, &temp_time);
			add_time_to_rdf (node, "created", temp_time);
			if (temp_time)
				e_cal_component_free_icaltimetype (temp_time);

			e_cal_component_get_contact_list (comp, &temp_list);
			add_list_to_rdf (node, "contact", temp_list, ECALCOMPONENTTEXT);
			if (temp_list)
				e_cal_component_free_text_list (temp_list);

			e_cal_component_get_dtstart (comp, &temp_dt);
			 add_time_to_rdf (node, "dtstart", temp_dt.value ? temp_dt.value : NULL);
			if (temp_dt.value)
				e_cal_component_free_datetime (&temp_dt);

			e_cal_component_get_dtend (comp, &temp_dt);
			add_time_to_rdf (node, "dtend", temp_dt.value ? temp_dt.value : NULL);
			if (temp_dt.value)
				e_cal_component_free_datetime (&temp_dt);

			e_cal_component_get_due (comp, &temp_dt);
			add_time_to_rdf (node, "due", temp_dt.value ? temp_dt.value : NULL);
			if (temp_dt.value)
				e_cal_component_free_datetime (&temp_dt);

			e_cal_component_get_percent (comp, &temp_int);
			add_nummeric_to_rdf (node, "percentComplete", temp_int);

			e_cal_component_get_priority (comp, &temp_int);
			add_nummeric_to_rdf (node, "priority", temp_int);

			e_cal_component_get_url (comp, &temp_constchar);
			add_string_to_rdf (node, "URL", temp_constchar);

			if (e_cal_component_has_attendees (comp)) {
				e_cal_component_get_attendee_list (comp, &temp_list);
				add_list_to_rdf (node, "attendee", temp_list, ECALCOMPONENTATTENDEE);
				if (temp_list)
					e_cal_component_free_attendee_list (temp_list);
			}

			e_cal_component_get_location (comp, &temp_constchar);
			add_string_to_rdf (node, "location", temp_constchar);

			e_cal_component_get_last_modified (comp, &temp_time);
			add_time_to_rdf (node, "lastModified",temp_time);


			/* Important note!
			 * The documentation is not requiring this!
			 *
			 * if (temp_time) e_cal_component_free_icaltimetype (temp_time);
			 *
			 * Please uncomment and fix documentation if untrue
			 * http://www.gnome.org/projects/evolution/developer-doc/libecal/ECalComponent.html
			 *	#e-cal-component-get-last-modified
			 */

			objects = g_list_next (objects);
		}

		/* I used a buffer rather than xmlDocDump: I want gnome-vfs support */
		xmlNodeDump (buffer, doc, doc->children, 2, 1);

		gnome_vfs_write (handle, xmlBufferContent (buffer), xmlBufferLength (buffer), NULL);

		xmlBufferFree (buffer);
		xmlFreeDoc (doc);
		gnome_vfs_close (handle);
	}

	g_object_unref (source_client);

	return;
}

FormatHandler *rdf_format_handler_new (void)
{
	FormatHandler *handler = g_new (FormatHandler, 1);

	handler->isdefault = FALSE;
	handler->combo_label = _("RDF format (.rdf)");
	handler->filename_ext = ".rdf";
	handler->options_widget = NULL;
	handler->save = do_save_calendar_rdf;

	return handler;
}