/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  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.
 */

#include "kz-links-dialog.h"

#include <stdio.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>

#include "gobject-utils.h"
#include "kz-bookmark-filter.h"
#include "kz-icons.h"

enum {
	PROP_0,
	PROP_KZ_WINDOW,
	PROP_KZ_EMBED,
	PROP_SELECTED_ONLY
};

enum {
	RESPONSE_SAVE
};

enum {
	TERMINATOR = -1,
	COLUMN_TITLE,
	COLUMN_URI,
	COLUMN_SAVE,
	N_COLUMNS
};

static void     kz_links_dialog_class_init         (KzLinksDialogClass *klass);
static void     kz_links_dialog_init               (KzLinksDialog  *kzlinks);
static GObject *kz_links_dialog_constructor        (GType           type,
						    guint           n_props,
						    GObjectConstructParam *props);
static void     kz_links_dialog_dispose            (GObject        *object);
static void     kz_links_dialog_set_property       (GObject       *object,
						    guint          prop_id,
						    const GValue  *value,
						    GParamSpec    *pspec);
static void     kz_links_dialog_get_property       (GObject       *object,
						    guint          prop_id,
						    GValue        *value,
						    GParamSpec    *pspec);
static void     kz_links_dialog_response           (GtkDialog      *dialog,
						    gint            arg);

static void     cb_parent_destroy                  (GtkWidget      *widget,
						    KzLinksDialog  *kzlinks);
static void     tree_sel_open_selected             (GtkTreeModel   *model,
						    GtkTreePath    *path,
						    GtkTreeIter    *iter,
						    gpointer        data);
static gboolean cb_tree_view_key_press             (GtkWidget      *widget,
						    GdkEventKey    *event,
						    KzLinksDialog  *kzlinks);
static gboolean cb_tree_view_button_press          (GtkWidget      *widget,
						    GdkEventButton *event,
						    KzLinksDialog  *kzlinks);
static void     cb_save_toggled                    (GtkCellRendererToggle *cell,
						    gchar          *path_str,
						    KzLinksDialog  *kzlinks);

static GtkDialogClass *parent_class = NULL;


KZ_OBJECT_GET_TYPE(kz_links_dialog, "KzLinksDialog", KzLinksDialog,
		   kz_links_dialog_class_init, kz_links_dialog_init,
		   GTK_TYPE_DIALOG)

static void
kz_links_dialog_class_init (KzLinksDialogClass *klass)
{
	GObjectClass *gobject_class;
	GtkDialogClass *dialog_class;

	parent_class = g_type_class_peek_parent (klass);

	gobject_class = (GObjectClass *) klass;
	dialog_class  = (GtkDialogClass *) klass;

	/* GtkObject signals */
	gobject_class->constructor  = kz_links_dialog_constructor;
	gobject_class->dispose      = kz_links_dialog_dispose;
	gobject_class->set_property = kz_links_dialog_set_property;
	gobject_class->get_property = kz_links_dialog_get_property;

	/* GtkDialog signals */
	dialog_class->response = kz_links_dialog_response;

	g_object_class_install_property
		(gobject_class,
		 PROP_KZ_WINDOW,
		 g_param_spec_object("kz-window",
				      _("KzWindow"),
				     _("The parent kazehakase window"),
				     KZ_TYPE_WINDOW,
				     G_PARAM_READWRITE |
				     G_PARAM_CONSTRUCT_ONLY));
	g_object_class_install_property
		(gobject_class,
		 PROP_KZ_EMBED,
		 g_param_spec_object("kz-embed",
				     _("KzEmbed"),
				     _("The embed widget to extract links"),
				     KZ_TYPE_EMBED,
				     G_PARAM_READWRITE |
				     G_PARAM_CONSTRUCT_ONLY));
	g_object_class_install_property
		(gobject_class,
		 PROP_SELECTED_ONLY,
		 g_param_spec_boolean("selected-only",
				      _("Selected Only"),
				      _("Whether extract only selected links or not"),
				      FALSE,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));
}


static void
kz_links_dialog_init (KzLinksDialog *kzlinks)
{
	GtkWidget *main_vbox, *scrwin, *tree_view;
	GtkListStore *store;
	GtkCellRenderer *cell;
	GtkTreeViewColumn *col;
	GtkTreeSelection *selection;

	kzlinks->kz            = NULL;
	kzlinks->kzembed       = NULL;
	kzlinks->selected_only = FALSE;

	main_vbox = GTK_DIALOG(kzlinks)->vbox;
	gtk_window_set_default_size(GTK_WINDOW(kzlinks), 600, 450);
	gtk_window_set_icon(GTK_WINDOW(kzlinks), kz_icon);

	gtk_dialog_add_buttons(GTK_DIALOG(kzlinks),
			       GTK_STOCK_OPEN,  GTK_RESPONSE_YES,
			       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
			       GTK_STOCK_SAVE,  RESPONSE_SAVE,
			       NULL);

	/* scrolled window */
	scrwin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
				       GTK_POLICY_AUTOMATIC,
				       GTK_POLICY_AUTOMATIC);
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
					    GTK_SHADOW_IN);
	gtk_box_pack_start(GTK_BOX(main_vbox), scrwin, TRUE, TRUE, 0);
	gtk_widget_show(scrwin);

	/* tree view */
	store = gtk_list_store_new(N_COLUMNS,
				   G_TYPE_STRING,
				   G_TYPE_STRING,
				   G_TYPE_BOOLEAN);
	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);

	/* save column */
	cell = gtk_cell_renderer_toggle_new();
	col = gtk_tree_view_column_new_with_attributes
			(_("Save"), cell, "active", COLUMN_SAVE, NULL);
	g_signal_connect(cell, "toggled",
			 G_CALLBACK(cb_save_toggled), kzlinks);
	gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);

	/* title column */
	cell = gtk_cell_renderer_text_new();
	col = gtk_tree_view_column_new_with_attributes
			(_("Title"), cell, "text", COLUMN_TITLE, NULL);
	gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_fixed_width (col, 200);
	gtk_tree_view_column_set_resizable(col, TRUE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);

	/* uri column */
	cell = gtk_cell_renderer_text_new();
	col = gtk_tree_view_column_new_with_attributes
			(_("URI"), cell, "text", COLUMN_URI, NULL);
	gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_fixed_width (col, 400);
	gtk_tree_view_column_set_resizable(col, TRUE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);

	g_signal_connect(tree_view, "key-press-event",
			 G_CALLBACK(cb_tree_view_key_press), kzlinks);
	g_signal_connect(tree_view, "button-press-event",
			 G_CALLBACK(cb_tree_view_button_press), kzlinks);

	gtk_container_add(GTK_CONTAINER(scrwin), tree_view);
	gtk_widget_show(tree_view);

	kzlinks->scrolled_window = scrwin;
	kzlinks->tree_view       = GTK_TREE_VIEW(tree_view);
	kzlinks->list_store      = store;
}


static GObject*
kz_links_dialog_constructor (GType                  type,
			     guint                  n_props,
			     GObjectConstructParam *props)
{
	GtkWidget *widget;
	KzLinksDialog *kzlinks;
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(parent_class);
	GList *list = NULL, *node;
	gboolean result;
	gchar buf[256];

	object = klass->constructor(type, n_props, props);

	kzlinks = KZ_LINKS_DIALOG(object);
	widget = GTK_WIDGET(kzlinks);

	g_signal_connect(kzlinks->kz, "destroy",
			 G_CALLBACK(cb_parent_destroy), kzlinks);
	g_signal_connect(kzlinks->kzembed, "destroy",
			 G_CALLBACK(cb_parent_destroy), kzlinks);

	g_snprintf(buf, sizeof(buf), _("Extracted links  - %s"),
		   kz_embed_get_title(kzlinks->kzembed));
	gtk_window_set_title(GTK_WINDOW(kzlinks), buf);

	result = kz_embed_get_links(kzlinks->kzembed, &list,
				    kzlinks->selected_only);
	if (!result || !list) return object;

	for (node = list; node; node = g_list_next(node))
	{
		KzBookmark *link = node->data;
		GtkTreeIter iter;
		const gchar *title = kz_bookmark_get_title(link);
		const gchar *uri = kz_bookmark_get_link(link);

		if (!uri || !*uri) continue;

		if (kz_bookmark_filter_out(link)) continue;

		gtk_list_store_append(kzlinks->list_store, &iter);
		gtk_list_store_set(kzlinks->list_store, &iter,
				   COLUMN_TITLE, title,
				   COLUMN_URI,   uri,
				   COLUMN_SAVE,  TRUE,
				   TERMINATOR);
	}

	g_list_foreach(list, (GFunc) g_object_unref, NULL);
	g_list_free(list);

	return object;
}


static void
kz_links_dialog_dispose (GObject *object)
{
	KzLinksDialog *kzlinks = KZ_LINKS_DIALOG(object);

	if (kzlinks->kz)
	{
		g_signal_handlers_disconnect_by_func
			(kzlinks->kz,
			 G_CALLBACK(cb_parent_destroy),
			 kzlinks);
		g_object_unref(kzlinks->kz);
		kzlinks->kz = NULL;
	}

	if (kzlinks->kzembed)
	{
		g_signal_handlers_disconnect_by_func
			(kzlinks->kzembed,
			 G_CALLBACK(cb_parent_destroy),
			 kzlinks);
		g_object_unref(kzlinks->kzembed);
		kzlinks->kzembed = NULL;
	}

	if (kzlinks->list_store)
	{
		g_object_unref(kzlinks->list_store);
		kzlinks->list_store= NULL;
	}

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


static void
kz_links_dialog_set_property (GObject         *object,
			      guint            prop_id,
			      const GValue    *value,
			      GParamSpec      *pspec)
{
	KzLinksDialog *kzlinks = KZ_LINKS_DIALOG(object);
  
	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		kzlinks->kz = g_object_ref(g_value_get_object(value));
		break;
	case PROP_KZ_EMBED:
		kzlinks->kzembed = g_object_ref(g_value_get_object(value));
		break;
	case PROP_SELECTED_ONLY:
		kzlinks->selected_only = g_value_get_boolean(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


static void
kz_links_dialog_get_property (GObject         *object,
			      guint            prop_id,
			      GValue          *value,
			      GParamSpec      *pspec)
{
	KzLinksDialog *kzlinks = KZ_LINKS_DIALOG(object);

	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		g_value_set_object(value, kzlinks->kz);
		break;
	case PROP_KZ_EMBED:
		g_value_set_object(value, kzlinks->kzembed);
		break;
	case PROP_SELECTED_ONLY:
		g_value_set_boolean(value, kzlinks->selected_only);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


GtkWidget *
kz_links_dialog_new (KzWindow *kz, KzEmbed *kzembed, gboolean selected_only)
{
	GtkWidget *widget;

	widget = GTK_WIDGET(g_object_new(KZ_TYPE_LINKS_DIALOG,
					 "kz-window",     kz,
					 "kz-embed",      kzembed,
					 "selected-only", selected_only,
					 NULL));

	return widget;
}


static gchar *last_saved_file = NULL;


static gboolean
kz_links_dialog_save (KzLinksDialog *kzlinks, const gchar *filename)
{
	FILE *fp;
	GtkTreeModel *model = GTK_TREE_MODEL(kzlinks->list_store);
	GtkTreeIter iter;
	gboolean exist;

	fp = fopen(filename, "wt");
	if (!fp)
	{
		GtkWidget *dialog;

		dialog = gtk_message_dialog_new(GTK_WINDOW(kzlinks),
						GTK_DIALOG_MODAL,
						GTK_MESSAGE_ERROR,
						GTK_BUTTONS_OK,
						_("Can't open %s for write!"),
						filename);
		gtk_dialog_run(GTK_DIALOG(dialog));
		gtk_widget_destroy(dialog);

		return FALSE;
	}

	exist = gtk_tree_model_get_iter_first(model, &iter);
	for (; exist; exist = gtk_tree_model_iter_next(model, &iter))
	{
		gchar *url = NULL;
		gboolean save = TRUE;

		gtk_tree_model_get(model, &iter,
				   COLUMN_URI,  &url,
				   COLUMN_SAVE, &save,
				   TERMINATOR);
		if (!url) continue;
		if (!*url || !save)
		{
			g_free(url);
			continue;
		}

		fputs(url, fp);
		fputc('\n', fp);
		g_free(url);
	}

	fclose(fp);

	return TRUE;
}


static void
cb_filedialog_response (GtkDialog *dialog, gint arg, KzLinksDialog *kzlinks)
{
	GtkFileChooserDialog *filedialog = GTK_FILE_CHOOSER_DIALOG(dialog);
	gchar *file = NULL;
	gboolean close = TRUE;

	file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filedialog));

	switch (arg) {
	case GTK_RESPONSE_OK:
		if (file && *file)
			close = kz_links_dialog_save(kzlinks, file);
	case GTK_RESPONSE_CANCEL:
		if (file && *file)
			g_free(last_saved_file);
		last_saved_file = g_strdup(file);
		if (close)
			gtk_widget_destroy(GTK_WIDGET(dialog));
		break;
	default:
		break;
	}

	if (file)
		g_free(file);
}


static void
kz_links_dialog_save_dialog (KzLinksDialog *kzlinks)
{
	GtkWidget *filedialog;

	filedialog = gtk_file_chooser_dialog_new (_("Save to file"),
						  GTK_WINDOW(kzlinks),
						  GTK_FILE_CHOOSER_ACTION_SAVE,
						  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
						  GTK_STOCK_OK, GTK_RESPONSE_OK,
						  NULL);
	gtk_dialog_set_default_response(GTK_DIALOG(filedialog),
					GTK_RESPONSE_OK);
	if (last_saved_file)
	{
		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filedialog),
						  last_saved_file);
	}
	g_signal_connect(filedialog, "response",
			 G_CALLBACK(cb_filedialog_response), kzlinks);
	g_signal_connect(filedialog, "destroy",
			 G_CALLBACK(gtk_main_quit), NULL);
	gtk_grab_add(GTK_WIDGET(filedialog));
	gtk_widget_show(filedialog);
	gtk_main();
}


static void
kz_links_dialog_response (GtkDialog *dialog, gint arg)
{
	KzLinksDialog *kzlinks;
	GtkTreeSelection *selection;

	g_return_if_fail (KZ_IS_LINKS_DIALOG(dialog));

	kzlinks = KZ_LINKS_DIALOG(dialog);
	if (!kzlinks->kz || !kzlinks->kzembed) return;

	switch (arg) {
	case GTK_RESPONSE_YES:
		selection = gtk_tree_view_get_selection(kzlinks->tree_view);
		gtk_tree_selection_selected_foreach
			(selection, tree_sel_open_selected, kzlinks);
		break;
	case GTK_RESPONSE_CLOSE:
		gtk_widget_destroy(GTK_WIDGET(dialog));
		break;
	case RESPONSE_SAVE:
		kz_links_dialog_save_dialog(kzlinks);
		break;
	default:
		break;
	}
}


static void
cb_parent_destroy(GtkWidget *widget, KzLinksDialog *kzlinks)
{
	g_return_if_fail (KZ_IS_LINKS_DIALOG(kzlinks));
	gtk_widget_destroy(GTK_WIDGET(kzlinks));
}


static void
tree_sel_open_selected (GtkTreeModel *model,
			GtkTreePath *path, GtkTreeIter *iter,
			gpointer data)
{
	KzLinksDialog *kzlinks = data;
	gchar *url;

	g_return_if_fail(KZ_IS_LINKS_DIALOG(kzlinks));

	gtk_tree_model_get(GTK_TREE_MODEL(kzlinks->list_store),
			   iter, 1, &url, -1);

	if (url && *url)
		kz_window_open_new_tab_with_parent(kzlinks->kz, url,
						   GTK_WIDGET(kzlinks->kzembed));

	g_free(url);
}


static gboolean
cb_tree_view_button_press (GtkWidget *widget, GdkEventButton *event,
			   KzLinksDialog *kzlinks)
{
	GtkTreeSelection *selection;

	g_return_val_if_fail(KZ_IS_LINKS_DIALOG(kzlinks), FALSE);

	if (event->type == GDK_2BUTTON_PRESS)
	{
		selection = gtk_tree_view_get_selection(kzlinks->tree_view);
		gtk_tree_selection_selected_foreach
			(selection, tree_sel_open_selected, kzlinks);
		return TRUE;
	}

	return FALSE;
}


static gboolean
cb_tree_view_key_press (GtkWidget *widget, GdkEventKey *event,
			KzLinksDialog *kzlinks)
{
	GtkTreeSelection *selection;

	g_return_val_if_fail(KZ_IS_LINKS_DIALOG(kzlinks), FALSE);

	switch (event->keyval) {
	case GDK_Return:
	case GDK_space:
		selection = gtk_tree_view_get_selection(kzlinks->tree_view);
		gtk_tree_selection_selected_foreach
			(selection, tree_sel_open_selected, kzlinks);
		return TRUE;
		break;
	default:
		break;
	}

	return FALSE;
}


static void
cb_save_toggled (GtkCellRendererToggle *cell, gchar *path_str,
		 KzLinksDialog *kzlinks)
{
        GtkTreeView *treeview;
        GtkTreeModel *model;
        GtkTreePath *path;
        GtkTreeIter iter;
	gboolean active;

	g_return_if_fail(KZ_IS_LINKS_DIALOG(kzlinks));

	treeview = GTK_TREE_VIEW(kzlinks->tree_view);
	model = gtk_tree_view_get_model(treeview);
	path = gtk_tree_path_new_from_string(path_str);

	active = gtk_cell_renderer_toggle_get_active(cell);

        gtk_tree_model_get_iter(model, &iter, path);
        gtk_list_store_set(GTK_LIST_STORE(model), &iter,
			   COLUMN_SAVE, !active,
			   TERMINATOR);

        gtk_tree_path_free (path);
}
