/* sunone-subscription-dialog.c
 *
 * Copyright (C) 2002-2005 Sun Microsystems, Inc
 *
 * AUTHORS
 *     Jack Jia <jack.jia@sun.com>
 *     Harry Lu <harry.lu@sun.com>
 *     Alfred Peng <alfred.peng@sun.com>
 *     Rodrigo Moya <rodrigo@ximian.com>
 *
 */

#include "sunone-i18n.h"
#include <string.h>
#include <sys/stat.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>
#include <e-util/e-dialog-widgets.h>
#include <e-util/e-dialog-utils.h>
#include "sunone-subscription-dialog.h"

extern char *evolution_dir;

struct _SunOneSubscriptionDialogPrivate {
	SunOneFolderTree *tree;
	SunOneConnection *cnc;
	GList *matched_calids;
	SunOneConnectionPreferences *prefs;
	GHashTable *to_subscribe;
	GladeXML *xml;
	GtkWidget *main_table;
	GtkTreeView *subscriptions_tree;
	GtkWidget *search_field;
	GtkWidget *search_by;
	GtkWidget *search_string;
	GtkWidget *search_button;
	GtkWidget *search_clear;
};

static void sunone_subscription_dialog_class_init (SunOneSubscriptionDialogClass *klass);
static void sunone_subscription_dialog_init (SunOneSubscriptionDialog *object);
static void sunone_subscription_dialog_dispose (GObject *object);
static void sunone_subscription_dialog_finalize (GObject *object);

static GObjectClass *parent_class = NULL;

static void
sunone_subscription_dialog_class_init (SunOneSubscriptionDialogClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	parent_class = g_type_class_ref (GTK_TYPE_DIALOG);

	object_class->dispose = sunone_subscription_dialog_dispose;
	object_class->finalize = sunone_subscription_dialog_finalize;
}

static void
refresh_table (SunOneSubscriptionDialog *dialog)
{
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;
	GtkListStore *subscriptions_model;
	GList *l;
		
	subscriptions_model = (GtkListStore *) gtk_tree_view_get_model (priv->subscriptions_tree);

	gtk_list_store_clear (subscriptions_model);

	for (l = priv->matched_calids; l != NULL; l = l->next) {
		SunOneCalendarProperties *calprops;
		GtkTreeIter iter;

		calprops = l->data;
		gtk_list_store_append (subscriptions_model, &iter);
		gtk_list_store_set (subscriptions_model, &iter,
				    SUB_COLUMN,
				    g_hash_table_lookup (priv->to_subscribe, calprops->relative_calid) != NULL,
				    SUB_CALID_COLUMN,
				    calprops->relative_calid,
				    SUB_OWNER_COLUMN,
				    calprops->primary_owner,
				    SUB_DES_COLUMN,
				    calprops->display_name,
				    -1);
	}
}

static void
free_matched_calids (SunOneSubscriptionDialogPrivate *priv)
{
	if (priv->matched_calids) {
		g_list_foreach (priv->matched_calids, (GFunc) sunone_connection_free_calprops, NULL);
		g_list_free (priv->matched_calids);
		priv->matched_calids = NULL;
	}
}

static void
clear_search_cb (GtkButton *button, gpointer user_data)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (user_data);
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;

	g_return_if_fail (IS_SUNONE_SUBSCRIPTION_DIALOG (dialog));

	gtk_combo_box_set_active ((GtkComboBox *) priv->search_by, CONTAINS);
	gtk_combo_box_set_active ((GtkComboBox *) priv->search_field, NAME_OR_ID);
	gtk_entry_set_text (GTK_ENTRY (priv->search_string), "");

	/* refresh the list */
	free_matched_calids (priv);
	refresh_table (dialog);
}

static void
do_search_cb (GtkButton *button, gpointer user_data)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (user_data);
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;
	GList *l;
	gint by, field;
	const char *str;

	g_return_if_fail (IS_SUNONE_SUBSCRIPTION_DIALOG (dialog));

	/*free previous result*/
	free_matched_calids (priv);

	/*do new search*/
	by = gtk_combo_box_get_active ((GtkComboBox *) priv->search_by);
	field = gtk_combo_box_get_active ((GtkComboBox *) priv->search_field);
	str = gtk_entry_get_text (GTK_ENTRY (priv->search_string));
	if (str && *str != 0)
		priv->matched_calids = sunone_connection_search_calids (priv->cnc, field, by, str);

	priv->prefs = sunone_connection_get_preferences (priv->cnc);
	if (priv->prefs) {
		for (l = priv->prefs->subscriptions; l != NULL; l = l->next) {
			SunOneCalendarSubscription *sub = l->data;

			g_hash_table_insert (priv->to_subscribe, g_strdup (sub->calid),
					     g_strdup (sub->display_name ? sub->display_name : ""));
		}
	}

	refresh_table (dialog);
}

static void
search_activated_cb (GtkComboBox *w, gpointer user_data)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (user_data);
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;

	g_return_if_fail (IS_SUNONE_SUBSCRIPTION_DIALOG (dialog));

	gtk_widget_set_sensitive (priv->search_button, TRUE);
	gtk_widget_grab_focus (priv->search_string);
}

static void
subscribe_toggled (GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (data);
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;
	SunOneCalendarProperties *calprops;
	GtkTreeModel *model;
	GtkTreeIter  iter;
	GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
	gboolean subscribe;
	gint row;
	GList *l;
	gpointer orig_key, orig_value;

	g_return_if_fail (IS_SUNONE_SUBSCRIPTION_DIALOG (dialog));

	model = gtk_tree_view_get_model (priv->subscriptions_tree);
	gtk_tree_model_get_iter (model, &iter, path);
	gtk_tree_model_get (model, &iter, SUB_COLUMN, &subscribe, -1);
	row = gtk_tree_path_get_indices (path)[0];
	gtk_tree_path_free (path);

	g_return_if_fail (row >= 0 && row < g_list_length (priv->matched_calids));

	l = g_list_nth (priv->matched_calids, row);
	if (!l)
		return;

	calprops = l->data;
	/* don't allow subscription from default calendar */
	if (!strcmp (calprops->relative_calid, priv->prefs->default_calendar))
		return;
	if (!subscribe) {
		g_hash_table_insert (priv->to_subscribe, g_strdup (calprops->relative_calid),
		g_strdup (calprops->display_name ? calprops->display_name : ""));
	} else {
		if (g_hash_table_lookup_extended (priv->to_subscribe, calprops->relative_calid, &orig_key, &orig_value)) {
			g_hash_table_remove (priv->to_subscribe, calprops->relative_calid);
			g_free (orig_key);
			g_free (orig_value);
		}
	}
	subscribe = !subscribe;

	gtk_list_store_set (GTK_LIST_STORE (model), &iter, SUB_COLUMN, subscribe, -1);
	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE);
}

static void
sunone_subscription_dialog_init (SunOneSubscriptionDialog *object)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (object);
	SunOneSubscriptionDialogPrivate *priv;
	GtkWidget *label;

	dialog->priv = g_new0 (SunOneSubscriptionDialogPrivate, 1);
	priv = dialog->priv;

	priv->cnc = NULL;
	priv->matched_calids = NULL;
	priv->prefs = NULL;
	priv->to_subscribe = g_hash_table_new (g_str_hash, g_str_equal);

	/* load UI */
	priv->xml = glade_xml_new (SUNONE_GLADEDIR "/sunone-subscription-dialog.glade",
				     "subscription-main-table", NULL);
	if (!priv->xml) {
		label = gtk_label_new (_("Could not load permissions dialog UI"));
		gtk_widget_show (label);
		gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, TRUE, TRUE, 2);
		return;
	}

	priv->main_table = glade_xml_get_widget (priv->xml, "subscription-main-table");
	gtk_widget_show (priv->main_table);
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), priv->main_table, TRUE, TRUE, 2);

	priv->search_field = glade_xml_get_widget (priv->xml, "subscription-search-field");
	gtk_combo_box_set_active ((GtkComboBox *) priv->search_field, 0);

	g_signal_connect ((GtkComboBox *) priv->search_field,
			    "changed", G_CALLBACK (search_activated_cb), dialog);

	priv->search_by = glade_xml_get_widget (priv->xml, "subscription-search-by");
	gtk_combo_box_set_active ((GtkComboBox *) priv->search_by, 0);

	g_signal_connect ((GtkComboBox *) priv->search_by,
			    "changed", G_CALLBACK (search_activated_cb), dialog);

	priv->search_string = glade_xml_get_widget (priv->xml, "subscription-search-string");
	g_signal_connect (G_OBJECT (priv->search_string), "changed",
			    G_CALLBACK (search_activated_cb), dialog);
	g_signal_connect (G_OBJECT (priv->search_string), "activate",
			    G_CALLBACK (do_search_cb), dialog);

	priv->search_button = glade_xml_get_widget (priv->xml, "subscription-search-button");
	g_signal_connect (G_OBJECT (priv->search_button), "clicked",
			    G_CALLBACK (do_search_cb), dialog);

	priv->search_clear = glade_xml_get_widget (priv->xml, "subscription-search-clear");
	g_signal_connect (G_OBJECT (priv->search_clear), "clicked",
			    G_CALLBACK (clear_search_cb), dialog);

	priv->subscriptions_tree = (GtkTreeView *)glade_xml_get_widget (priv->xml, "sub-treeview");

	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
}

static void
free_to_subscribe_item (gpointer key, gpointer value, gpointer user_data)
{
	g_free (key);
	g_free (value);
}

static void
sunone_subscription_dialog_dispose (GObject *object)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (object);
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;

	g_return_if_fail (IS_SUNONE_SUBSCRIPTION_DIALOG (dialog));

	if (priv) {
		if (priv->tree) {
			g_object_unref (G_OBJECT (priv->tree));
			priv->tree = NULL;
		}

		if (priv->cnc) {
			g_object_unref (G_OBJECT (priv->cnc));
			priv->cnc = NULL;
		}

		if (priv->xml) {
			g_object_unref (G_OBJECT (priv->xml));
			priv->xml = NULL;
		}

		if (priv->prefs) {
			sunone_connection_free_preferences (priv->prefs);
			priv->prefs = NULL;
		}

		if (priv->to_subscribe) {
			g_hash_table_foreach (priv->to_subscribe, (GHFunc) free_to_subscribe_item, NULL);
			g_hash_table_destroy (priv->to_subscribe);
			priv->to_subscribe = NULL;
		}

		if (priv->matched_calids) {
			g_list_foreach (priv->matched_calids, (GFunc) sunone_connection_free_calprops, NULL);
			g_list_free (priv->matched_calids);
			priv->matched_calids = NULL;
		}

		g_free (priv);
		dialog->priv = NULL;
	}

	if (G_OBJECT_CLASS (parent_class)->dispose)
		(* G_OBJECT_CLASS (parent_class)->dispose) (object);
}

static void
sunone_subscription_dialog_finalize (GObject *object)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (object);

	g_return_if_fail (IS_SUNONE_SUBSCRIPTION_DIALOG (dialog));

	if (G_OBJECT_CLASS (parent_class)->finalize)
		(* G_OBJECT_CLASS (parent_class)->finalize) (object);
}

G_DEFINE_TYPE (SunOneSubscriptionDialog, sunone_subscription_dialog, GTK_TYPE_DIALOG)

static void
add_subscription_from_hash (gpointer key, gpointer value, gpointer user_data)
{
	SunOneConnectionPreferences *prefs = user_data;
	SunOneCalendarSubscription *sub;
	gchar *calid = key;
	gchar *display_name = value;

	sub = g_new0 (SunOneCalendarSubscription, 1);
	sub->calid = g_strdup (calid);
	sub->display_name = g_strdup (display_name);

	prefs->subscriptions = g_list_append (prefs->subscriptions, sub);
}

static void
add_folder_hash (gpointer key, gpointer value, gpointer user_data)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (user_data);
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;
	SunOneAccount *account;
	char *calid = key;
	char *display_name = value;

	account = sunone_folder_tree_get_account (priv->tree);
	if (!account)
		return;

	sunone_folder_tree_add_calendar (priv->tree, account, calid, display_name);
}

static void
update_folder_tree (SunOneSubscriptionDialog *dialog, GList *old_subscriptions)
{
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;
	SunOneAccount *account;
	GList *l;
	gchar *calid_hash, *name_hash;

	account = sunone_folder_tree_get_account (priv->tree);
	if (!account)
		return;

	for (l = old_subscriptions; l != NULL; l = l->next) {
		SunOneCalendarSubscription *sub = l->data;

		if (g_hash_table_lookup_extended (priv->to_subscribe, sub->calid, 
						  (gpointer *)&calid_hash, (gpointer *)&name_hash)) {
			g_hash_table_remove (priv->to_subscribe, sub->calid);
			g_free (calid_hash);
			g_free (name_hash);

			/* found in both lists, so no changes */
			continue;
		}

		/* not found in the new list, so remove it */
		sunone_folder_tree_remove_calendar (priv->tree, account, sub->calid);
	}

	/* now, the left ones are new subscriptions */
	g_hash_table_foreach (priv->to_subscribe, (GHFunc) add_folder_hash, dialog);
	sunone_folder_tree_sync ();
}

static void
dialog_button_clicked_cb (GtkWidget *gdialog, gint response, gpointer user_data)
{
	SunOneSubscriptionDialog *dialog = SUNONE_SUBSCRIPTION_DIALOG (gdialog);
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;

	g_return_if_fail (IS_SUNONE_SUBSCRIPTION_DIALOG (dialog));

	if (response == GTK_RESPONSE_OK) {
		guint rc;
		GList *old_subscriptions;

		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL, FALSE);

		old_subscriptions = priv->prefs->subscriptions;
		priv->prefs->subscriptions = NULL;

		g_hash_table_foreach (priv->to_subscribe, (GHFunc) add_subscription_from_hash, priv->prefs);

		/* make sure we don't remove accidentally the default calendar */
		if (!g_hash_table_lookup (priv->to_subscribe, priv->prefs->default_calendar)) {
			add_subscription_from_hash (priv->prefs->default_calendar,
						    priv->prefs->default_calendar,
						    priv->prefs);
		}

		/* send query to the server */
		rc = sunone_connection_set_preferences (priv->cnc, priv->prefs);
		if (SUNONE_ERROR_IS_SUCCESSFUL (rc)) {
			update_folder_tree (dialog, old_subscriptions);
			g_list_foreach (old_subscriptions, (GFunc) sunone_connection_free_subscription, NULL);
			g_list_free (old_subscriptions);
		} else {
			g_list_foreach (old_subscriptions, (GFunc) sunone_connection_free_subscription, NULL);
			g_list_free (old_subscriptions);
			e_notice (GTK_WINDOW (dialog), GTK_MESSAGE_ERROR, _("Could not set preferences for this connection"));

			gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE);
			gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL, TRUE);
			return;
		}
	}
	gtk_widget_destroy (GTK_WIDGET (gdialog));
}

static void
load_subscriptions (SunOneSubscriptionDialog *dialog)
{
	SunOneSubscriptionDialogPrivate *priv = dialog->priv;
	GtkListStore *subscriptions_model;
	GtkCellRenderer *renderer;

	/* create the Table and its model */
	subscriptions_model = gtk_list_store_new (SUB_TREE_N_COLUMNS, G_TYPE_BOOLEAN,
						  G_TYPE_STRING, G_TYPE_STRING,
						  G_TYPE_STRING);
	gtk_tree_view_set_model (priv->subscriptions_tree, (GtkTreeModel *)subscriptions_model);
	
	renderer = gtk_cell_renderer_toggle_new();
	g_signal_connect (renderer, "toggled",
					    G_CALLBACK (subscribe_toggled), dialog);
	gtk_tree_view_insert_column_with_attributes (priv->subscriptions_tree, -1,
						     _("Subscription"), renderer,
						     "active",
						     SUB_COLUMN,
						     NULL);

	renderer = gtk_cell_renderer_text_new ();
	gtk_tree_view_insert_column_with_attributes (priv->subscriptions_tree, -1,
						     _("Calendar ID"), renderer,
						     "text",
						     SUB_CALID_COLUMN,
						     NULL);

	renderer = gtk_cell_renderer_text_new ();
	gtk_tree_view_insert_column_with_attributes (priv->subscriptions_tree, -1,
						     _("Owner"), renderer,
						     "text",
						     SUB_OWNER_COLUMN,
						     NULL);

	renderer = gtk_cell_renderer_text_new ();
	gtk_tree_view_insert_column_with_attributes (priv->subscriptions_tree, -1,
						     _("Description"), renderer,
						     "text",
						     SUB_DES_COLUMN,
						     NULL);

	if (priv->matched_calids)
		refresh_table (dialog);	
}

GtkWidget *
sunone_subscription_dialog_new (SunOneFolderTree *tree, SunOneConnection *cnc)
{
	SunOneSubscriptionDialogPrivate *priv;
	SunOneSubscriptionDialog *dialog;

	g_return_val_if_fail (IS_SUNONE_CONNECTION (cnc), NULL);
	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL);

	dialog = g_object_new (SUNONE_SUBSCRIPTION_DIALOG_TYPE, NULL);
	priv = dialog->priv;

	g_object_ref (tree);
	priv->tree = tree;
	g_object_ref (cnc);
	priv->cnc = cnc;

	gtk_window_set_title (GTK_WINDOW (dialog), _("Remote Calendar Subscriptions"));
	gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); 
	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
	gtk_widget_set_size_request (GTK_WIDGET (dialog), -1, 400);

	g_signal_connect (dialog, "response",
			    G_CALLBACK (dialog_button_clicked_cb), NULL);

	load_subscriptions (dialog);

	return GTK_WIDGET (dialog);
}
