/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
 * common.c
 *
 *  Created on: 2010-08-18
 *      Author: Umer Kayani <u.kayani@tarent.de>,
 *				Peter Neuhaus <p.neuhaus@tarent.de>,
 *				Hendrik Helwich <h.helwich@tarent.de>,
 *				Andreas Grau <a.grau@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 "common.h"
#include "../util.h"
#include "../logging.h"

/**
 * initialize I_common
 */
I_common*
new_i_common (void)
{
	return (I_common*) g_new0(I_common, 1);
}

/**
 * Initialize Date_or_datetime
 */
Date_or_datetime*
new_date_or_datetime()
{
	Date_or_datetime* date_or_datetime = g_new0(Date_or_datetime, 1);
	date_or_datetime->date = NULL;
	date_or_datetime->date_time = NULL;

	return date_or_datetime;
}

/**
 * Adds an xml tree which is serialized in a string to the kolab store.
 *
 * @param  common
 *         struct which holds the kolab store which is used to store the data
 * @param  parent_ptr
 *         a pointer to the root element which logically holds the given xml
 *         element. This must be a pointer which is also available by the
 *         given common struct.
 *         If the xml element is located below the root xml element, this
 *         parameter should be a pointer to the common struct itself.
 * @param  xml_str
 *         an xml string which could not be mapped to the evolution class but
 *         which should be preserved
 * @param  reverse_order
 *         insert at begin of list if this is true; default: false.
 */
void
kolab_store_add_element(I_common *i_common, gpointer parent_ptr, gchar *xml_str, gboolean reverse_order)
{
	GList *list = NULL;

	log_debug("add to kolab store: %d, %s", parent_ptr, xml_str);
	if (i_common->kolab_store == NULL) { /* initialize kolab store */
		/* g_direct_hash() : key pointers are used directly as hash value */
		/* keys/values are not freed automatically when replaced or removed */
		i_common->kolab_store = g_hash_table_new(g_direct_hash, g_direct_equal);
	}
	list = g_hash_table_lookup(i_common->kolab_store, parent_ptr);
	if (reverse_order)
		list = g_list_prepend(list, (gpointer)xml_str);
	else
		list = g_list_append(list, (gpointer)xml_str);
	g_hash_table_insert(i_common->kolab_store, parent_ptr, list);
}

/**
 * Gets a list of xml trees which are serialized in a string from the kolab
 * store and which are referenced by the given parent element pointer.
 *
 * @param  common
 *         struct which holds the kolab store which is used to retrieve the data
 * @param  parent_ptr
 *         a pointer to the root element which logically holds the given xml
 *         element. This must be a pointer which is also available by the
 *         given common struct.
 *         If the xml element is located below the root xml element, this
 *         parameter should be a pointer to the common struct itself.
 * @return a list which holds xml strings which are related to the given parent
 *         element pointer
 */
GList*
kolab_store_get_element_list(I_common *i_common, gpointer parent_ptr)
{
	if (i_common == NULL || i_common->kolab_store == NULL)
		return NULL;
	return g_hash_table_lookup(i_common->kolab_store, parent_ptr);
}


/**
 * frees GList *and* all elements stored in GList. GList will be
 * set to NULL.
 *
 * generic_g_list_free() iterates over all elements in GList l and
 * frees memory of each element by calling an element-specific
 * free function. after elements have been freed, the GList itself
 * gets freed by calling g_list_free()
 *
 * @param l
 *			pointer to GList with data elements
 * @param free_func
 *			function pointer to element-specific free function.
 *			free_func takes a **void as argument (has to be casted
 *			to a pointer to the type of element in free_func) and
 *			has return type	void.
 *
 */
void
generic_g_list_free(GList **l, void (*free_func) (void **data))
{
	if (l && *l) {
		GList *i = *l;				/* copy of *l for iteration */

		do {
			free_func(&i->data);		/* free data elements in list */
		} while ((i = g_list_next(i)));

		g_list_free(*l);			/* free list nodes */
		*l = NULL;				/* avoid dangling pointers */
	}
}

/**
 * frees GSList *and* all elements stored in GSList. GSList will be
 * set to NULL.
 *
 * generic_g_slist_free() iterates over all elements in GSList l and
 * frees memory of each element by calling an element-specific
 * free function. after elements have been freed, the GSList itself
 * gets freed by calling g_slist_free()
 *
 * @param l
 *			pointer to GSList with data elements
 * @param free_func
 *			function pointer to element-specific free function.
 *			free_func takes a **void as argument (has to be casted
 *			to a pointer to the type of element in free_func) and
 *			has return type	void.
 *
 */
void
generic_g_slist_free(GSList **l, void (*free_func) (void **data))
{
	if (l && *l) {
		GSList *i = *l;			/* copy of *l for iteration */

		do {
			free_func(&i->data);	/* free data elements in list */
		} while ((i = g_slist_next(i)));

		g_slist_free(*l);		/* free list nodes */
		*l = NULL;			/* avoid dangling pointers */
	}
}



/**
 * free memory used by primitive datatypes (e.g char, int...).
 * reminder: memory has to be allocated by g_new(), g_new0() or function
 * that internally uses them...
 *
 * takes a pointer to a pointer to primitive datatype as argument
 */
void
free_primitive_data (void **data)
{
	/* data doesn't need to be casted */
	if (data && *data) {
		g_free(*data);
		*data = NULL;
	}
}

/**
 * specific free function for kolab store elements
 */
static gboolean
free_kolab_store_elements(gpointer key, gpointer value, gpointer data)
{
	/* keys will be freed somewhere else */
	(void)key;
	(void)data;

	generic_g_list_free ((GList**) &value, free_primitive_data);
	return TRUE;
}

/**
 * specific free function for kolab store
 */
static void
free_kolab_store(GHashTable **hash)
{
	if (hash && *hash) {
		g_hash_table_foreach_remove(*hash, free_kolab_store_elements, NULL);
		g_hash_table_unref(*hash);
		*hash = NULL;
	}
}

/**
 * specific free function for I_common objects
 */
void
free_i_common (I_common **i_common)
{
	if (i_common && *i_common) {
		I_common *ic = *i_common; /* local copy for readability */
		G_STRING_FREE (ic->product_id);
		G_STRING_FREE (ic->uid);
		G_STRING_FREE (ic->body);
		G_STRING_FREE (ic->categories);
		free_date_or_datetime (&ic->creation_datetime);
		free_date_or_datetime (&ic->last_modified_datetime);
		/* g_free(ic->vtimezone); */
		/* FIXME timezone leak fixes in libical0.48 make
		 *       this one to lead to mem corruption when
		 *       storing an object (did not happen in 0.44).
		 *
		 *       We will need to wait for upstream libical
		 *       fixes for the memory corruption which
		 *       are being worked on for an updated 0.48,
		 *       then return here to see whether we can do
		 *       better than just not freeing the timezone
		 */

		g_list_free(ic->kolab_attachment_store); /* no need to free content (must be freed outside of the library) */
		ic->kolab_attachment_store = NULL;

		generic_g_list_free (&ic->link_attachments, free_primitive_data);

		generic_g_list_free (&ic->inline_attachment_names, free_primitive_data);

		free_primitive_data(&ic->evolution_store);
		free_kolab_store(&ic->kolab_store);

		g_free(ic);
		*i_common = NULL;
	}
}

/**
 * specific free function for Date_or_datetime objects
 */
void free_date_or_datetime (Date_or_datetime **dt)
{
	if (dt && *dt) {
		G_DATE_FREE ((*dt)->date);
		if ((*dt)->date_time != NULL)
			g_free((*dt)->date_time);
		g_free(*dt);
		*dt = NULL;
	}
}

/**
 * specific free function for Evolution_field objects
 */
void free_evolution_field(Evolution_field **e_field)
{
	if (e_field && *e_field) {
		G_STRING_FREE ((*e_field)->name);
		G_STRING_FREE ((*e_field)->value);

		g_free(*e_field);
		*e_field = NULL;
	}
}

/**
 * specific free function for GString
 */
void
free_g_string (void **data)
{
	if (data && *data) {
		G_STRING_FREE (*data);
		*data = NULL;
	}
}


/*
 * print function for debugging
 */
void
print_date_or_datetime (const Date_or_datetime *dodt, const gchar *caption)
{

	(void)caption;

	if (dodt) {
		if (dodt->date)
			/* log_debug("%s", convertGDateToString( dateOrDateTime->date)); */
			log_debug("\n\t Date: %d-%d-%d", (dodt->date)->year,
			          (dodt->date)->month, (dodt->date)->day);
		else if (dodt->date_time) {
			log_debug ("\n%s", caption);
			log_debug ("\n\t Time: %s", ctime (dodt->date_time));
		} else
			log_debug ("NULL");
	}
}

/*
 * print function for debugging
 */
void
print_i_common (const I_common *common)
{
	if (common->product_id != NULL)
		log_debug ("\nProduct_id: %s", common->product_id->str);
	if (common->uid != NULL)
		log_debug ("\nUID: %s", ((common)->uid)->str);
	if (common->body != NULL)
		log_debug ("\nBody: %s", (common->body)->str);
	if (common->categories != NULL)
		log_debug ("\nCategories: %s", (common->categories)->str);
	if (common->creation_datetime != NULL)
		/* log_debug("\nCreationDate: %d-%d-%d", (common->creation_datetime->date)->year, (common->creation_datetime->date)->month, (common->creation_datetime->date)->day); */
		print_date_or_datetime (common->creation_datetime, "CreationDate");
	if (common->last_modified_datetime != NULL) {
		print_date_or_datetime (common->last_modified_datetime, "last_modified_datetime");
	}
	/* if (common->sensitivity) */
	log_debug ("\nSensitivity: %d", common->sensitivity);

	/* print_kolab_store (common); */
	print_kolab_attachment_store (common);
	print_evolution_store (common);
	/* print_extended_evolution_store (common); */

}

/*
 * print function for debugging
 */
void
print_kolab_attachment_store (const I_common *c)
{
#ifndef KOLABCONV_DEBUG
	(void)c;
#else
	GList* list = NULL;
	list = g_list_first (c->kolab_attachment_store);
	int i = 0;

	log_debug ("\n\nKolabAttachmentStore:");
	if (list == NULL)
		log_debug (" NULL");
	else
		while (list != NULL) {
			Kolab_conv_mail_part *bin_attachment = (Kolab_conv_mail_part *) list->data;

			log_debug ("\nItem# %d: \n\tFileName: %s, FileLength: %d", (++i),
			           bin_attachment->name, bin_attachment->length);
			list = list->next;
		}
#endif
}


/*
 * print function for debugging
 */
void
print_evolution_store (const I_common *c)
{
#ifndef KOLABCONV_DEBUG
	(void)c;
#else
	GList* list = NULL;
	list = g_list_first (c->evolution_store);
	gint i = 0;

	log_debug ("\n\nEvolution_store:");
	if (list == NULL)
		log_debug (" NULL");
	else
		while (list != NULL) {

			Evolution_field *eField = ((Evolution_field *) list->data);

			log_debug ("\nitem# %d: \t FieldName = %s, value = %s", (++i),
			           eField->name->str, eField->value->str);
			list = list->next;
		}
#endif
}
