/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/***************************************************************************
 *            camel-kolab-imapx-metadata.c
 *
 *  Tue Oct 19 18:58:03 2010
 *  Copyright  2010  Christian Hilberg
 *  <hilberg@kernelconcepts.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 main.c; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */

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

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <glib/gi18n-lib.h>

#include <libekolabutil/kolab-util-error.h>

#include "camel-kolab-imapx-folder.h"
#include "camel-kolab-imapx-metadata.h"

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

static CamelKolabImapxFolderMetadata*
camel_kolab_imapx_metadata_folder_metadata_new_from_imapx (CamelImapxMetadataAnnotation *man,
                                                           camel_imapx_metadata_proto_t proto,
                                                           GError **err)
{
	CamelKolabImapxFolderMetadata *kfmd = NULL;
	CamelImapxMetadataAttrib *ma = NULL;
	CamelImapxMetadataSpec *spec = NULL;
	gchar *typestring = NULL;
	GError *tmp_err = NULL;

	/* man may be NULL */
	g_assert ((proto > CAMEL_IMAPX_METADATA_PROTO_INVAL) &&
	          (proto < CAMEL_IMAPX_METADATA_LAST_PROTO));
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	if (man == NULL)
		return NULL;

	g_assert (man->entries != NULL);

	/* search for folder type info */
	spec = camel_imapx_metadata_spec_new (proto, /* IMAP metadata protocol type */
	                                      NULL, /* search starts at annotation */
	                                      "/vendor/kolab/folder-type",
	                                      NULL, /* "value" will be substituted */
	                                      &tmp_err);
	if (spec == NULL) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	kfmd = camel_kolab_imapx_folder_metadata_new ();

	ma = camel_imapx_metadata_get_attrib_from_annotation (man, spec);
	camel_imapx_metadata_spec_free (spec);

	if (ma == NULL) {
		/* no folder type info available */
		kfmd->folder_type = KOLAB_FOLDER_TYPE_UNKNOWN;
		return kfmd;
	}

	g_assert (ma->data != NULL);
	g_assert (ma->type != NULL);

	if (ma->type[CAMEL_IMAPX_METADATA_ACCESS_SHARED] != CAMEL_IMAPX_METADATA_ATTRIB_TYPE_UTF8) {
		/* protocol violation - kolab folder type
		 * string must be an UTF-8 shared value
		 */
		camel_kolab_imapx_folder_metadata_free (kfmd);
		g_set_error (err,
		             KOLAB_CAMEL_KOLAB_ERROR,
		             KOLAB_CAMEL_KOLAB_ERROR_FORMAT,
		             _("Invalid Kolab folder type string encoding"));
		return NULL;
	}

	typestring = (gchar*) ma->data[CAMEL_IMAPX_METADATA_ACCESS_SHARED]->data;
	kfmd->folder_type = kolab_util_folder_type_get_id (typestring);
	if (kfmd->folder_type == KOLAB_FOLDER_TYPE_INVAL) {
		/* protocol violation - invalid kolab folder type string */
		camel_kolab_imapx_folder_metadata_free (kfmd);
		g_set_error (err,
		             KOLAB_CAMEL_KOLAB_ERROR,
		             KOLAB_CAMEL_KOLAB_ERROR_TYPE,
		             _("Invalid Kolab folder type string"));
		return NULL;
	}

	return kfmd;
}

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

CamelKolabImapxMetadata*
camel_kolab_imapx_metadata_new (void)
{
	CamelKolabImapxMetadata *kmd = g_new0 (CamelKolabImapxMetadata, 1);
	kmd->mdb = NULL;
	kmd->kolab_metadata = g_hash_table_new_full (g_str_hash,
	                                             g_str_equal,
	                                             g_free,
	                                             camel_kolab_imapx_folder_metadata_gdestroy);

	return kmd;
}

void
camel_kolab_imapx_metadata_free (CamelKolabImapxMetadata *kmd)
{
	if (kmd == NULL)
		return;

	if (kmd->kolab_metadata)
		/* need hash table with key:val destroy function set */
		g_hash_table_destroy (kmd->kolab_metadata);

	g_free (kmd);
}

gboolean
camel_kolab_imapx_metadata_init (CamelKolabImapxMetadata *kmd,
                                 const gchar *cachepath,
                                 GError **err)
{
	gboolean db_ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (kmd != NULL);
	g_assert (cachepath != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	if (kmd->mdb == NULL)
		kmd->mdb = camel_kolab_imapx_metadata_db_new ();

	db_ok = camel_kolab_imapx_metadata_db_open (kmd->mdb,
	                                            cachepath,
	                                            &tmp_err);
	if (!db_ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	db_ok = camel_kolab_imapx_metadata_db_init (kmd->mdb,
	                                            &tmp_err);
	if (!db_ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	kolab_util_folder_init ();

	return TRUE;
}

gboolean
camel_kolab_imapx_metadata_shutdown (CamelKolabImapxMetadata *kmd,
                                     GError **err)
{
	GError *tmp_err = NULL;
	gboolean db_ok = FALSE;

	g_assert (kmd != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	db_ok = camel_kolab_imapx_metadata_db_close (kmd->mdb,
	                                             &tmp_err);
	if (!db_ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	kolab_util_folder_shutdown ();

	return TRUE;
}

gboolean
camel_kolab_imapx_metadata_remove (CamelKolabImapxMetadata *kmd,
                                   const gchar *foldername,
                                   GError **err)
{
	gboolean db_ok = FALSE;
	gboolean mem_ok = FALSE;
	GError *tmp_err = NULL;

	/* remove from local cache only, let the imap daemon
	 * take care of the server side.
	 * Handle removal of non-existent folder names gracefully
	 * (i.e. report I/O errors only, return okay if folder name
	 * does not exist in sqlite/in-mem db)
	 */

	g_assert (kmd != NULL);
	g_assert (kmd->mdb != NULL);
	g_assert (kmd->kolab_metadata != NULL);
	g_assert (foldername != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	/* remove from metadata db */
	db_ok = camel_kolab_imapx_metadata_db_remove_folder (kmd->mdb,
	                                                     foldername,
	                                                     &tmp_err);
	if (! db_ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	/* remove from in-mem */
	/* need hash table with key:value destroy functions set */
	mem_ok = g_hash_table_remove (kmd->kolab_metadata,
	                              foldername);
	if (! mem_ok)
		g_warning ("%s: [%s] not in mem cache",
		           __func__, foldername);

	return TRUE;
}

void
camel_kolab_imapx_metadata_update (CamelKolabImapxMetadata *kmd,
                                   CamelImapxMetadata *md,
                                   camel_imapx_metadata_proto_t proto)
{
	/* TODO better error reporting */

	CamelImapxMetadataAnnotation *man = NULL;
	CamelKolabImapxFolderMetadata *kfmd = NULL;
	GHashTableIter mbox_iter;
	gpointer mbox_key = NULL;
	gpointer mbox_value = NULL;
	gchar *foldername = NULL;
	GError *tmp_err = NULL;

	g_assert (kmd != NULL);
	g_assert (md != NULL);
	g_assert ((proto > CAMEL_IMAPX_METADATA_PROTO_INVAL) &&
	          (proto < CAMEL_IMAPX_METADATA_LAST_PROTO));

	g_hash_table_iter_init (&mbox_iter, md->mboxes);
	while (g_hash_table_iter_next (&mbox_iter, &mbox_key, &mbox_value)) {
		foldername = (gchar*) mbox_key;
		man = (CamelImapxMetadataAnnotation*) mbox_value;

		kfmd = camel_kolab_imapx_metadata_folder_metadata_new_from_imapx \
			(man,
			 proto,
			 &tmp_err);

		if (kfmd == NULL) {
			g_warning ("%s: kolab annotation error for [%s]: %s",
			           __func__, foldername, tmp_err->message);
			g_clear_error (&tmp_err);
			continue;
		}
		/* need hash table with key:value destroy functions set */
		g_hash_table_replace (kmd->kolab_metadata,
		                      g_strdup (foldername),
		                      kfmd);
	}
}

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