/*
 *  Copyright (C) 2002  Ricardo Fernández Pascual
 *  Copyright (C) 2004  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 <glib/gi18n.h>

#include <gtk/gtkuimanager.h>
#include <gtk/gtkactiongroup.h>
#include <gtk/gtkradioaction.h>

#include "galeon-window.h"
#include "galeon-tab.h"
#include "galeon-embed-utils.h"
#include "galeon-css-menu.h"

#include "gul-gobject-misc.h"
#include "galeon-marshal.h"
#include "galeon-debug.h"
#include "gul-string.h"
#include "gul-gui.h"

/**
 * Private data
 */
struct _GaleonCssMenuPrivate
{
	GaleonWindow *window;
	GtkUIManager *merge;
	GaleonEmbed *embed;

	GtkActionGroup *item_action_group;
	guint merge_id;

	GtkActionGroup *menu_action_group;
	guint menu_merge_id;

	guint rebuild_timeout;
	gboolean block_signals;
};

enum
{
	PROP_0,
	PROP_WINDOW
};

static void galeon_css_menu_rebuild (GaleonCssMenu *menu);

static gpointer g_object_class;

#define CSS_MENU_PLACEHOLDER_PATH       "/menubar/View/ViewStylesMenuPlaceholder"
#define STYLESHEET_KEY                  "galeon-stylesheet"
#define USERSTYLE_KEY			"galeon-user-style"
#define REBUILD_TIMEOUT 700

static void
activate_stylesheet_cb (GtkAction *action, GaleonCssMenu *menu)
{
	GaleonCssMenuPrivate *p = menu->priv;
	EmbedStyleSheet *style;

	if (menu->priv->block_signals) return;

	g_return_if_fail (IS_GALEON_EMBED (p->embed));

	style = g_object_get_data (G_OBJECT (action), STYLESHEET_KEY);

	if (style->type == STYLESHEET_USER)
	{
		EmbedStyleSheet *overrideStyle =
			g_object_get_data(G_OBJECT(p->embed), USERSTYLE_KEY);
		if (overrideStyle)
		{
			galeon_embed_remove_user_stylesheet(p->embed,
							    overrideStyle);
		}
		galeon_embed_apply_user_stylesheet(p->embed, style->name,
						   &overrideStyle);
		g_object_set_data_full(G_OBJECT(p->embed), USERSTYLE_KEY,
		                       overrideStyle,
				       (GDestroyNotify)galeon_embed_stylesheet_free);
	}
	else if (style->type == STYLESHEET_USER_NONE)
	{
		EmbedStyleSheet *overrideStyle =
			g_object_get_data(G_OBJECT(p->embed), USERSTYLE_KEY);
		if (overrideStyle)
		{
			galeon_embed_remove_user_stylesheet(p->embed,
							    overrideStyle);
			g_object_set_data(G_OBJECT(p->embed), USERSTYLE_KEY,
					  NULL);
		}
	}
	else
	{
		galeon_embed_set_stylesheet (p->embed, style);
	}
}

static GtkAction *
create_stylesheet_action (GaleonCssMenu *menu, EmbedStyleSheet *style)
{
	GtkAction  *action;
	gchar *name, *tooltip, *label;

	label = gul_string_double_underscores (style->name);

	switch (style->type)
	{
	case STYLESHEET_NONE:
		tooltip = g_strdup (_("Render the page without using a style"));
		break;
	case STYLESHEET_BASIC:
		tooltip = g_strdup (_("Render the page using the default style"));
		break;
	case STYLESHEET_USER_NONE:
		tooltip = g_strdup (_("Do not apply a user style to the page"));
		break;
	case STYLESHEET_USER:
		tooltip = g_strdup_printf (_("Apply the user style from \"%s\" to the page"),
				    style->name);
		break;
	default:
		tooltip = g_strdup_printf (_("Render the page using the \"%s\" style"), 
					   style->name);
		break;
	}

	name = g_strdup_printf ("GaleonCSS%sAction", style->name);
	action = g_object_new (GTK_TYPE_RADIO_ACTION,
			       "name", name,
			       "label", label,
			       "tooltip", tooltip,
			       NULL);

	g_object_set_data_full (G_OBJECT(action), STYLESHEET_KEY, style,
				(GDestroyNotify)galeon_embed_stylesheet_free);

	g_signal_connect_object (action, "activate",
				 G_CALLBACK (activate_stylesheet_cb),
				 menu, 0);

	gtk_action_group_add_action (menu->priv->item_action_group, action);
	g_object_unref (action);

	g_free (name);
	g_free (label);
	g_free (tooltip);
	return action;
}

static void
galeon_css_menu_rebuild (GaleonCssMenu *menu)
{
	GaleonCssMenuPrivate *p = menu->priv;
	GList *stylesheets = 0, *user_styles = 0, *l;
	GtkAction *action;
	gresult result;
	EmbedStyleSheet *current = NULL, *user_current = NULL;
	GSList *radio_group = NULL, *user_group = NULL;

	if (p->merge_id > 0)
	{
		gtk_ui_manager_remove_ui (p->merge, p->merge_id);
		gtk_ui_manager_ensure_update (p->merge);
		p->merge_id = 0;
	}

	if (p->item_action_group)
	{
		gtk_ui_manager_remove_action_group (p->merge, p->item_action_group);
		g_object_unref (p->item_action_group);
		p->item_action_group = 0;
	}

	LOG ("Rebuilding stylesheet menu");

	result = galeon_embed_get_stylesheets (p->embed, &stylesheets);
	if (result != G_OK) stylesheets = NULL;

	result = galeon_embed_get_selected_stylesheet (p->embed, &current);
	if (result != G_OK) current = NULL;

	user_styles = galeon_embed_utils_get_user_stylesheets();

	user_current = g_object_get_data(G_OBJECT(p->embed), USERSTYLE_KEY);

	p->block_signals = TRUE;

	/* Create the new action group */
	p->item_action_group = 
		gtk_action_group_new ("StylesheetMenuDynamicActions");
	gtk_action_group_set_translation_domain (p->item_action_group, NULL);
	gtk_ui_manager_insert_action_group (p->merge, p->item_action_group, 0);

	p->merge_id = gtk_ui_manager_new_merge_id (p->merge);

	action = gtk_action_group_get_action (p->menu_action_group,
					      "StyleMenuAction");
	g_object_set (G_OBJECT (action), "sensitive",
	              (stylesheets != NULL || user_styles != NULL), NULL);
	
	for (l = stylesheets; l ; l = l->next)
	{
		gchar name[128];
		EmbedStyleSheet *style = (EmbedStyleSheet*) (l->data);
		action = create_stylesheet_action (menu, style);

		g_snprintf (name, sizeof(name), "%sItem", gtk_action_get_name (action));

		gtk_ui_manager_add_ui (p->merge, p->merge_id,
				       CSS_MENU_PLACEHOLDER_PATH "/StyleMenu",
				       name, gtk_action_get_name (action),
				       GTK_UI_MANAGER_MENUITEM, FALSE);

		/* Make sure all widgets are in the same radio_group */
		gtk_radio_action_set_group (GTK_RADIO_ACTION (action), radio_group);
		radio_group = gtk_radio_action_get_group (GTK_RADIO_ACTION (action));

		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), 
					      galeon_embed_stylesheet_compare (style, current));
	}

	if (stylesheets && user_styles)
	{
		gtk_ui_manager_add_ui(p->merge, p->merge_id,
				      CSS_MENU_PLACEHOLDER_PATH "/StyleMenu",
				      "usersep", NULL,
				      GTK_UI_MANAGER_SEPARATOR, FALSE);
	}

	for (l = user_styles; l ; l = l->next)
	{
		gchar name[128];
		EmbedStyleSheet *style = (EmbedStyleSheet*)(l->data);
		action = create_stylesheet_action(menu, style);

		g_snprintf(name, sizeof(name), "%sItem", gtk_action_get_name(action));

		gtk_ui_manager_add_ui(p->merge, p->merge_id,
				      CSS_MENU_PLACEHOLDER_PATH "/StyleMenu",
				      name, gtk_action_get_name(action),
				      GTK_UI_MANAGER_MENUITEM, FALSE);

		/* Make sure all widgets are in the same radio_gruop */
		gtk_radio_action_set_group(GTK_RADIO_ACTION(action), user_group);
		user_group = gtk_radio_action_get_group(GTK_RADIO_ACTION (action));

		gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), 
					     galeon_embed_stylesheet_compare(style,
									     user_current));
	}

	/* Don't need to free the contents, they are held as weak ref's
	 * against their action */
	g_list_free (stylesheets);
	galeon_embed_stylesheet_free (current);
	g_list_free(user_styles);

	p->block_signals = FALSE;

	gtk_ui_manager_ensure_update (p->merge);
}

static gboolean
galeon_css_menu_rebuild_timeout_cb (gpointer data)
{
	GaleonCssMenu *gm = data;
	GaleonCssMenuPrivate *p = gm->priv;
	LOG ("GaleonCssMenu rebuild timeout");

	p->rebuild_timeout = 0;

	galeon_css_menu_rebuild (data);
	return FALSE;
}

static void 
galeon_css_menu_rebuild_with_timeout (GaleonCssMenu *gm) 
{
	GaleonCssMenuPrivate *p = gm->priv;

	LOG ("galeon_css_menu_rebuild_with_timeout");

	if (p->rebuild_timeout)
	{
		g_source_remove (p->rebuild_timeout);
	}
	
	p->rebuild_timeout = g_timeout_add (REBUILD_TIMEOUT, 
					    galeon_css_menu_rebuild_timeout_cb, gm);
}

static void
galeon_css_menu_rebuild_for_reload(GaleonCssMenu *gm)
{
	GaleonCssMenuPrivate *p = gm->priv;

	g_object_set_data(G_OBJECT(p->embed), USERSTYLE_KEY, NULL);

	galeon_css_menu_rebuild_with_timeout(gm);
}

static void
galeon_css_menu_set_embed (GaleonCssMenu *menu, GaleonEmbed *embed)
{
	GaleonCssMenuPrivate *p = menu->priv;

	if (p->embed)
	{
		g_signal_handlers_disconnect_matched (p->embed, G_SIGNAL_MATCH_DATA, 
						      0, 0, NULL, NULL, menu);
		g_object_unref (p->embed);
	}

	p->embed = embed;
	if (p->embed)
	{
		g_object_ref (p->embed);
		g_signal_connect_object (p->embed, "ge_net_stop",
					 G_CALLBACK (galeon_css_menu_rebuild_for_reload),
					 menu, G_CONNECT_SWAPPED);
	}

	galeon_css_menu_rebuild_with_timeout (menu);
}


static void
embed_changed_cb (GaleonWindow *window, GaleonEmbed *old_embed, 
		  GaleonEmbed *new_embed, GaleonCssMenu *menu)
{
	galeon_css_menu_set_embed (menu, new_embed);
}


static GtkActionEntry entries[] = {
	{ "StyleMenuAction", NULL, N_("St_yles"), NULL,
	  N_("Select a different style for this page"), NULL }
};
static guint n_entries = G_N_ELEMENTS(entries);

static void
galeon_css_menu_set_window (GaleonCssMenu *menu, GaleonWindow *window)
{
	GaleonCssMenuPrivate *p = menu->priv;
	GaleonEmbed *embed;
	GtkAction *action;

	p->window = window;
	
	/* Create the Action Group */
	p->merge = g_object_ref (GTK_UI_MANAGER (window->merge));
	menu->priv->menu_action_group = gtk_action_group_new ("CssMenuActions");
	gtk_action_group_set_translation_domain (menu->priv->menu_action_group, NULL);
	gtk_ui_manager_insert_action_group(p->merge, p->menu_action_group, 0);
	gtk_action_group_add_actions (p->menu_action_group, entries,
				      n_entries, window);

	action = gtk_action_group_get_action (p->menu_action_group, "StyleMenuAction");
	g_object_set (G_OBJECT (action), "hide_if_empty", FALSE, NULL);

	p->menu_merge_id = gtk_ui_manager_new_merge_id (p->merge);

	gtk_ui_manager_add_ui (p->merge, p->menu_merge_id,
			       CSS_MENU_PLACEHOLDER_PATH,
			       "StyleMenu",
			       "StyleMenuAction",
			       GTK_UI_MANAGER_MENU, FALSE);

	/* Attach the signals to the window */
	g_signal_connect_object (window, "active-embed-changed",
				 G_CALLBACK (embed_changed_cb),
				 menu, 0);
	embed = galeon_window_get_active_embed (window);

	galeon_css_menu_set_embed (menu, embed);
}


static void
galeon_css_menu_set_property (GObject *object,
			      guint prop_id,
			      const GValue *value,
			      GParamSpec *pspec)
{
	GaleonCssMenu *m = GALEON_CSS_MENU (object);

	switch (prop_id)
	{
	case PROP_WINDOW:
		{
			galeon_css_menu_set_window 
				(m, GALEON_WINDOW (g_value_get_object (value)));
		}
		break;
	default: 
		break;
	}
}

static void
galeon_css_menu_get_property (GObject *object,
			      guint prop_id,
			      GValue *value,
			      GParamSpec *pspec)
{
	GaleonCssMenu *m = GALEON_CSS_MENU (object);
	
	switch (prop_id)
	{
	case PROP_WINDOW:
		g_value_set_object (value, m->priv->window);
		break;
	default: 
		break;
	}
}


static void
galeon_css_menu_finalize_impl(GObject *o)
{
	GaleonCssMenu *gm = GALEON_CSS_MENU(o);
	GaleonCssMenuPrivate *p = gm->priv;

	LOG ("galeon_css_menu_finalize_impl");

	if (p->merge_id > 0)
	{
		gtk_ui_manager_remove_ui (p->merge, p->merge_id);
	}

	if (p->menu_merge_id > 0)
	{
		gtk_ui_manager_remove_ui (p->merge, p->menu_merge_id);
	}

	if (p->menu_action_group)
	{
		gtk_ui_manager_remove_action_group
			(p->merge, p->menu_action_group);
		g_object_unref (p->menu_action_group);
	}

	if (p->item_action_group)
	{
		gtk_ui_manager_remove_action_group
			(p->merge, p->item_action_group);
		g_object_unref (p->item_action_group);
	}

	if (p->rebuild_timeout)
	{
		g_source_remove (p->rebuild_timeout);
	}

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

	gtk_ui_manager_ensure_update (p->merge);
	g_object_unref (p->merge);
	g_free(p);

	G_OBJECT_CLASS(g_object_class)->finalize (o);
}

static void 
galeon_css_menu_init(GaleonCssMenu *m)
{
	GaleonCssMenuPrivate *p = g_new0 (GaleonCssMenuPrivate, 1);
	m->priv = p;
}

static void
galeon_css_menu_class_init(GaleonCssMenuClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	g_object_class = g_type_class_peek_parent(klass);

	object_class->set_property = galeon_css_menu_set_property;
	object_class->get_property = galeon_css_menu_get_property;
	object_class->finalize = galeon_css_menu_finalize_impl;

	g_object_class_install_property (object_class,
                                         PROP_WINDOW,
                                         g_param_spec_object ("GaleonWindow",
                                                              "GaleonWindow",
                                                              "Parent window",
                                                              GALEON_WINDOW_TYPE,
                                                              G_PARAM_READWRITE |
							      G_PARAM_CONSTRUCT_ONLY));
}


MAKE_GET_TYPE(galeon_css_menu, "GaleonCssMenu", GaleonCssMenu,
	      galeon_css_menu_class_init,
	      galeon_css_menu_init, G_TYPE_OBJECT);


GaleonCssMenu *
galeon_css_menu_new (GaleonWindow *window)
{
	GaleonCssMenu *ret = g_object_new (GALEON_TYPE_CSS_MENU, 
					   "GaleonWindow", window, NULL);
	return ret;
}
