/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/***************************************************************************
 *            camel-imapx-extd-folder.c
 *
 *  2011-11-25, 21:02:28
 *  Copyright 2011, Christian Hilberg
 *  <hilberg@unix-ag.org>
 ****************************************************************************/

/*
 * 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 "camel-imapx-summary.h"
#include "camel-imapx-extd-folder.h"
#include "camel-imapx-folder-defs.h"
#include "camel-imapx-folder-friend.h"

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

static CamelFolderClass *parent_folder_class = NULL;

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

G_DEFINE_TYPE (CamelIMAPXExtdFolder, camel_imapx_extd_folder, CAMEL_TYPE_IMAPX_FOLDER)

/*----------------------------------------------------------------------------*/
/* object init */

static void
camel_imapx_extd_folder_init (CamelIMAPXExtdFolder *self)
{
	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
}

static void
camel_imapx_extd_folder_dispose (GObject *object)
{
	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (object));

	G_OBJECT_CLASS (camel_imapx_extd_folder_parent_class)->dispose (object);
}

static void
camel_imapx_extd_folder_finalize (GObject *object)
{
	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (object));

	G_OBJECT_CLASS (camel_imapx_extd_folder_parent_class)->finalize (object);
}

/*----------------------------------------------------------------------------*/
/* class functions */

static GPtrArray*
imapx_extd_folder_search_by_expression (CamelFolder *self,
                                        const gchar *expression,
                                        GError **err)
{
	GPtrArray *result = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (expression != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	result = parent_folder_class->search_by_expression (self,
	                                                    expression,
	                                                    err);
	return result;
}

static GPtrArray*
imapx_extd_folder_search_by_uids (CamelFolder *self,
                                  const gchar *expression,
                                  GPtrArray *uids,
                                  GError **err)
{
	GPtrArray *result = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (expression != NULL);
	g_assert (uids != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	result = parent_folder_class->search_by_uids (self,
	                                              expression,
	                                              uids,
	                                              err);
	return result;
}

static guint32
imapx_extd_folder_count_by_expression (CamelFolder *self,
                                       const gchar *expression,
                                       GError **err)
{
	guint32 count = 0;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (expression != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, 0);

	count = parent_folder_class->count_by_expression (self,
	                                                  expression,
	                                                  err);
	return count;
}

static void
imapx_extd_folder_search_free (CamelFolder *self,
                               GPtrArray *result)
{
	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (result != NULL);

	parent_folder_class->search_free (self, result);
}

static gchar*
imapx_extd_folder_get_filename (CamelFolder *self,
                                const gchar *uid,
                                GError **err)
{
	gchar *filename = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (uid != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	filename = parent_folder_class->get_filename (self,
	                                              uid,
	                                              err);
	return filename;
}

static gboolean
imapx_extd_folder_append_message_sync (CamelFolder *self,
                                       CamelMimeMessage *message,
                                       CamelMessageInfo *finfo,
                                       gchar **appended_uid,
                                       GCancellable *cancellable,
                                       GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (CAMEL_IS_MIME_MESSAGE (message));
	/* info may be NULL */ /* FIXME correct? */
	/* FIXME what can we assert for **appended_uid? */
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_folder_class->append_message_sync (self,
	                                               message,
	                                               finfo,
	                                               appended_uid,
	                                               cancellable,
	                                               err);
	return ok;
}

static gboolean
imapx_extd_folder_expunge_sync (CamelFolder *self,
                                GCancellable *cancellable,
                                GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_folder_class->expunge_sync (self,
	                                        cancellable,
	                                        err);
	return ok;
}

static CamelMimeMessage*
imapx_extd_folder_get_message_sync (CamelFolder *self,
                                    const gchar *message_uid,
                                    GCancellable *cancellable,
                                    GError **err)
{
	CamelMimeMessage *msg = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (message_uid != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	msg = parent_folder_class->get_message_sync (self,
	                                             message_uid,
	                                             cancellable,
	                                             err);
	return msg;
}

static gboolean
imapx_extd_folder_refresh_info_sync (CamelFolder *self,
                                     GCancellable *cancellable,
                                     GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_folder_class->refresh_info_sync (self,
	                                             cancellable,
	                                             err);
	return ok;
}

static gboolean
imapx_extd_folder_synchronize_sync (CamelFolder *self,
                                    gboolean expunge,
                                    GCancellable *cancellable,
                                    GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_folder_class->synchronize_sync (self,
	                                            expunge,
	                                            cancellable,
	                                            err);
	return ok;
}

static gboolean
imapx_extd_folder_synchronize_message_sync (CamelFolder *self,
                                            const gchar *message_uid,
                                            GCancellable *cancellable,
                                            GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_assert (message_uid != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_folder_class->synchronize_message_sync (self,
	                                                    message_uid,
	                                                    cancellable,
	                                                    err);
	return ok;
}

static gboolean
imapx_extd_folder_transfer_messages_to_sync (CamelFolder *source,
                                             GPtrArray *message_uids,
                                             CamelFolder *destination,
                                             gboolean delete_originals,
                                             GPtrArray **transferred_uids,
                                             GCancellable *cancellable,
                                             GError **err)
{
	gboolean ok = FALSE;

	/* TODO is CamelFolder always CamelIMAPXExtdFolder
	 *      here, so we can assert that? looks as though
	 *      this one is more of a utility function than
	 *      a CamelIMAPXFolder class function...
	 */

	g_assert (CAMEL_IS_FOLDER (source));
	g_assert (message_uids != NULL);
	g_assert (CAMEL_IS_FOLDER (destination));
	g_assert (transferred_uids == NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_folder_class->transfer_messages_to_sync (source,
	                                                     message_uids,
	                                                     destination,
	                                                     delete_originals,
	                                                     transferred_uids,
	                                                     cancellable,
	                                                     err);
	return ok;
}

static guint64
imapx_extd_folder_get_uidvalidity (CamelIMAPXExtdFolder *self,
                                   GError **err)
{
	CamelIMAPXFolder *ifolder = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (self));
	g_return_val_if_fail (err == NULL || *err == NULL, 0);

	ifolder = CAMEL_IMAPX_FOLDER (self);

	return ifolder->uidvalidity_on_server;
}
/*----------------------------------------------------------------------------*/
/* class init */

static void
camel_imapx_extd_folder_class_init (CamelIMAPXExtdFolderClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	GObjectClass *parent_object_class = NULL;
	CamelFolderClass *folder_class = CAMEL_FOLDER_CLASS (klass);

	parent_object_class = G_OBJECT_CLASS (camel_imapx_extd_folder_parent_class);
	parent_folder_class = CAMEL_FOLDER_CLASS (camel_imapx_extd_folder_parent_class);

	/* g_type_class_add_private (klass, sizeof (CamelIMAPXExtdFolderPrivate)); */

	object_class->dispose = camel_imapx_extd_folder_dispose;
	object_class->finalize = camel_imapx_extd_folder_finalize;
	object_class->set_property = parent_object_class->set_property;
	object_class->get_property = parent_object_class->get_property;

	folder_class->search_by_expression = imapx_extd_folder_search_by_expression;
	folder_class->search_by_uids = imapx_extd_folder_search_by_uids;
	folder_class->count_by_expression = imapx_extd_folder_count_by_expression;
	folder_class->search_free = imapx_extd_folder_search_free;
	folder_class->get_filename = imapx_extd_folder_get_filename;
	folder_class->append_message_sync = imapx_extd_folder_append_message_sync;
	folder_class->expunge_sync = imapx_extd_folder_expunge_sync;
	folder_class->get_message_sync = imapx_extd_folder_get_message_sync;
	folder_class->refresh_info_sync = imapx_extd_folder_refresh_info_sync;
	folder_class->synchronize_sync = imapx_extd_folder_synchronize_sync;
	folder_class->synchronize_message_sync = imapx_extd_folder_synchronize_message_sync;
	folder_class->transfer_messages_to_sync = imapx_extd_folder_transfer_messages_to_sync;

	klass->get_uidvalidity = imapx_extd_folder_get_uidvalidity;

	/* dupe from parent (do we need this, really, if we do not add properties?) */
	g_object_class_install_property (object_class,
	                                 PROP_APPLY_FILTERS,
	                                 g_param_spec_boolean ("apply-filters",
	                                                       "Apply Filters",
	                                                       _("Apply message _filters to this folder"),
	                                                       FALSE,
	                                                       G_PARAM_READWRITE |
	                                                       CAMEL_PARAM_PERSISTENT));
}

/*----------------------------------------------------------------------------*/
/* API functions */

CamelIMAPXExtdFolder*
camel_imapx_extd_folder_new (CamelIMAPXExtdStore *store,
                             const gchar *folder_dir,
                             const gchar *folder_name,
                             GError **err)
{
	/* This function is a modified dupe of camel_imapx_folder_new() in
	 * CamelIMAPXFolder.
	 * We need to dupe it in order to return a CamelIMAPXExtdFolder
	 * Upstream fixes need to be applied here, too.
	 */

	CamelFolder *folder = NULL;
	CamelIMAPXFolder *ifolder = NULL;
	CamelIMAPXExtdFolder *efolder = NULL;
	CamelService *service = NULL;
	CamelSettings *settings = NULL;
	const gchar *short_name = NULL;
	gchar *state_file = NULL;
	gboolean filter_all = FALSE;
	gboolean filter_inbox = FALSE;
	gboolean filter_junk = FALSE;
	gboolean filter_junk_inbox = FALSE;

	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (store));
	g_assert (folder_dir != NULL);
	g_assert (folder_name != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	g_debug ("%s: opening imap folder '%s'\n",
	         __func__, folder_dir);

	service = CAMEL_SERVICE (store);
	settings = camel_service_get_settings (service);

	g_object_get (settings,
	              "filter-all", &filter_all,
	              "filter-inbox", &filter_inbox,
	              "filter-junk", &filter_junk,
	              "filter-junk-inbox", &filter_junk_inbox,
	              NULL);

	short_name = strrchr (folder_name, '/');
	if (short_name)
		short_name++;
	else
		short_name = folder_name;

	efolder = g_object_new (CAMEL_TYPE_IMAPX_EXTD_FOLDER,
	                        "display-name", short_name,
	                        "full_name", folder_name,
	                        "parent-store", store, NULL);
	ifolder = CAMEL_IMAPX_FOLDER (efolder);
	folder  = CAMEL_FOLDER (efolder);

	ifolder->raw_name = g_strdup (folder_name);

	folder->summary = camel_imapx_summary_new (folder);
	if (!folder->summary) {
		g_set_error (err,
		             CAMEL_ERROR, CAMEL_ERROR_GENERIC,
		             _("Could not create folder summary for %s"),
		             short_name);
		return NULL;
	}

	ifolder->cache = camel_data_cache_new (folder_dir, err);
	if (!ifolder->cache) {
		g_prefix_error (err,
		                _("Could not create cache for %s"),
		                short_name);
		return NULL;
	}

	state_file = g_build_filename (folder_dir, "cmeta", NULL);
	camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
	g_free (state_file);
	camel_object_state_read (CAMEL_OBJECT (folder));

	ifolder->search = camel_folder_search_new ();
	ifolder->search_lock = g_mutex_new ();
	ifolder->stream_lock = g_mutex_new ();
	ifolder->ignore_recent = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
	ifolder->exists_on_server = 0;
	ifolder->unread_on_server = 0;
	ifolder->modseq_on_server = 0;
	ifolder->uidnext_on_server = 0;

	if (!g_ascii_strcasecmp (folder_name, "INBOX")) {
		if (filter_inbox || filter_all)
			folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
		if (filter_junk)
			folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
	} else {
		if (filter_junk && !filter_junk_inbox)
			folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;

		if (filter_all || camel_imapx_folder_get_apply_filters (ifolder))
			folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
	}

	camel_store_summary_connect_folder_summary (CAMEL_STORE_SUMMARY (CAMEL_IMAPX_STORE (store)->summary),
	                                            folder_name, folder->summary);

	return efolder;
}

guint64
camel_imapx_extd_folder_get_uidvalidity (CamelIMAPXExtdFolder *self,
                                         GError **err)
{
	guint64 uidvalidity = 0;
	CamelIMAPXExtdFolderClass *klass = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_EXTD_FOLDER (self), 0);

	klass = CAMEL_IMAPX_EXTD_FOLDER_GET_CLASS (self);
	uidvalidity = klass->get_uidvalidity (self, err);

	return uidvalidity;
}

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