/*
 *  Copyright (C) 2000, 2001, 2002, 2003 Marco Pesenti Gritti
 *  Copyright (C) 2003 Christian Persch
 *  Copyright (C) 2003 Crispin Flowerday
 *
 *  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 "galeon-encoding-dialog.h"
#include "galeon-encodings.h"
#include "galeon-embed.h"
#include "galeon-embed-shell.h"
#include "galeon-shell.h"
#include "gul-state.h"

#include <gtk/gtklabel.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtktreeview.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtkcellrenderertext.h>

#include <gtk/gtktreeview.h>
#include <gtk/gtktreemodel.h>

#include <glib/gi18n.h>
#include <string.h>

enum
{
	COL_NAME,
	COL_ENCODING,
};


enum
{
	PROP_ENCODING_TREEVIEW,
	PROP_AUTOMATIC,
	PROP_MANUAL,
	PROP_WINDOW
};

static const
GaleonDialogProperty properties [] =
{
	{ PROP_ENCODING_TREEVIEW, "encoding_treeview",	NULL, PT_NORMAL, NULL },
	{ PROP_AUTOMATIC,         "automatic_button",	NULL, PT_NORMAL, NULL },
	{ PROP_MANUAL,            "manual_button",	NULL, PT_NORMAL, NULL },
	{ PROP_WINDOW,            "encoding_dialog",	NULL, PT_NORMAL, NULL },
	{ -1 }
};

struct GaleonEncodingDialogPrivate
{
	GaleonEncodings *encodings;
	GaleonWindow *window;
	GaleonEmbed *embed;
	GtkWidget *enc_view;
	gboolean update_tag;
	gint ignore_activation;
	
	/* Hack, see comment in tree_view_selection_changed_cb */
	guint idle_id;
};

void		galeon_encoding_dialog_response_cb	(GtkWidget *widget,
							 gint response,
							 GaleonEncodingDialog *dialog);

static GObjectClass *parent_class = NULL;


static void
sync_encoding_against_embed (GaleonEncodingDialog *dialog)
{
	GaleonEmbed *embed;
	GaleonEncodingPageInfo *page_info;
	const GaleonEncodingInfo *info;
        GtkTreeSelection *selection;
        GtkTreeModel *model;
        GList *rows;
	GtkWidget *button;
	GtkTreeIter iter;

	dialog->priv->update_tag = TRUE;

	embed = galeon_embed_dialog_get_embed (GALEON_EMBED_DIALOG (dialog));
	g_return_if_fail (IS_GALEON_EMBED (embed));

	galeon_embed_get_encoding_info (embed, &page_info);
	if (page_info == NULL) return;

	info = galeon_encodings_get_encoding (dialog->priv->encodings, page_info->encoding);
	if (!info) return;

	/* select the current encoding in the list view */
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->enc_view));
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->enc_view));
	gtk_tree_model_get_iter_first (model, &iter);
	do {
		gchar *encoding;
		gtk_tree_model_get (model, &iter, COL_ENCODING, &encoding, -1);
		if (!strcmp (encoding, page_info->encoding))
		{
			gtk_tree_selection_select_iter (selection, &iter);
			g_free (encoding);
			break;
		}
		g_free (encoding);
	} while (gtk_tree_model_iter_next (model, &iter));

	/* scroll the view so the active encoding is visible */
        rows = gtk_tree_selection_get_selected_rows (selection, &model);
        if (rows != NULL)
	{
		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->priv->enc_view),
					      (GtkTreePath *) rows->data,
					      NULL, /* column */
					      TRUE,
					      0.5,
					      0.0);
		g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
		g_list_free (rows);
	}

	button = galeon_dialog_get_control (GALEON_DIALOG (dialog), 
					    page_info->forced ? PROP_MANUAL : PROP_AUTOMATIC);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);

	galeon_encoding_page_info_free (page_info);

	dialog->priv->update_tag = FALSE;
}

/* Re-sync the dialog when the embed stops loading
 */
static void
embed_net_stop_cb (GaleonEmbed *embed, GaleonEncodingDialog *dialog)
{
	if (dialog->priv->ignore_activation)
	{
		dialog->priv->ignore_activation--;
		return;
	}

	sync_encoding_against_embed (dialog);
}


/** Re-sync the dialog when the embed changes 
 */
static void
sync_embed_cb (GaleonEncodingDialog *dialog, GParamSpec *pspec, gpointer dummy)
{
	GaleonEmbed *embed;
	embed = galeon_embed_dialog_get_embed (GALEON_EMBED_DIALOG (dialog));

	if (dialog->priv->embed != NULL)
	{
		g_signal_handlers_disconnect_by_func (dialog->priv->embed,
						      G_CALLBACK (embed_net_stop_cb),
						      dialog);
	}

	g_signal_connect (G_OBJECT (embed), "ge_net_stop",
			  G_CALLBACK (embed_net_stop_cb), dialog);
	dialog->priv->embed = embed;

	sync_encoding_against_embed (dialog);

}

/* Re-sync the current emebed when it changes, this will cause the
 * sync_embed_cb to be fired, hence syncing the dialog
 */
static void
sync_active_tab (GaleonWindow *window, GaleonEmbed *oldembed, GaleonEmbed *newembed,
		 GaleonEncodingDialog *dialog)
{
	g_signal_handlers_disconnect_by_func (oldembed,
					      G_CALLBACK (embed_net_stop_cb),
					      dialog);
	g_object_set (G_OBJECT (dialog), "GaleonEmbed", newembed, NULL);
}

static void
sync_parent_window_cb (GaleonEncodingDialog *dialog, GParamSpec *pspec, gpointer dummy)
{
	GaleonWindow *window;
	GaleonEmbed *embed;
	GValue value = { 0, };

	g_return_if_fail (dialog->priv->window == NULL);

	g_value_init (&value, GTK_TYPE_WIDGET);
	g_object_get_property (G_OBJECT (dialog), "ParentWindow", &value);
	window = GALEON_WINDOW (g_value_get_object (&value));
	g_value_unset (&value);

	g_return_if_fail (IS_GALEON_WINDOW (window));

	dialog->priv->window = window;

	embed = galeon_window_get_active_embed (dialog->priv->window);
	g_object_set (G_OBJECT (dialog), "GaleonEmbed", embed, NULL);

	g_signal_connect (G_OBJECT (window), "active_embed_changed",
			  G_CALLBACK (sync_active_tab), dialog);
}

static void
activate_choice (GaleonEncodingDialog *dialog, const gchar *selected)
{
	GaleonEmbed *embed;
	GaleonEncodingPageInfo *info;
	GtkWidget *button;

	embed = galeon_embed_dialog_get_embed (GALEON_EMBED_DIALOG (dialog));
	g_return_if_fail (IS_GALEON_EMBED (embed));

	galeon_embed_get_encoding_info (embed, &info);
	if (info == NULL) return;

	if (!selected)
	{
		/* only unset if it was forced before */
		if (info->forced)
		{
			galeon_embed_set_encoding (embed, "");
		}
	}
	else
	{
		/* only force it if it's different from active */
		if (info->encoding && strcmp (info->encoding, selected) != 0)
		{
			galeon_embed_set_encoding (embed, selected);

			galeon_encodings_add_recent (dialog->priv->encodings, selected);
			button = galeon_dialog_get_control (GALEON_DIALOG (dialog), 
							    PROP_MANUAL);
			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
		}
	}

	galeon_encoding_page_info_free (info);
}

static void
automatic_toggled_cb (GtkToggleButton *button, GaleonEncodingDialog *dialog)
{
	if (dialog->priv->update_tag == TRUE) return;

	if (gtk_toggle_button_get_active (button))
	{
		activate_choice (dialog, NULL);
	}
}

static gboolean
tree_view_selection_changed_idle_cb (GaleonEncodingDialog *dialog)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkTreeSelection *selection;

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->enc_view));
	if (gtk_tree_selection_get_selected (selection, &model, &iter))
	{
		gchar *encoding;
		gtk_tree_model_get (model, &iter, COL_ENCODING, &encoding, -1);

		/* No need to update the treeview when the net_stop signal 
		 * gets called */
		dialog->priv->ignore_activation++;
		activate_choice (dialog, encoding);

		g_free (encoding);
	}

	dialog->priv->idle_id = 0;
	return FALSE;
}

static void
tree_view_selection_changed_cb (GtkTreeSelection *selection, 
				GaleonEncodingDialog *dialog)
{
	if (dialog->priv->update_tag == TRUE) return;

	/* Ok, so this officially sucks, the first click in the
	 * treeview emits a "changed" signal for the first row
	 * and then immediately a "changed" signal for the real row
	 * See bug: http://bugzilla.gnome.org/show_bug.cgi?id=94837
	 *
	 * It seem the work-around is to use an idle handler to 
	 * ensure that the selection is only handled once.
	 */
	if (!dialog->priv->idle_id)
	{
		dialog->priv->idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
				   (GSourceFunc)tree_view_selection_changed_idle_cb,
				   dialog, NULL);
	}
}


static void
tree_view_row_activated_cb (GtkTreeView *treeview, GtkTreePath *path,
			    GtkTreeViewColumn *arg2,
			    GaleonEncodingDialog *dialog)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkWidget *button;
	gchar *encoding;

	if (dialog->priv->update_tag == TRUE) return;

	model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));

	gtk_tree_model_get_iter (model, &iter, path);
	gtk_tree_model_get (model, &iter, COL_ENCODING, &encoding, -1);

	button = galeon_dialog_get_control (GALEON_DIALOG (dialog), PROP_MANUAL);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);

	activate_choice (dialog, encoding);

	g_free (encoding);

	/* Get rid of the dialog */
	g_object_unref (dialog);
}

void
galeon_encoding_dialog_response_cb (GtkWidget *widget,
				    gint response,
				    GaleonEncodingDialog *dialog)
{
	g_object_unref (dialog);
}


static int
compare_encodings (GaleonEncodingInfo *a, GaleonEncodingInfo *b)
{
	return g_utf8_collate (a->title, b->title);
}


static void
galeon_encoding_dialog_init (GaleonEncodingDialog *dialog)
{
	GtkWidget *treeview, *button, *window;
        GtkListStore *liststore;
        GtkCellRenderer *renderer;
        GtkTreeViewColumn *column;
	GtkTreeSelection *selection;
	GaleonEmbedShell *embed_shell;
	GList *list, *l;

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

	embed_shell = galeon_shell_get_embed_shell (galeon_shell);
	galeon_embed_shell_get_encodings (embed_shell, &(dialog->priv->encodings));

	dialog->priv->update_tag = FALSE;

	galeon_dialog_construct (GALEON_DIALOG (dialog),
				 properties,
				 "galeon.glade",
				 "encoding_dialog");

	treeview = galeon_dialog_get_control (GALEON_DIALOG (dialog), PROP_ENCODING_TREEVIEW);

	/* Setup the List View */
        liststore = gtk_list_store_new (2,
                                        G_TYPE_STRING,
                                        G_TYPE_STRING);
        gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL(liststore));
        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
	gtk_tree_selection_set_mode (selection,
				     GTK_SELECTION_SINGLE);

	renderer = gtk_cell_renderer_text_new ();
        gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
                                                     COL_NAME,
						     _("Encoding"),
                                                     renderer,
                                                     "text", COL_NAME,
                                                     NULL);
        column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), COL_NAME);
        gtk_tree_view_column_set_resizable (column, FALSE);
        gtk_tree_view_column_set_reorderable (column, FALSE);

	button = galeon_dialog_get_control (GALEON_DIALOG (dialog), PROP_AUTOMATIC);
	gtk_label_set_use_markup (GTK_LABEL (GTK_BIN (button)->child), TRUE);
	g_signal_connect (button, "toggled",
			  G_CALLBACK (automatic_toggled_cb), dialog);

	button = galeon_dialog_get_control (GALEON_DIALOG (dialog), PROP_MANUAL);
	gtk_label_set_use_markup (GTK_LABEL (GTK_BIN (button)->child), TRUE);

	/* Populate the List */
	list = galeon_encodings_get_encodings (dialog->priv->encodings, LG_ALL);
	list = g_list_sort (list, (GCompareFunc) compare_encodings);
	for (l = list ; l ; l = l->next)
	{
		GtkTreeIter iter;
		GaleonEncodingInfo *info = (GaleonEncodingInfo*)(l->data);

		gtk_list_store_append (liststore, &iter);
		gtk_list_store_set (liststore, &iter,
				    COL_NAME, _(info->title),
				    COL_ENCODING, info->encoding,
				    -1);
	}
	g_list_free (list);

	dialog->priv->enc_view = treeview;

	window = galeon_dialog_get_control (GALEON_DIALOG (dialog), PROP_WINDOW);
	gul_state_monitor_window (window, "Encoding Dialog", 330, 410);

	g_signal_connect (G_OBJECT (selection), "changed",
			  G_CALLBACK (tree_view_selection_changed_cb),
			  dialog);
	g_signal_connect (G_OBJECT (treeview), "row-activated",
			  G_CALLBACK (tree_view_row_activated_cb),
			  dialog);

	g_signal_connect (G_OBJECT (dialog), "notify::ParentWindow",
			  G_CALLBACK (sync_parent_window_cb), NULL);
	g_signal_connect (G_OBJECT (dialog), "notify::GaleonEmbed",
			  G_CALLBACK (sync_embed_cb), NULL);
}

static void
galeon_encoding_dialog_finalize (GObject *object)
{
	GaleonEncodingDialog *dialog = GALEON_ENCODING_DIALOG (object);

	if (dialog->priv->window != NULL)
	{
		g_signal_handlers_disconnect_by_func (dialog->priv->window,
						      G_CALLBACK (sync_active_tab),
						      dialog);
	}

	if (dialog->priv->embed != NULL)
	{
		g_signal_handlers_disconnect_by_func (dialog->priv->embed,
						      G_CALLBACK (embed_net_stop_cb),
						      dialog);
	}

	if (dialog->priv->idle_id)
	{
		g_source_remove (dialog->priv->idle_id);
	}

	g_free (dialog->priv);

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

static void
galeon_encoding_dialog_class_init (GaleonEncodingDialogClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	object_class->finalize = galeon_encoding_dialog_finalize;
}
		
GaleonEncodingDialog *
galeon_encoding_dialog_new (GaleonWindow *parent)
{
	return g_object_new (GALEON_TYPE_ENCODING_DIALOG,
			     "ParentWindow", parent,
			     NULL);
}

GType
galeon_encoding_dialog_get_type (void)
{
	static GType galeon_type_encoding_dialog = 0;

	if (galeon_type_encoding_dialog == 0)
	{
		static const GTypeInfo our_info =
		{
			sizeof (GaleonEncodingDialogClass),
			NULL, /* base_init */
			NULL, /* base_finalize */
			(GClassInitFunc) galeon_encoding_dialog_class_init,
			NULL,
			NULL, /* class_data */
			sizeof (GaleonEncodingDialog),
			0, /* n_preallocs */
			(GInstanceInitFunc) galeon_encoding_dialog_init
		};

		galeon_type_encoding_dialog = g_type_register_static (GALEON_EMBED_DIALOG_TYPE,
								    "GaleonEncodingDialog",
								    &our_info, 0);
	}
	
	return galeon_type_encoding_dialog;
}
