/*
 *  Copyright (C) 2002  Ricardo Fernández Pascual
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <glib/gi18n.h>
#include "bookmarks-import-druid.h"
#include "bookmarks-io.h"
#include "bookmarks-tree-model.h"
#include "bookmarks-tree-view.h"
#include "bookmarks-util.h"
#include "xbel.h"
#include "gul-gobject-misc.h"
#include <libgnomeui/gnome-druid.h>
#include <libgnomeui/gnome-druid-page-standard.h>
#include <libgnomeui/gnome-druid-page-edge.h>
#include <libgnomeui/gnome-file-entry.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtktreeview.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkscrolledwindow.h>
#include <string.h>

/**
 * Private data
 */
struct _GbImportDruidPrivate {

	GbBookmarkSet *dest;
	GbBookmarkSet *imported;
	
	GtkWidget *window;
	GnomeDruid *druid;

	GtkWidget *location_entry;
	GtkTreeView *location_list;
	GtkListStore *location_model;
	gchar *location;

	GbTreeView *imported_list;
	GbTreeView *current_list;
	GbTreeView *second_current_list;

	GtkWidget *auto_merge_page;	
	GtkWidget *manual_merge_page;	

	gboolean backup_done;
	gchar *backup_filename;
};

/**
 * Private functions, only availble from this file
 */
static void		gb_import_druid_class_init		(GbImportDruidClass *klass);
static void		gb_import_druid_init			(GbImportDruid *d);
static void		gb_import_druid_init_widgets		(GbImportDruid *d);
static void		gb_import_druid_init_location_widgets 	(GbImportDruid *d);
static void		gb_import_druid_init_merge_widgets 	(GbImportDruid *d);
static void		gb_import_druid_finalize_impl		(GObject *o);
static const char *	gb_import_druid_get_string		(GbImportDruid *d, const char *string_id);
static const char *	gb_import_druid_get_string_impl		(GbImportDruid *d, const char *string_id);
static GSList *		gb_import_druid_get_locations		(GbImportDruid *d);
static GSList *		gb_import_druid_get_locations_impl	(GbImportDruid *d);
static GbIO *		gb_import_druid_get_io			(GbImportDruid *d);
static GbIO *		gb_import_druid_get_io_impl		(GbImportDruid *d);
static void 		gb_import_druid_page_3_prepare_cb	(GnomeDruidPage *druid_page, 
								 GtkWidget *druid,
								 GbImportDruid *d);
static void 		gb_import_druid_page_5_prepare_cb	(GnomeDruidPage *druid_page, 
								 GtkWidget *druid,
								 GbImportDruid *d);
static void		gb_import_druid_location_list_selection_changed_cb (GtkTreeSelection *ts, 
									    GbImportDruid *d);
static void		gb_import_druid_location_entry_changed_cb (GtkEditable *e, GbImportDruid *d);
static void		gb_import_druid_merge_button_clicked_cb	(GtkButton *b, GbImportDruid *d);


static gpointer g_object_class;

/**
 * ImportDruid object
 */

MAKE_GET_TYPE (gb_import_druid, "GbImportDruid", GbImportDruid, gb_import_druid_class_init, 
	       gb_import_druid_init, G_TYPE_OBJECT);

static void
gb_import_druid_class_init (GbImportDruidClass *klass)
{
	klass->get_string = gb_import_druid_get_string_impl;
	klass->get_locations = gb_import_druid_get_locations_impl;
	klass->get_io = gb_import_druid_get_io_impl;
	G_OBJECT_CLASS (klass)->finalize = gb_import_druid_finalize_impl;
	g_object_class = g_type_class_peek_parent (klass);
}

static void 
gb_import_druid_init (GbImportDruid *e)
{
	GbImportDruidPrivate *p = g_new0 (GbImportDruidPrivate, 1);
	e->priv = p;
	
}

static void
gb_import_druid_finalize_impl (GObject *o)
{
	GbImportDruid *e = GB_IMPORT_DRUID (o);
	GbImportDruidPrivate *p = e->priv;

	if (p->dest)
	{
		g_object_unref (p->dest);
	}

	if (p->imported)
	{
		g_object_unref (p->imported);
	}

	g_free (p->location);
	g_free (p->backup_filename);
	g_free (p);
	
	G_OBJECT_CLASS (g_object_class)->finalize (o);

}

static void
gb_import_druid_init_widgets (GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;
	GnomeDruidPage *page;
	GtkWidget *w;

	g_return_if_fail (GB_IS_BOOKMARK_SET (p->dest));
	g_return_if_fail (p->window == NULL);
	
	p->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title (GTK_WINDOW (p->window), 
			      gb_import_druid_get_string (d, "window title"));

	g_object_weak_ref (G_OBJECT (p->window), (GWeakNotify) g_object_unref, d);

	p->druid = GNOME_DRUID (gnome_druid_new ());
	gtk_widget_show (GTK_WIDGET (p->druid));
	gtk_container_add (GTK_CONTAINER (p->window), GTK_WIDGET (p->druid));
	gnome_druid_set_show_help (p->druid, FALSE);

	g_signal_connect_object (p->druid, "cancel",
				 G_CALLBACK (gtk_widget_destroy),
				 p->window,
				 G_CONNECT_SWAPPED);

	g_signal_connect_object (p->druid, "destroy",
				 G_CALLBACK (gtk_widget_destroy),
				 p->window,
				 G_CONNECT_SWAPPED);

	/* page 1 */
	page = GNOME_DRUID_PAGE (gnome_druid_page_edge_new (GNOME_EDGE_START));
	gtk_widget_show (GTK_WIDGET (page));
	gnome_druid_page_edge_set_title (GNOME_DRUID_PAGE_EDGE (page), 
					 gb_import_druid_get_string (d, "page 1 title"));
	gnome_druid_append_page (p->druid, page);
	gnome_druid_set_page (p->druid, page);
	gnome_druid_page_edge_set_text (GNOME_DRUID_PAGE_EDGE (page),
					_("This druid will help you to import a bookmarks file and "
					  "merge its contents in the current bookmark set."));

	/* page 2 */
	page = GNOME_DRUID_PAGE (gnome_druid_page_standard_new ());
	gtk_widget_show (GTK_WIDGET (page));
	gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (page), 
					     _("Bookmark file location"));
	gnome_druid_append_page (p->druid, page);
	
	gb_import_druid_init_location_widgets (d);
	w = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), 
					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_widget_show (w);
	gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (p->location_list));

	gnome_druid_page_standard_append_item (GNOME_DRUID_PAGE_STANDARD (page),
					       _("Found possible locations:"),
					       w,
					       _("These locations are typical places where "
						 "bookmarks can be found."));

	gnome_druid_page_standard_append_item (GNOME_DRUID_PAGE_STANDARD (page),
					       _("Chosen location:"),
					       p->location_entry,
					       _("This is the location that will be used. You "
						 "can choose one from the list or enter your own."));
	
	/* page 3 */
	page = GNOME_DRUID_PAGE (gnome_druid_page_standard_new ());
	gtk_widget_show (GTK_WIDGET (page));
	p->manual_merge_page = GTK_WIDGET (page);
	gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (page), 
					     _("Merge imported bookmarks"));
	gnome_druid_append_page (p->druid, page);
	
	gb_import_druid_init_merge_widgets (d);

	w = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), 
					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_widget_show (w);
	gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (p->imported_list));
	gnome_druid_page_standard_append_item (GNOME_DRUID_PAGE_STANDARD (page),
					       _("Imported bookmarks:"),
					       w,
					       _("These are the bookmarks just imported."));
	gtk_box_set_child_packing   (GTK_BOX (w->parent), w, TRUE, TRUE, 0, GTK_PACK_START);
	gtk_box_set_child_packing   (GTK_BOX (w->parent->parent), w->parent, TRUE, TRUE, 0, GTK_PACK_START);

	w = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), 
					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_widget_show (w);
	gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (p->current_list));
	gnome_druid_page_standard_append_item (GNOME_DRUID_PAGE_STANDARD (page),
					       _("Current bookmarks:"),
					       w,
					       _("These are the bookmarks of your "
						 "current bookmark set."));
	gtk_box_set_child_packing   (GTK_BOX (w->parent), w, TRUE, TRUE, 0, GTK_PACK_START);
	gtk_box_set_child_packing   (GTK_BOX (w->parent->parent), w->parent, TRUE, TRUE, 0, GTK_PACK_START);

	w = gtk_button_new_with_mnemonic (_("Automatic merge"));
	gtk_widget_show (w);
	gtk_box_pack_start (GTK_BOX (GNOME_DRUID_PAGE_STANDARD (page)->vbox), w, FALSE, FALSE, 0);
	g_signal_connect (w, "clicked",
			  G_CALLBACK (gb_import_druid_merge_button_clicked_cb), d);			  

	w = gtk_label_new (_("You can manually merge the two bookmark sets using "
			     "drag & drop and copy & paste.\n"
			     "Alternatively, you can try the Automatic Merger."));
	gtk_widget_show (w);
	gtk_box_pack_start (GTK_BOX (GNOME_DRUID_PAGE_STANDARD (page)->vbox), w, FALSE, FALSE, 0);

	
	g_signal_connect (page, "prepare", G_CALLBACK (gb_import_druid_page_3_prepare_cb), d);

	/* page 4 */
	page = GNOME_DRUID_PAGE (gnome_druid_page_standard_new ());
	p->auto_merge_page = GTK_WIDGET (page);
	gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (page), 
					     _("Automatically merged bookmarks"));
	gnome_druid_append_page (p->druid, page);

	w = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), 
					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_widget_show (w);
	gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (p->second_current_list));
	gnome_druid_page_standard_append_item (GNOME_DRUID_PAGE_STANDARD (page),
					       _("Merged bookmarks:"),
					       w,
					       _("This is the resulting bookmark set after merging."));
	gtk_box_set_child_packing   (GTK_BOX (w->parent), w, TRUE, TRUE, 0, GTK_PACK_START);
	gtk_box_set_child_packing   (GTK_BOX (w->parent->parent), w->parent, TRUE, TRUE, 0, GTK_PACK_START);

	/* page 5 */
	page = GNOME_DRUID_PAGE (gnome_druid_page_edge_new (GNOME_EDGE_FINISH));
	gtk_widget_show (GTK_WIDGET (page));
	gnome_druid_page_edge_set_title (GNOME_DRUID_PAGE_EDGE (page), 
					 _("Bookmarks Imported"));
	gnome_druid_append_page (p->druid, page);

	g_signal_connect (page, "prepare", G_CALLBACK (gb_import_druid_page_5_prepare_cb), d);

	/* misc */
	gnome_druid_set_show_help (p->druid, FALSE);
	gtk_button_set_label (GTK_BUTTON (p->druid->cancel), GTK_STOCK_CLOSE);
}

static void
gb_import_druid_init_location_widgets (GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;
	GSList *locs;
	GSList *li;
	GtkTreeSelection *sel;

	p->location_entry = gnome_file_entry_new ("galeon-bookamrks-import-locations",
						  _("Choose a file to import"));

	/* Use the new file chooser if we can */
	if (g_object_class_find_property (G_OBJECT_GET_CLASS (p->location_entry), 
					  "use_filechooser" ))
	{
		g_object_set (p->location_entry, "use_filechooser", TRUE, NULL);
	}

	gtk_widget_show (p->location_entry);
	g_signal_connect (gnome_entry_gtk_entry (GNOME_ENTRY (gnome_file_entry_gnome_entry 
							      (GNOME_FILE_ENTRY (p->location_entry)))),
			  "changed", 
			  G_CALLBACK (gb_import_druid_location_entry_changed_cb), d);

	p->location_model = gtk_list_store_new (1, G_TYPE_STRING);
	p->location_list = GTK_TREE_VIEW (gtk_tree_view_new_with_model 
					  (GTK_TREE_MODEL (p->location_model)));
	
	gtk_tree_view_insert_column_with_attributes (p->location_list, -1, _("Location"),
						     gtk_cell_renderer_text_new (), 
						     "text", 0,
						     NULL);
	gtk_tree_view_set_headers_visible (p->location_list, FALSE);
	sel = gtk_tree_view_get_selection (p->location_list);
	gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
	
	g_signal_connect (sel, "changed", 
			  G_CALLBACK (gb_import_druid_location_list_selection_changed_cb), d);

	locs = gb_import_druid_get_locations (d);

	for (li = locs; li; li = li->next)
	{
		if (g_file_test (li->data, G_FILE_TEST_IS_REGULAR))
		{
			GtkTreeIter iter;
			gtk_list_store_append (p->location_model, &iter);
			gtk_list_store_set (p->location_model, &iter, 0, li->data, -1);
			
			/* make sure something is selected */
			if (!gtk_tree_selection_get_selected (sel, NULL, NULL))
			{
				gtk_tree_selection_select_iter (sel, &iter);
			}
		}
	}
	
	g_slist_foreach (locs, (GFunc) g_free, NULL);
	g_slist_free (locs);

	gtk_widget_show (GTK_WIDGET (p->location_list));
}

static void
gb_import_druid_init_merge_widgets (GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;

	p->current_list = gb_tree_view_new ();
	gtk_widget_show (GTK_WIDGET (p->current_list));

	p->second_current_list = gb_tree_view_new ();
	gtk_widget_show (GTK_WIDGET (p->second_current_list));

	p->imported_list = gb_tree_view_new ();
	gtk_widget_show (GTK_WIDGET (p->imported_list));
		
}

void
gb_import_druid_show (GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;

	g_return_if_fail (GB_IS_BOOKMARK_SET (p->dest));
	g_return_if_fail (GTK_IS_WINDOW (p->window));
	
	gtk_widget_show (GTK_WIDGET (p->window));
}

void
gb_import_druid_set_dest_bookmark_set (GbImportDruid *d, GbBookmarkSet *bs)
{
	GbImportDruidPrivate *p = d->priv;

	if (p->dest)
	{
		g_signal_handlers_disconnect_matched (p->dest, G_SIGNAL_MATCH_DATA, 0, 0, 
						      NULL, NULL, d);
		g_object_unref (p->dest);
	}

	p->dest = bs;

	if (bs)
	{
		g_object_ref (bs);
		gb_import_druid_init_widgets (d);
	}

}

static const char *
gb_import_druid_get_string_impl (GbImportDruid *d, const char *string_id)
{
	g_warning ("Should not be reached");
	return NULL;
}

static const char *
gb_import_druid_get_string (GbImportDruid *d, const char *string_id)
{
	return GB_IMPORT_DRUID_GET_CLASS (d)->get_string (d, string_id);
}

static GSList *
gb_import_druid_get_locations_impl (GbImportDruid *d)
{
	g_warning ("Should not be reached");
	return NULL;
}

static GSList *
gb_import_druid_get_locations (GbImportDruid *d)
{
	return GB_IMPORT_DRUID_GET_CLASS (d)->get_locations (d);
}

static GbIO *
gb_import_druid_get_io_impl (GbImportDruid *d)
{
	g_warning ("Should not be reached");
	return NULL;
}

static GbIO *
gb_import_druid_get_io (GbImportDruid *d)
{
	return GB_IMPORT_DRUID_GET_CLASS (d)->get_io (d);
}

static void
gb_import_druid_page_3_prepare_cb (GnomeDruidPage *druid_page, 
				   GtkWidget *druid,
				   GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;

	if (p->location)
	{
		GbIO *io = gb_import_druid_get_io (d);
		if (p->imported)
		{
			g_object_unref (p->imported);
		}
		
		p->imported = gb_io_load_from_file (io, p->location);
			
		g_object_unref (io);
	}
		
	if (p->imported)
	{
		GbTreeModel *tm = gb_tree_model_new_for_set (p->imported);
		gb_tree_view_set_model (p->imported_list, tm);
		g_object_unref (tm);
	}
	else
	{
		GbBookmarkSet *dummy_set = gb_bookmark_set_new ();
		GbFolder *dummy_root = gb_folder_new (dummy_set, _("No bookmarks have been imported."));
		GbTreeModel *tm;
		gb_bookmark_set_set_root (dummy_set, dummy_root);
		tm = gb_tree_model_new_for_set (dummy_set);
		gb_tree_view_set_model (p->imported_list, tm);
		g_object_unref (tm);
		g_object_unref (dummy_root);
		p->imported = dummy_set;

		/* TODO: show an error */
	}
	
	if (p->dest)
	{
		GbTreeModel *tm = gb_tree_model_new_for_set (p->dest);
		gb_tree_view_set_model (p->current_list, tm);
		gb_tree_view_set_model (p->second_current_list, tm);
		g_object_unref (tm);

		/* make a backup copy of the current bookmark set */
		if (!p->backup_done)
		{
			if (p->dest->filename)
			{
				GbIO *io = GB_IO (gb_xbel_new ());
				p->backup_filename = g_strconcat (p->dest->filename, ".bak", NULL);
				p->backup_done = gb_io_save_to_file (io, p->dest, p->backup_filename);
				if (!p->backup_done)
				{
					g_free (p->backup_filename);
					p->backup_filename = NULL;
				}
			}
		}
	}
}

static gboolean
gb_import_druid_page_5_prepare_cb_ugly_hack (GtkWidget *w, GdkEventAny *event, GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;
	gtk_widget_show (p->druid->next);
	gtk_widget_hide (p->druid->finish);
	return FALSE;
}

static void
gb_import_druid_page_5_prepare_cb (GnomeDruidPage *druid_page, 
				   GtkWidget *druid,
				   GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;
	gchar *s;

	if (p->backup_done)
	{
		s = g_strdup_printf (_("The importing process has finished. For safety, you have a "
				       "copy of your old bookmark set in %s."),
				     p->backup_filename);
	}
	else
	{
		s = g_strdup_printf (_("The importing process has finished."));
	}
	gnome_druid_page_edge_set_text (GNOME_DRUID_PAGE_EDGE (druid_page), s);
	g_free (s);

	/* FIXME: this is very ugly... */
	g_signal_connect (p->druid->finish, "map_event", 
			  G_CALLBACK (gb_import_druid_page_5_prepare_cb_ugly_hack), d);
}

static void
gb_import_druid_location_list_selection_changed_cb (GtkTreeSelection *ts, GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;
	GtkTreeModel *m;
	GtkTreeIter it;
	
	if (gtk_tree_selection_get_selected (ts, &m, &it))
	{
		GValue val = { 0 };
		const gchar *sel;
		
		gtk_tree_model_get_value (m, &it, 0, &val);
		sel = g_value_get_string (&val);

		if (sel)
		{
			gnome_file_entry_set_filename (GNOME_FILE_ENTRY (p->location_entry), sel);
		}
		g_value_unset (&val);
	}
}

static void
gb_import_druid_location_entry_changed_cb (GtkEditable *e, GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;
	gchar *sel = gtk_editable_get_chars (e, 0, -1);
	g_free (p->location);
	p->location = sel;
}

static void
gb_import_druid_merge_button_clicked_cb (GtkButton *b, GbImportDruid *d)
{
	GbImportDruidPrivate *p = d->priv;
	
	gtk_widget_show (p->auto_merge_page);
	gnome_druid_set_page (p->druid, GNOME_DRUID_PAGE (p->auto_merge_page));

	gb_util_merge_trees (p->dest->root, p->imported->root);
}

