/*
 *  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-export-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 "gul-general.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 _GbExportDruidPrivate {

	GbBookmarkSet *bset;
	
	GtkWidget *window;
	GnomeDruid *druid;

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

	GbTreeView *exported_list;
	GbTreeView *current_list;
	GbTreeView *second_current_list;

	gboolean backup_done;
	gchar *backup_filename;
};

/**
 * Private functions, only availble from this file
 */
static void		gb_export_druid_class_init		(GbExportDruidClass *klass);
static void		gb_export_druid_init			(GbExportDruid *d);
static void		gb_export_druid_init_widgets		(GbExportDruid *d);
static void		gb_export_druid_init_location_widgets 	(GbExportDruid *d);
static void		gb_export_druid_finalize_impl		(GObject *o);
static const char *	gb_export_druid_get_string		(GbExportDruid *d, const char *string_id);
static const char *	gb_export_druid_get_string_impl		(GbExportDruid *d, const char *string_id);
static GSList *		gb_export_druid_get_locations		(GbExportDruid *d);
static GSList *		gb_export_druid_get_locations_impl	(GbExportDruid *d);
static GbIO *		gb_export_druid_get_io			(GbExportDruid *d);
static GbIO *		gb_export_druid_get_io_impl		(GbExportDruid *d);
static void 		gb_export_druid_page_5_prepare_cb	(GnomeDruidPage *druid_page, 
								 GtkWidget *druid,
								 GbExportDruid *d);
static void		gb_export_druid_location_list_selection_changed_cb (GtkTreeSelection *ts, 
									    GbExportDruid *d);
static void		gb_export_druid_location_entry_changed_cb (GtkEditable *e, GbExportDruid *d);


static gpointer g_object_class;

/**
 * ExportDruid object
 */

MAKE_GET_TYPE (gb_export_druid, "GbExportDruid", GbExportDruid, gb_export_druid_class_init, 
	       gb_export_druid_init, G_TYPE_OBJECT);

static void
gb_export_druid_class_init (GbExportDruidClass *klass)
{
	klass->get_string = gb_export_druid_get_string_impl;
	klass->get_locations = gb_export_druid_get_locations_impl;
	klass->get_io = gb_export_druid_get_io_impl;
	G_OBJECT_CLASS (klass)->finalize = gb_export_druid_finalize_impl;
	g_object_class = g_type_class_peek_parent (klass);
}

static void 
gb_export_druid_init (GbExportDruid *e)
{
	GbExportDruidPrivate *p = g_new0 (GbExportDruidPrivate, 1);
	e->priv = p;
	
}

static void
gb_export_druid_finalize_impl (GObject *o)
{
	GbExportDruid *e = GB_EXPORT_DRUID (o);
	GbExportDruidPrivate *p = e->priv;

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

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

}

static void
gb_export_druid_init_widgets (GbExportDruid *d)
{
	GbExportDruidPrivate *p = d->priv;
	GnomeDruidPage *page;
	GtkWidget *w;

	g_return_if_fail (GB_IS_BOOKMARK_SET (p->bset));
	g_return_if_fail (p->window == NULL);
	
	p->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_resizable (GTK_WINDOW (p->window), FALSE);
	gtk_window_set_title (GTK_WINDOW (p->window), 
			      gb_export_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_export_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 export a bookmarks file."
					  "It will make a backup of any overwritten file."));

	/* 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_export_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 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 Exported"));
	gnome_druid_append_page (p->druid, page);

	g_signal_connect (page, "prepare", G_CALLBACK (gb_export_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_export_druid_init_location_widgets (GbExportDruid *d)
{
	GbExportDruidPrivate *p = d->priv;
	GSList *locs;
	GSList *li;
	GtkTreeSelection *sel;

	p->location_entry = gnome_file_entry_new ("galeon-bookamrks-export-locations",
						  _("Choose a file to export"));
	/* 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_export_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_export_druid_location_list_selection_changed_cb), d);

	locs = gb_export_druid_get_locations (d);

	for (li = locs; li; li = li->next)
	{
		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));
}

void
gb_export_druid_show (GbExportDruid *d)
{
	GbExportDruidPrivate *p = d->priv;

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

void
gb_export_druid_set_bookmark_set (GbExportDruid *d, GbBookmarkSet *bs)
{
	GbExportDruidPrivate *p = d->priv;

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

	p->bset = bs;

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

}

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

static const char *
gb_export_druid_get_string (GbExportDruid *d, const char *string_id)
{
	return GB_EXPORT_DRUID_GET_CLASS (d)->get_string (d, string_id);
}

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

static GSList *
gb_export_druid_get_locations (GbExportDruid *d)
{
	return GB_EXPORT_DRUID_GET_CLASS (d)->get_locations (d);
}

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

static GbIO *
gb_export_druid_get_io (GbExportDruid *d)
{
	return GB_EXPORT_DRUID_GET_CLASS (d)->get_io (d);
}

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

static void
gb_export_druid_page_5_prepare_cb (GnomeDruidPage *druid_page, 
				   GtkWidget *druid,
				   GbExportDruid *d)
{
	GbExportDruidPrivate *p = d->priv;
	gchar *s;
	gboolean err = TRUE;

	if (p->location)
	{
		GbIO *io = gb_export_druid_get_io (d);
		
		if (!p->backup_done)
		{
			p->backup_filename = g_strconcat (p->location, ".bak", NULL);
			if (gul_copy_file (p->location, p->backup_filename))
			{
				p->backup_done = TRUE;
			}
			else
			{
				g_free (p->backup_filename);
				p->backup_filename = NULL;
			}
		}

		if (p->backup_done || !g_file_test (p->location, G_FILE_TEST_EXISTS))
		{
			err = ! gb_io_save_to_file (io, p->bset, p->location);
		}
			
		g_object_unref (io);
	}

	if (!err)
	{
		if (p->backup_done)
		{
			s = g_strdup_printf (_("The exporting 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 exporting process has finished."));
		}
	}
	else
	{
		s = g_strdup (_("There has been an error exporting your bookmarks."));
	}

	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_export_druid_page_5_prepare_cb_ugly_hack), d);
}

static void
gb_export_druid_location_list_selection_changed_cb (GtkTreeSelection *ts, GbExportDruid *d)
{
	GbExportDruidPrivate *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_export_druid_location_entry_changed_cb (GtkEditable *e, GbExportDruid *d)
{
	GbExportDruidPrivate *p = d->priv;
	gchar *sel = gtk_editable_get_chars (e, 0, -1);
	g_free (p->location);
	p->location = sel;
	p->backup_done = FALSE;
	g_free (p->backup_filename);
	p->backup_filename = NULL;
}

