/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
 * kolab-conv.c
 *
 *  Created on: 16.09.2010
 *      Author: Hendrik Helwich <h.helwich@tarent.de>,
 *      		Peter Neuhaus <p.neuhaus@tarent.de>
 */

/*
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 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 Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
 */

#include <config.h>

#include <glib/gi18n-lib.h>

#include <libecal/e-cal-util.h>
#include "kolab-conv.h"
#include "evolution/evolution.h"
#include "kolab/kolab.h"
#include "logging.h"
#include "kolab/kolab-util.h"

/**
 * kolabconv_error_generic_quark:
 *
 * Create error quark for generic error.
 *
 * Returns: Error quark
 */
GQuark
kolabconv_error_generic_quark(void)
{
	static GQuark q = 0;
	if (0 == q)
		q = g_quark_from_static_string("kolabconv-error-generic-quark");
	return q;
}

/**
 * kolabconv_error_read_kolab_quark:
 *
 * Create error quark for kolab read error.
 *
 * Returns: Error quark
 */
GQuark
kolabconv_error_read_kolab_quark(void)
{
	static GQuark q = 0;
	if (0 == q)
		q = g_quark_from_static_string("kolabconv-error-read-kolab-quark");
	return q;
}

/**
 * kolabconv_error_read_evolution_quark:
 *
 * Create error quark for evolution read error.
 *
 * Returns: Error quark
 */
GQuark
kolabconv_error_read_evolution_quark(void)
{
	static GQuark q = 0;
	if (0 == q)
		q = g_quark_from_static_string("kolabconv-error-read-evolution-quark");
	return q;
}


/*----------------------------------------------------------------------------*/

/**
 * kolabconv_cal_util_freebusy_new_fb_url:
 * @servername: The fully-qualified server name or an IP number string
 * @username: The user name to use for the request URL
 * @use_ssl If TRUE, use "HTTPS", otherwise use "HTTP"
 * @listtype The type of URL to create (trigger URL or ifb, vfb, xfb)
 *
 * Creates a new Kolab F/B trigger/request URL given the Kolab
 * server name, user name and whether or not to use an SSL
 * encrypted connection. The listtype parameter specifies which
 * kind of URL to create (trigger, ifb, vfb, xfb).
 *
 * Returns: A NULL-terminated string containing a full KOLAB F/B trigger/request URL
 *          (free it using g_free() if no longer needed)
 */
gchar*
kolabconv_cal_util_freebusy_new_fb_url (const gchar *servername,
                                        const gchar *username,
                                        gboolean use_ssl,
                                        Kolab_conv_freebusy_type listtype)
{
	/* TODO: Improve me! */

	gchar *url_string;
	gchar *protocol;
	gchar *res_name;

	g_assert (servername != NULL);
	g_assert (username != NULL);

	if (use_ssl)
		protocol = "https";
	else
		protocol = "http";

	switch (listtype) {
	case KOLABCONV_FB_TYPE_TRIGGER:
		res_name = g_strconcat ("trigger/", username, "/Calendar.pfb", NULL);
		break;
	case KOLABCONV_FB_TYPE_IFB:
		res_name = g_strconcat (username, ".ifb", NULL);
		break;
	case KOLABCONV_FB_TYPE_VFB:
		res_name = g_strconcat (username, ".vfb", NULL);
		break;
	case KOLABCONV_FB_TYPE_XFB:
		res_name = g_strconcat (username, ".xfb", NULL);
		break;
	default:
		g_error ("kolabconv_cal_util_freebusy_new_fb_url: invalid listtype");
	};

	url_string = g_strconcat (protocol, "://", servername, "/freebusy/", res_name, NULL);
	g_free (res_name);
	return url_string;
}

/**
 * kolabconv_cal_util_freebusy_ecalcomp_new_from_ics:
 * @ics_string: Ical string containing the trigger dummy response or
 *              freebusy information as sent by the Kolab server (for the format
 *	        check with the Kolab format specification)
 * @nbytes: The number of bytes the ics_string is supposed to contain
 *          (this info is obtained from the underlying tcp stream infrastructure)
 * @err: A GError, set in case of empty server response, missing VFREEBUSY
 *       component, or otherwise invalid input data
 *
 * Creates a new ECalComponent from an ICS string as sent by the Kolab
 * server as an answer to a trigger- or freebusy-request. No sanity checking
 * on the server response string (the ics_string) is done currently.
 *
 * Returns: A pointer to an ECalComponent structure containing the converted
 *          VFREEBUSY information. In case of an error, the return value is NULL.
 *          free the object using g_object_unref() if no longer needed
 */
ECalComponent*
kolabconv_cal_util_freebusy_ecalcomp_new_from_ics (const gchar *ics_string,
                                                   gint nbytes,
                                                   GError **err)
{
	ECalComponent *ecalcomp = NULL;
	ECalComponent *ecalcomp_tmp = NULL;
	icalcomponent *icalcomp_ics = NULL;
	icalcomponent *icalcomp_vfb = NULL;
	gboolean set_ical_ok = FALSE;

	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	/* TODO: sanity-check incoming data */

	if (nbytes > 0) {
		icalcomp_ics = (icalcomponent *) e_cal_util_parse_ics_string(ics_string);
	} else {
		g_debug ("kolabconv_cal_util_fb_new_ecalcomp_from_request: empty server response");
		g_set_error (err,
		             KOLABCONV_ERROR_READ_KOLAB,
		             KOLABCONV_ERROR_READ_KOLAB_INPUT_IS_NULL, /* appropriate? */
		             _("Empty server response"));
		goto CLEANUP;
	}

	if (icalcomp_ics != NULL) {
		icalcomp_vfb = icalcomponent_get_first_component (icalcomp_ics, ICAL_VFREEBUSY_COMPONENT);
	} else {
		g_debug ("kolabconv_cal_util_fb_new_ecalcomp_from_request: cannot parse server response");
		g_set_error (err,
		             KOLABCONV_ERROR_READ_KOLAB,
		             KOLABCONV_ERROR_READ_KOLAB_MALFORMED_XML, /* appropriate? */
		             _("Cannot parse server response"));
		goto CLEANUP;
	}

	if (icalcomp_vfb != NULL) {
		ecalcomp_tmp = e_cal_component_new ();
		set_ical_ok = e_cal_component_set_icalcomponent (ecalcomp_tmp, icalcomp_vfb);
		if (set_ical_ok) {
			/* need to copy ecalcomp_tmp since it will become
			 * invalid once icalcomp_[iv]fb are destroyed
			 */
			ecalcomp = e_cal_component_clone (ecalcomp_tmp);
		} else {
			g_debug ("kolabconv_cal_util_fb_new_ecalcomp_from_request: unsupported component type");
			g_set_error (err,
			             KOLABCONV_ERROR_READ_KOLAB,
			             KOLABCONV_ERROR_READ_KOLAB_INVALID_KOLAB_XML, /* appropriate? */
			             _("Unsupported component type"));
		}
		g_object_unref (ecalcomp_tmp);
		ecalcomp_tmp = NULL;
	} else {
		g_debug ("kolabconv_cal_util_fb_new_ecalcomp_from_request: missing component part");
		g_set_error (err,
		             KOLABCONV_ERROR_READ_KOLAB,
		             KOLABCONV_ERROR_READ_KOLAB_MISSING_XML_PART, /* appropriate? */
		             _("Unsupported component type"));
	}

 CLEANUP:
	if (icalcomp_vfb != NULL)
		icalcomponent_free (icalcomp_vfb);
	if (icalcomp_ics != NULL)
		icalcomponent_free (icalcomp_ics);

	return ecalcomp;
}




/**
 * Initialize this library.
 */
void
kolabconv_initialize (void)
{
	g_type_init ();
}

/**
 * Shutdown this library.
 */
void
kolabconv_shutdown (void)
{
	/* TODO: Check whether libxml needs to be shut down explicitly. (or any other libs) */

	/* free datastructure for ical builtin timezones */
	icaltimezone_free_builtin_timezones();
}

/**
 * kolabconv_kcontact_to_econtact:
 * @kmail: a Kolab_conv_mail structure. Container for kolab mail MIME parts.
 * @error: Placeholder for error information.
 *
 * Converts Kolab_conv_mail structure containing a kolab contact to internal
 * I_contact structure and then to an Evolution EContact GObject.
 *
 * Returns: EContact (GObject). Use kolabconv_free_econtact() to free memory
 * 			for EContact if no longer needed.
 */
EContact*
kolabconv_kcontact_to_econtact (const Kolab_conv_mail* kmail, GError** error)
{
	EContact *econtact = NULL;
	I_contact *icontact = NULL;

	log_kolab_mail("input data:", kmail);
	/* create icontact */
	icontact = conv_kolab_conv_mail_to_I_contact(kmail, error);
	/* create evolution contact if success */
	if (*error == NULL)
		econtact = conv_I_contact_to_EContact(&icontact, error);
	/* icontact is freed in the above operation already */
	log_evolution_vcard("output data:", econtact);
	return econtact;
}

/**
 * kolabconv_econtact_to_kcontact:
 * @econtact: an EContact (GObject). Contains an Evolution contact PIM-Object.
 * @error: Placeholder for error information.
 *
 * Converts Evolution EContact GObject to internal I_contact structure and
 * then to a Kolab_conv_mail structure containing a kolab contact.
 *
 * Returns: Kolab_conv_mail structure containing the kolab mail MIME parts.
 * 			Use kolabconv_free_kmail() to free memory for Kolab_conv_mail if
 * 			no longer needed.
 */
Kolab_conv_mail*
kolabconv_econtact_to_kcontact (const EContact* econtact, GError** error)
{
	Kolab_conv_mail *kmail = NULL;
	I_contact *icontact = NULL;

	log_evolution_vcard("input data:", econtact);
	/* create icontact */
	icontact = conv_EContact_to_I_contact(econtact, error);
	/* create kolab contact mail if success */
	if (*error == NULL)
		kmail = conv_I_contact_to_kolab_conv_mail(&icontact, error);
	/* icontact is freed in the above operation already */
	log_kolab_mail("output data:", kmail);
	return kmail;
}

/**
 * kolabconv_kevent_to_eevent:
 * @kmail: a Kolab_conv_mail structure. Container for kolab mail MIME parts.
 * @error: Placeholder for error information.
 *
 * Converts Kolab_conv_mail structure containing a kolab event to internal
 * I_event structure and then to an Evolution ECalComponentWithTZ structure.
 *
 * Returns: ECalComponentWithTZ structure. Use kolabconv_free_ecalendar()
 * 			to free memory if no longer needed.
 */
ECalComponentWithTZ*
kolabconv_kevent_to_eevent (const Kolab_conv_mail* kmail, GError** error)
{
	ECalComponentWithTZ *eevent = NULL;
	I_event *ievent = NULL;

	log_kolab_mail("input data:", kmail);
	/* create internal pim struct */
	ievent = conv_kolab_conv_mail_to_I_event(kmail, error);
	/* create evolution pim object if success */
	if (*error == NULL)
		eevent = conv_I_event_to_ECalComponentWithTZ(&ievent, error);
	/* internal pim struct is freed in the above operation already */
	log_evolution_ical("output data:", eevent);
	return eevent;
}

/**
 * kolabconv_eevent_to_kevent:
 * @ecalcomp: an ECalComponentWithTZ structure. Contains an Evolution event PIM-Object.
 * @error: Placeholder for error information.
 *
 * Converts Evolution ECalComponentWithTZ structure to internal I_event structure
 * and then to a Kolab_conv_mail structure containing a kolab event.
 *
 * Returns: Kolab_conv_mail structure containing the kolab mail MIME parts.
 * 			Use kolabconv_free_kmail() to free memory for Kolab_conv_mail if
 * 			no longer needed.
 */
Kolab_conv_mail*
kolabconv_eevent_to_kevent (const ECalComponentWithTZ* ecalcomp, GError** error)
{
	Kolab_conv_mail *kmail = NULL;
	I_event *ievent = NULL;

	log_evolution_ical("input data:", ecalcomp);
	/* create internal pim struct */
	ievent = conv_ECalComponentWithTZ_to_I_event(ecalcomp, error);
	/* create kolab mail if success */
	if (*error == NULL)
		kmail = conv_I_event_to_kolab_conv_mail(&ievent, error);
	/* internal pim struct is freed in the above operation already */
	log_kolab_mail("output data:", kmail);
	return kmail;
}

/**
 * kolabconv_ktask_to_etask:
 * @kmail: a Kolab_conv_mail structure. Container for kolab mail MIME parts.
 * @error: Placeholder for error information.
 *
 * Converts Kolab_conv_mail structure containing a kolab task to internal
 * I_task structure and then to an Evolution ECalComponentWithTZ structure.
 *
 * Returns: ECalComponentWithTZ structure. Use kolabconv_free_ecalendar()
 * 			to free memory if no longer needed.
 */
ECalComponentWithTZ*
kolabconv_ktask_to_etask (const Kolab_conv_mail* kmail, GError** error)
{
	ECalComponentWithTZ *etask = NULL;
	I_task *itask = NULL;

	log_kolab_mail("input data:", kmail);
	/* create internal pim struct */
	itask = conv_kolab_conv_mail_to_I_task(kmail, error);
	/* create evolution pim object if success */
	if (*error == NULL)
		etask = conv_I_task_to_ECalComponentWithTZ(&itask, error);
	/* internal pim struct is freed in the above operation already */
	log_evolution_ical("output data:", etask);
	return etask;
}

/**
 * kolabconv_etask_to_ktask:
 * @ecalcomp: an ECalComponentWithTZ structure. Contains an Evolution task PIM-Object.
 * @error: Placeholder for error information.
 *
 * Converts Evolution ECalComponentWithTZ structure to internal I_task structure
 * and then to a Kolab_conv_mail structure containing a kolab task.
 *
 * Returns: Kolab_conv_mail structure containing the kolab mail MIME parts.
 * 			Use kolabconv_free_kmail() to free memory for Kolab_conv_mail if
 * 			no longer needed.
 */
Kolab_conv_mail*
kolabconv_etask_to_ktask (const ECalComponentWithTZ* ecalcomp, GError** error)
{
	Kolab_conv_mail *kmail = NULL;
	I_task *itask = NULL;

	log_evolution_ical("input data:", ecalcomp);
	/* create internal pim struct */
	itask = conv_ECalComponentWithTZ_to_I_task(ecalcomp, error);
	/* create kolab mail if success */
	if (*error == NULL)
		kmail = conv_I_task_to_kolab_conv_mail(&itask, error);
	/* internal pim struct is freed in the above operation already */
	log_kolab_mail("output data:", kmail);
	return kmail;
}

/**
 * kolabconv_knote_to_enote:
 * @kmail: a Kolab_conv_mail structure. Container for kolab mail MIME parts.
 * @error: Placeholder for error information.
 *
 * Converts Kolab_conv_mail structure containing a kolab note to internal
 * I_note structure and then to an Evolution ECalComponentWithTZ GObject.
 *
 * Returns: ECalComponentWithTZ structure. Use kolabconv_free_ecalendar()
 * 			to free memory if no longer needed.
 */
ECalComponentWithTZ*
kolabconv_knote_to_enote (const Kolab_conv_mail* kmail, GError** error)
{
	ECalComponentWithTZ *enote = NULL;
	I_note *inote = NULL;

	log_kolab_mail("input data:", kmail);
	/* create internal pim struct */
	inote = conv_kolab_conv_mail_to_I_note(kmail, error);
	/* create evolution pim object if success */
	if (*error == NULL)
		enote = conv_I_note_to_ECalComponentWithTZ(&inote, error);
	/* internal pim struct is freed in the above operation already */
	log_evolution_ical("output data:", enote);
	return enote;
}

/**
 * kolabconv_enote_to_knote:
 * @ecalcomp: an ECalComponentWithTZ structure. Contains an Evolution note PIM-Object.
 * @error: Placeholder for error information.
 *
 * Converts Evolution ECalComponentWithTZ structure to internal I_note structure
 * and then to a Kolab_conv_mail structure containing a kolab note.
 *
 * Returns: Kolab_conv_mail structure containing the kolab mail MIME parts.
 * 			Use kolabconv_free_kmail() to free memory for Kolab_conv_mail if
 * 			no longer needed.
 */
Kolab_conv_mail*
kolabconv_enote_to_knote (const ECalComponentWithTZ* ecalcomp, GError** error)
{
	Kolab_conv_mail *kmail = NULL;
	I_note *inote = NULL;

	log_evolution_ical("input data:", ecalcomp);
	/* create internal pim struct */
	inote = conv_ECalComponentWithTZ_to_I_note(ecalcomp, error);
	/* create kolab mail if success */
	if (*error == NULL)
		kmail = conv_I_note_to_kolab_conv_mail(&inote, error);
	/* internal pim struct is freed in the above operation already */
	log_kolab_mail("output data:", kmail);
	return kmail;
}



/**
 * free_ecalcomp:
 * @comp: ECalComponent to free.
 *
 * free ECalComponent.
 */
static void
free_ecalcomp(ECalComponent *comp)
{
	if (comp != NULL)
		g_object_unref (comp);
}

/**
 * kolabconv_free_econtact:
 * @econtact: EContact to free.
 *
 * free EContact.
 */
void
kolabconv_free_econtact(EContact *econtact)
{
	if (econtact != NULL)
		g_object_unref (econtact);
}

/**
 * kolabconv_free_ecalendar:
 * @comp: ECalComponentWithTZ to free.
 *
 * free ECalComponentWithTZ.
 */
void
kolabconv_free_ecalendar(ECalComponentWithTZ *comp)
{
	if (comp != NULL) {
		free_ecalcomp(comp->maincomp);
		free_ecalcomp(comp->timezone);
		g_free(comp);
	}
}

static void
free_kmail_part(void **mailpart)
{
	if (mailpart && *mailpart) {
		Kolab_conv_mail_part *mp = (Kolab_conv_mail_part *)*mailpart;
		if (mp->name != NULL)
			g_free(mp->name);
		if (mp->mime_type != NULL)
			g_free(mp->mime_type);
		if (mp->data != NULL)
			g_free(mp->data);
	}
}

/**
 * kolabconv_free_kmail:
 * @kmail: Kolab_conv_mail to free.
 *
 * free Kolab_conv_mail.
 */
void
kolabconv_free_kmail(Kolab_conv_mail *kmail)
{
	if (kmail->mail_parts != NULL) {
		guint i;
		for (i = 0; i < kmail->length; i++) {
			Kolab_conv_mail_part *part = kmail->mail_parts+i;
			free_kmail_part((void **) &part);
		}
	}
	g_free(kmail->mail_parts);
	g_free(kmail);
}

/**
 * kolabconv_free_kmail_part:
 * @mailpart: Kolab_conv_mail_part to free.
 *
 * free Kolab_conv_mail_part.
 */
/* void kolabconv_free_kmail_part(Kolab_conv_mail_part **mailpart) */
void kolabconv_free_kmail_part(void **mailpart)
{
	if (mailpart && *mailpart) {
		free_kmail_part(mailpart);
		g_free (*mailpart);
		mailpart = NULL;
	}
}
