/*
 * e-mail-folder-create-dialog.c
 *
 * 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.
 *
 * 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 Lesser General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "e-mail-folder-create-dialog.h"

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

#include "em-vfolder-editor-rule.h"
#include "mail-vfolder-ui.h"

#define E_MAIL_FOLDER_CREATE_DIALOG_GET_PRIVATE(obj) \
	(G_TYPE_INSTANCE_GET_PRIVATE \
	((obj), E_TYPE_MAIL_FOLDER_CREATE_DIALOG, EMailFolderCreateDialogPrivate))

typedef struct _AsyncContext AsyncContext;

struct _EMailFolderCreateDialogPrivate {
	EMailUISession *session;
	GtkWidget *name_entry;
};

struct _AsyncContext {
	EMailFolderCreateDialog *dialog;
	EActivity *activity;
};

enum {
	PROP_0,
	PROP_SESSION
};

enum {
	FOLDER_CREATED,
	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

G_DEFINE_TYPE (
	EMailFolderCreateDialog,
	e_mail_folder_create_dialog,
	EM_TYPE_FOLDER_SELECTOR)

static void
async_context_free (AsyncContext *async_context)
{
	g_clear_object (&async_context->dialog);
	g_clear_object (&async_context->activity);

	g_slice_free (AsyncContext, async_context);
}

static void
mail_folder_create_dialog_create_folder_cb (GObject *source_object,
                                            GAsyncResult *result,
                                            gpointer user_data)
{
	EMailFolderCreateDialog *dialog;
	EActivity *activity;
	EAlertSink *alert_sink;
	GdkWindow *gdk_window;
	AsyncContext *async_context;
	GError *local_error = NULL;

	async_context = (AsyncContext *) user_data;

	dialog = async_context->dialog;
	activity = async_context->activity;
	alert_sink = e_activity_get_alert_sink (activity);

	/* Set the cursor back to normal. */
	gdk_window = gtk_widget_get_window (GTK_WIDGET (dialog));
	gdk_window_set_cursor (gdk_window, NULL);

	e_mail_store_create_folder_finish (
		CAMEL_STORE (source_object), result, &local_error);

	/* Ignore cancellations. */
	if (e_activity_handle_cancellation (activity, local_error)) {
		g_error_free (local_error);

	} else if (local_error != NULL) {
		e_alert_submit (
			alert_sink,
			"system:simple-error",
			local_error->message, NULL);
		g_error_free (local_error);

	} else {
		gtk_widget_destroy (GTK_WIDGET (dialog));
	}

	async_context_free (async_context);
}

static void
mail_folder_create_dialog_create_folder (EMailFolderCreateDialog *dialog)
{
	CamelStore *store = NULL;
	gchar *create_folder_name;
	gchar *parent_folder_name = NULL;
	const gchar *text;

	em_folder_selector_get_selected (
		EM_FOLDER_SELECTOR (dialog), &store, &parent_folder_name);

	g_return_if_fail (store != NULL);

	text = gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry));

	if (parent_folder_name != NULL) {
		create_folder_name = g_strconcat (
			parent_folder_name, "/", text, NULL);
	} else {
		create_folder_name = g_strdup (text);
	}

	/* For the vfolder store, we just open the editor window. */
	if (CAMEL_IS_VEE_STORE (store)) {
		EMailUISession *session;
		EFilterRule *rule;

		session = e_mail_folder_create_dialog_get_session (dialog);
		rule = em_vfolder_editor_rule_new (E_MAIL_SESSION (session));
		e_filter_rule_set_name (rule, create_folder_name);
		vfolder_gui_add_rule (EM_VFOLDER_RULE (rule));

		gtk_widget_destroy (GTK_WIDGET (dialog));

	} else {
		AsyncContext *async_context;
		EActivity *activity;
		GCancellable *cancellable;
		GdkCursor *gdk_cursor;
		GdkWindow *gdk_window;

		/* Make the cursor appear busy. */
		gdk_cursor = gdk_cursor_new (GDK_WATCH);
		gdk_window = gtk_widget_get_window (GTK_WIDGET (dialog));
		gdk_window_set_cursor (gdk_window, gdk_cursor);
		g_object_unref (gdk_cursor);

		activity = em_folder_selector_new_activity (
			EM_FOLDER_SELECTOR (dialog));

		async_context = g_slice_new0 (AsyncContext);
		async_context->dialog = g_object_ref (dialog);
		async_context->activity = g_object_ref (activity);

		cancellable = e_activity_get_cancellable (activity);

		e_mail_store_create_folder (
			store, create_folder_name,
			G_PRIORITY_DEFAULT, cancellable,
			mail_folder_create_dialog_create_folder_cb,
			async_context);

		g_object_unref (activity);
	}

	g_free (create_folder_name);
	g_free (parent_folder_name);

	g_object_unref (store);
}

static gboolean
mail_folder_create_dialog_inputs_are_valid (EMailFolderCreateDialog *dialog)
{
	GtkEntry *entry;
	const gchar *original_text;
	gchar *stripped_text;
	gboolean folder_or_store_is_selected;
	gboolean inputs_are_valid;

	entry = GTK_ENTRY (dialog->priv->name_entry);

	original_text = gtk_entry_get_text (entry);
	stripped_text = e_util_strdup_strip (original_text);

	folder_or_store_is_selected =
		em_folder_selector_get_selected (
		EM_FOLDER_SELECTOR (dialog), NULL, NULL);

	inputs_are_valid =
		folder_or_store_is_selected &&
		(stripped_text != NULL) &&
		(strchr (stripped_text, '/') == NULL);

	g_free (stripped_text);

	return inputs_are_valid;
}

static void
mail_folder_create_dialog_entry_activate_cb (GtkEntry *entry,
                                             EMailFolderCreateDialog *dialog)
{
	if (mail_folder_create_dialog_inputs_are_valid (dialog))
		gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
}

static void
mail_folder_create_dialog_entry_changed_cb (GtkEntry *entry,
                                            EMailFolderCreateDialog *dialog)
{
	gtk_dialog_set_response_sensitive (
		GTK_DIALOG (dialog), GTK_RESPONSE_OK,
		mail_folder_create_dialog_inputs_are_valid (dialog));
}

static void
mail_folder_create_dialog_set_session (EMailFolderCreateDialog *dialog,
                                       EMailUISession *session)
{
	g_return_if_fail (E_IS_MAIL_UI_SESSION (session));
	g_return_if_fail (dialog->priv->session == NULL);

	dialog->priv->session = g_object_ref (session);
}

static void
mail_folder_create_dialog_set_property (GObject *object,
                                        guint property_id,
                                        const GValue *value,
                                        GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_SESSION:
			mail_folder_create_dialog_set_session (
				E_MAIL_FOLDER_CREATE_DIALOG (object),
				g_value_get_object (value));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
mail_folder_create_dialog_get_property (GObject *object,
                                        guint property_id,
                                        GValue *value,
                                        GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_SESSION:
			g_value_set_object (
				value,
				e_mail_folder_create_dialog_get_session (
				E_MAIL_FOLDER_CREATE_DIALOG (object)));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
mail_folder_create_dialog_dispose (GObject *object)
{
	EMailFolderCreateDialogPrivate *priv;

	priv = E_MAIL_FOLDER_CREATE_DIALOG_GET_PRIVATE (object);

	g_clear_object (&priv->session);
	g_clear_object (&priv->name_entry);

	/* Chain up to parent's dispose() method. */
	G_OBJECT_CLASS (e_mail_folder_create_dialog_parent_class)->
		dispose (object);
}

static void
mail_folder_create_dialog_constructed (GObject *object)
{
	EMailFolderCreateDialog *dialog;
	EMailAccountStore *account_store;
	EMailUISession *session;
	EMFolderTree *folder_tree;
	EMFolderTreeModel *model;
	GQueue queue = G_QUEUE_INIT;
	GtkWidget *container;
	GtkWidget *widget;
	GtkLabel *label;

	dialog = E_MAIL_FOLDER_CREATE_DIALOG (object);
	session = e_mail_folder_create_dialog_get_session (dialog);
	model = em_folder_selector_get_model (EM_FOLDER_SELECTOR (dialog));

	/* Populate the tree model before chaining up, since the
	 * subclass will immediately try to restore the tree state. */

	account_store = e_mail_ui_session_get_account_store (session);
	e_mail_account_store_queue_enabled_services (account_store, &queue);

	while (!g_queue_is_empty (&queue)) {
		CamelService *service;
		CamelStoreFlags flags;

		service = g_queue_pop_head (&queue);
		g_warn_if_fail (CAMEL_IS_STORE (service));

		flags = CAMEL_STORE (service)->flags;
		if ((flags & CAMEL_STORE_CAN_EDIT_FOLDERS) == 0)
			continue;

		em_folder_tree_model_add_store (model, CAMEL_STORE (service));
	}

	/* Chain up to parent's constructed() method. */
	G_OBJECT_CLASS (e_mail_folder_create_dialog_parent_class)->constructed (object);

	gtk_window_set_title (GTK_WINDOW (dialog), _("Create Folder"));

	em_folder_selector_set_caption (
		EM_FOLDER_SELECTOR (dialog),
		_("Specify where to create the folder:"));

	em_folder_selector_set_default_button_label (
		EM_FOLDER_SELECTOR (dialog), _("C_reate"));

	folder_tree = em_folder_selector_get_folder_tree (
		EM_FOLDER_SELECTOR (dialog));
	em_folder_tree_set_excluded (folder_tree, EMFT_EXCLUDE_NOINFERIORS);

	/* Add a folder name entry field to the dialog. */

	container = em_folder_selector_get_content_area (
		EM_FOLDER_SELECTOR (dialog));

	widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
	gtk_widget_show (widget);

	container = widget;

	widget = gtk_label_new_with_mnemonic (_("Folder _name:"));
	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
	gtk_widget_show (widget);

	label = GTK_LABEL (widget);

	widget = gtk_entry_new ();
	gtk_label_set_mnemonic_widget (label, widget);
	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
	dialog->priv->name_entry = g_object_ref (widget);
	gtk_widget_grab_focus (widget);
	gtk_widget_show (widget);

	g_signal_connect (
		widget, "activate",
		G_CALLBACK (mail_folder_create_dialog_entry_activate_cb),
		dialog);

	g_signal_connect (
		widget, "changed",
		G_CALLBACK (mail_folder_create_dialog_entry_changed_cb),
		dialog);
}

static void
mail_folder_create_dialog_response (GtkDialog *dialog,
                                    gint response_id)
{
	/* Do not chain up.  GtkDialog does not implement this method. */

	switch (response_id) {
		case GTK_RESPONSE_OK:
			mail_folder_create_dialog_create_folder (
				E_MAIL_FOLDER_CREATE_DIALOG (dialog));
			break;
		case GTK_RESPONSE_CANCEL:
			gtk_widget_destroy (GTK_WIDGET (dialog));
			break;
		default:
			break;
	}
}

static void
mail_folder_create_dialog_folder_selected (EMFolderSelector *selector,
                                           CamelStore *store,
                                           const gchar *folder_name)
{
	EMailFolderCreateDialog *dialog;

	/* Do not chain up.  This overrides the subclass behavior. */

	dialog = E_MAIL_FOLDER_CREATE_DIALOG (selector);

	/* Can be NULL during dispose, when the folder tree model is being cleared */
	if (dialog->priv->name_entry)
		gtk_dialog_set_response_sensitive (
			GTK_DIALOG (dialog), GTK_RESPONSE_OK,
			mail_folder_create_dialog_inputs_are_valid (dialog));
}

static void
e_mail_folder_create_dialog_class_init (EMailFolderCreateDialogClass *class)
{
	GObjectClass *object_class;
	GtkDialogClass *dialog_class;
	EMFolderSelectorClass *selector_class;

	g_type_class_add_private (
		class, sizeof (EMailFolderCreateDialogPrivate));

	object_class = G_OBJECT_CLASS (class);
	object_class->set_property = mail_folder_create_dialog_set_property;
	object_class->get_property = mail_folder_create_dialog_get_property;
	object_class->dispose = mail_folder_create_dialog_dispose;
	object_class->constructed = mail_folder_create_dialog_constructed;

	dialog_class = GTK_DIALOG_CLASS (class);
	dialog_class->response = mail_folder_create_dialog_response;

	selector_class = EM_FOLDER_SELECTOR_CLASS (class);
	selector_class->folder_selected = mail_folder_create_dialog_folder_selected;

	g_object_class_install_property (
		object_class,
		PROP_SESSION,
		g_param_spec_object (
			"session",
			"Session",
			"An EMailUISession from which "
			"to list enabled mail stores",
			E_TYPE_MAIL_UI_SESSION,
			G_PARAM_READWRITE |
			G_PARAM_CONSTRUCT_ONLY |
			G_PARAM_STATIC_STRINGS));

	signals[FOLDER_CREATED] = g_signal_new (
		"folder-created",
		G_OBJECT_CLASS_TYPE (object_class),
		G_SIGNAL_RUN_LAST,
		G_STRUCT_OFFSET (EMailFolderCreateDialogClass, folder_created),
		NULL, NULL, NULL,
		G_TYPE_NONE, 2,
		CAMEL_TYPE_STORE,
		G_TYPE_STRING);
}

static void
e_mail_folder_create_dialog_init (EMailFolderCreateDialog *dialog)
{
	dialog->priv = E_MAIL_FOLDER_CREATE_DIALOG_GET_PRIVATE (dialog);
}

GtkWidget *
e_mail_folder_create_dialog_new (GtkWindow *parent,
                                 EMailUISession *session)
{
	GtkWidget *dialog;
	EMFolderTreeModel *model;

	g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session), NULL);

	/* XXX The folder tree model is a construct-only property in
	 *     EMFolderSelector, so create an empty model here and then
	 *     populate it during instance initialization.  Already too
	 *     much logic here for a "new" function, but works for now. */

	model = em_folder_tree_model_new ();
	em_folder_tree_model_set_session (model, E_MAIL_SESSION (session));

	dialog = g_object_new (
		E_TYPE_MAIL_FOLDER_CREATE_DIALOG,
		"transient-for", parent,
		"model", model,
		"session", session, NULL);

	g_object_unref (model);

	return dialog;
}

EMailUISession *
e_mail_folder_create_dialog_get_session (EMailFolderCreateDialog *dialog)
{
	g_return_val_if_fail (E_IS_MAIL_FOLDER_CREATE_DIALOG (dialog), NULL);

	return dialog->priv->session;
}

