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

/*
 *  Copyright (C) 2003 Hiroyuki Ikezoe
 *  Copyright (C) 2003 - 2004 Takuro Ashie
 *  Copyright (C) 2004 Hidetaka Iwai
 *
 *  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-location-entry-action.h"

#include <stdlib.h>
#include <gdk/gdkkeysyms.h>
#include <sys/types.h>

#include "kazehakase.h"
#include "intl.h"
#include "utils/utils.h"
#include "gobject-utils.h"
#include "gtk24backports.h"
#include "kz-mozembed.h"
#include "kz-bookmark.h"
#include "kz-actions.h"

enum {
	PROP_0,
	PROP_KZ_WINDOW,
	PROP_KZ_BOOKMARK
};

static void kz_location_entry_action_class_init       (KzLocationEntryActionClass *class);
static void kz_location_entry_action_init             (KzLocationEntryAction      *action);
static void kz_location_entry_action_dispose          (GObject *obj);
static void kz_location_entry_action_set_property     (GObject           *object,
						       guint              prop_id,
						       const GValue      *value,
						       GParamSpec        *pspec);
static void kz_location_entry_action_get_property     (GObject           *object,
						       guint              prop_id,
						       GValue            *value,
						       GParamSpec        *pspec);

static void kz_location_entry_action_activate         (GtkAction         *action);
static void kz_location_entry_action_connect_proxy    (GtkAction         *action,
						       GtkWidget         *proxy);
static void kz_location_entry_action_disconnect_proxy (GtkAction         *action,
						       GtkWidget         *proxy);

static void     kz_location_entry_action_history_changed (KzHistoryAction *action);
static void     kz_location_entry_action_sync_history    (KzLocationEntryAction *action);
static gboolean cb_entry_key_press                       (GtkWidget *widget,
							  GdkEventKey *event,
							  KzLocationEntryAction *action);
static void     cb_entry_populate_popup         	 (GtkEntry *entry,
						   	  GtkMenu *menu,
	  			    	 	   	  KzLocationEntryAction *action);


static void     set_old_smart_list_settings (KzLocationEntryAction *action);

static KzHistoryActionClass *parent_class = NULL;


KZ_OBJECT_GET_TYPE(kz_location_entry_action,
		   "KzLocationEntryAction",
		   KzLocationEntryAction,
		   kz_location_entry_action_class_init,
		   kz_location_entry_action_init,
		   KZ_TYPE_HISTORY_ACTION)


static void
kz_location_entry_action_class_init (KzLocationEntryActionClass *klass)
{
	GObjectClass *object_class;
	GtkActionClass *action_class;
	KzHistoryActionClass *history_class;

	parent_class  = g_type_class_peek_parent(klass);
	object_class  = G_OBJECT_CLASS(klass);
	action_class  = GTK_ACTION_CLASS(klass);
	history_class = KZ_HISTORY_ACTION_CLASS(klass);

	object_class->set_property     = kz_location_entry_action_set_property;
	object_class->get_property     = kz_location_entry_action_get_property;
	object_class->dispose          = kz_location_entry_action_dispose;

	action_class->activate         = kz_location_entry_action_activate;
	action_class->connect_proxy    = kz_location_entry_action_connect_proxy;
	action_class->disconnect_proxy = kz_location_entry_action_disconnect_proxy;

	history_class->history_changed = kz_location_entry_action_history_changed;

	g_object_class_install_property
		(object_class,
		 PROP_KZ_WINDOW,
		 g_param_spec_object ("kz-window",
				      _("KzWindow"),
				      _("The KzWindow to add a home button"),
				      KZ_TYPE_WINDOW,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));
	g_object_class_install_property
		(object_class,
		 PROP_KZ_BOOKMARK,
		 g_param_spec_object ("kz-bookmark",
				      _("KzBookmark"),
				      _("The Bookmark object"),
				      KZ_TYPE_BOOKMARK,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));
}


static void
kz_location_entry_action_init (KzLocationEntryAction *action)
{
	action->kz = NULL;
	action->bookmark = NULL;
	action->activating = FALSE;
	action->synchronizing = FALSE;
}


static void
kz_location_entry_action_dispose (GObject *obj)
{
	KzLocationEntryAction *action = KZ_LOCATION_ENTRY_ACTION(obj);

	if (action->kz)
	{
		kz_location_entry_action_store_history(action);
		g_object_unref(action->kz);
		action->kz = NULL;
	}

	if (action->bookmark)
	{
		g_object_unref(action->bookmark);
		action->bookmark = NULL;
	}
	if (G_OBJECT_CLASS(parent_class)->dispose)
		G_OBJECT_CLASS(parent_class)->dispose(obj);
}


static void
kz_location_entry_action_set_property (GObject         *object,
				       guint            prop_id,
				       const GValue    *value,
				       GParamSpec      *pspec)
{
	KzLocationEntryAction *action = KZ_LOCATION_ENTRY_ACTION(object);
  
	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		action->kz = g_object_ref(g_value_get_object(value));
		break;
	case PROP_KZ_BOOKMARK:
		action->bookmark = g_object_ref(g_value_get_object(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


static void
kz_location_entry_action_get_property (GObject         *object,
				       guint            prop_id,
				       GValue          *value,
				       GParamSpec      *pspec)
{
	KzLocationEntryAction *action = KZ_LOCATION_ENTRY_ACTION(object);

	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		g_value_set_object(value, action->kz);
		break;
	case PROP_KZ_BOOKMARK:
		g_value_set_object(value, action->bookmark);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


static void
kz_location_entry_action_activate (GtkAction *action)
{
	KzLocationEntryAction *entry_action;
	const gchar *uri;
	gchar *smart_uri;
	gboolean new_tab;
	GdkModifierType state = (GdkModifierType)0;
	gint x, y;
 
	g_return_if_fail(KZ_IS_LOCATION_ENTRY_ACTION(action));

	KZ_LOCATION_ENTRY_ACTION(action)->activating = TRUE;
	if (GTK_ACTION_CLASS(parent_class)->activate)
		GTK_ACTION_CLASS(parent_class)->activate(action);
	KZ_LOCATION_ENTRY_ACTION(action)->activating = FALSE;

	entry_action = KZ_LOCATION_ENTRY_ACTION(action);

	uri = kz_entry_action_get_text(KZ_ENTRY_ACTION(action));
	smart_uri = kz_bookmark_get_smart_uri(entry_action->bookmark, uri);

	gdk_window_get_pointer(NULL, &x, &y, &state);
	
	KZ_CONF_GET("Global", "entry_open_in_new_tab", new_tab, BOOL);
	new_tab = (state & GDK_CONTROL_MASK) ? !new_tab: new_tab;

	if (new_tab)
		kz_window_open_new_tab(entry_action->kz, smart_uri);
	else	
		kz_window_load_url(entry_action->kz, smart_uri);
	g_free(smart_uri);
}


static void
kz_location_entry_action_connect_proxy (GtkAction *action, GtkWidget *proxy)
{
	GtkEntry *entry;

	GTK_ACTION_CLASS (parent_class)->connect_proxy (action, proxy);

	entry = kz_entry_action_get_entry_widget(KZ_ENTRY_ACTION(action), proxy);
	if (entry)
	{
		g_signal_connect(G_OBJECT(entry), "key-press-event",
				 G_CALLBACK(cb_entry_key_press), action);
		g_signal_connect(G_OBJECT(entry), "populate-popup",
				 G_CALLBACK(cb_entry_populate_popup), action);
	}
}


static void
kz_location_entry_action_disconnect_proxy (GtkAction *action, GtkWidget *proxy)
{
	GtkEntry *entry;

	entry = kz_entry_action_get_entry_widget(KZ_ENTRY_ACTION(action), proxy);
	if (entry)
	{
		g_signal_handlers_disconnect_by_func
			(G_OBJECT(entry),
			 G_CALLBACK(cb_entry_key_press),
			 action);
		g_signal_handlers_disconnect_by_func
			(G_OBJECT(entry),
			 G_CALLBACK(cb_entry_populate_popup),
			 action);
	}

	GTK_ACTION_CLASS (parent_class)->disconnect_proxy (action, proxy);
}


static void
kz_location_entry_action_history_changed (KzHistoryAction *action)
{
	g_return_if_fail(KZ_IS_LOCATION_ENTRY_ACTION(action));

	kz_location_entry_action_sync_history(KZ_LOCATION_ENTRY_ACTION(action));

	if (KZ_HISTORY_ACTION_CLASS(parent_class)->history_changed)
		KZ_HISTORY_ACTION_CLASS(parent_class)->history_changed(action);
}


KzLocationEntryAction *
kz_location_entry_action_new (KzWindow *kz, KzBookmark *bookmark)
{
	KzLocationEntryAction *action;

	action = KZ_LOCATION_ENTRY_ACTION(
			g_object_new(KZ_TYPE_LOCATION_ENTRY_ACTION,
				     "name",        "LocationEntry",
				     "label",       _("Location Entry"),
				     "tooltip",     NULL,
				     "stock_id",    GTK_STOCK_NEW,
				     "kz-window",   kz,
				     "kz-bookmark", bookmark,
				     NULL));
	kz_location_entry_action_restore_history(action);
	
	/* Use old user setting in ~/.kazehakase/kzrc if exist */
	set_old_smart_list_settings(action);

	return action;
}


void
kz_location_entry_action_store_history (KzLocationEntryAction *action)
{
	GList *list, *node;
	gint i = 0;

	g_return_if_fail(KZ_IS_LOCATION_ENTRY_ACTION(action));

	list = kz_profile_enum_key(kz_global_profile, "LocationEntry", TRUE);
	for (node = list; node; node = g_list_next(node))
	{
		const gchar *key = node->data;

		if (key && *key && key_seems_sequential(key, "history"))
			kz_profile_delete_key(kz_global_profile,
					      "LocationEntry", key);
	}
	g_list_free(list);

	list = (GList*)kz_history_action_get_history(KZ_HISTORY_ACTION(action));
	for (node = list; node; node = g_list_next((GList *) node))
	{
		const gchar *uri = node->data;
		gchar key[256];

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

		g_snprintf(key, G_N_ELEMENTS(key), "history%d", i);

		KZ_CONF_SET_STR("LocationEntry", key, uri);
		i++;
	}
}


void
kz_location_entry_action_restore_history (KzLocationEntryAction *action)
{
	GList *list, *node, *history = NULL;
	gint max_history = 32;
	gboolean success;
	gchar *text;

	g_return_if_fail(KZ_IS_LOCATION_ENTRY_ACTION(action));

	if (action->activating) return;

	text = g_strdup(kz_entry_action_get_text(KZ_ENTRY_ACTION(action)));

	list = kz_profile_enum_key(kz_global_profile, "LocationEntry", TRUE);
	for (node = list; node; node = g_list_next(node))
	{
		const gchar *key = node->data;
		gchar *value;

		if (!key_seems_sequential(key, "history")) continue;

		value = KZ_CONF_GET_STR("LocationEntry", key);
		if (value && *value)
			history = g_list_append(history, value);
	}

	success = KZ_CONF_GET("LocationEntry", "max_history",
			      max_history, INT);
	if (success)
		kz_history_action_set_max_history
			(KZ_HISTORY_ACTION(action), max_history);

	if (history)
	{
		kz_history_action_set_history(KZ_HISTORY_ACTION(action),
					      history);
	}

	g_list_foreach(history, (GFunc) g_free, NULL);
	g_list_free(history);

	kz_entry_action_set_text(KZ_ENTRY_ACTION(action), text);
	g_free(text);
}


static void
kz_location_entry_action_sync_history (KzLocationEntryAction *action)
{
	GList *list, *node;

	g_return_if_fail(KZ_IS_LOCATION_ENTRY_ACTION(action));

	if (action->synchronizing) return;
	action->synchronizing = TRUE;

	kz_location_entry_action_store_history
		(KZ_LOCATION_ENTRY_ACTION(action));

	list = kz_window_get_window_list();
	for (node = list; node; node = g_list_next(node))
	{
		KzWindow *tmpkz = node->data;
		GtkAction *act;

		if (tmpkz == action->kz) continue;

		act = gtk_action_group_get_action(tmpkz->actions,
						  "LocationEntry");
		if (KZ_LOCATION_ENTRY_ACTION(act)->synchronizing) continue;
		kz_location_entry_action_restore_history
			(KZ_LOCATION_ENTRY_ACTION(act));
	}

	action->synchronizing = FALSE;
}


static gboolean
cb_entry_key_press (GtkWidget *widget, GdkEventKey *event,
		    KzLocationEntryAction *action)
{
	gboolean new_tab;

	if ((event->keyval == GDK_Return || event->keyval == GDK_ISO_Enter)
	    && (event->state & GDK_CONTROL_MASK))
	{
		const gchar *uri;
		gchar *smart_uri;

		KZ_CONF_GET("Global", "entry_open_in_new_tab", new_tab, BOOL);

		uri = kz_entry_action_get_text(KZ_ENTRY_ACTION(action));
		smart_uri = kz_bookmark_get_smart_uri(KZ_LOCATION_ENTRY_ACTION(action)->bookmark, uri);
		if (new_tab)
			kz_window_load_url(action->kz, smart_uri);
		else 
			kz_window_open_new_tab(action->kz, smart_uri);
		g_free(smart_uri);

		return TRUE;
	}

	return FALSE;
}


static void
cb_activate_edit_bookmark (GtkMenuItem *menuitem,
			   KzLocationEntryAction *action)
{
	GtkAction *edit_action;
	KzWindow *kz = action->kz;

	kz_actions_set_bookmark_for_action(kz, action->bookmark);
	edit_action = gtk_action_group_get_action(kz->actions,
					     	  "EditBookmarks");
	if (edit_action)
		gtk_action_activate(edit_action);
	kz_actions_set_bookmark_for_action(kz, NULL);
}

static void 
cb_entry_populate_popup (GtkEntry *entry, GtkMenu *menu,
		    	 KzLocationEntryAction *action)
{
	GtkWidget *separator, *menuitem;

	separator = gtk_separator_menu_item_new();
	gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
	gtk_widget_show(separator);

	menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Smart Bookmark"));
	g_signal_connect(G_OBJECT(menuitem), "activate",
			 G_CALLBACK(cb_activate_edit_bookmark), action);
	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
	gtk_widget_show(menuitem);
}


/* this function will be obsoleted */
static void
set_old_smart_list_settings (KzLocationEntryAction *action)
{
	GList *list, *node, *smart_list = NULL;
	gint index;
	
	list = kz_profile_enum_key(kz_global_profile, "LocationEntry", TRUE);
	for (node = list; node; node = g_list_next(node))
	{

		const gchar *regex_key = node->data;
		gchar key[32];
		gchar *first;
		gchar *regex, *uri;
		gboolean encode = FALSE;
		KzBookmarkSmartProperty *prop;

		if (!key_seems_sequential(regex_key, "regex")) continue;
		regex = kz_profile_get_string(kz_global_profile, "LocationEntry", regex_key);

		first = strpbrk(regex_key, "0123456789");
		if(!first)
		{
			g_warning("KzPrefsLocationEntry: unexpected error: "
				  "URI regex key has no number!");
			continue;
		}

		index = strtol(first, (char**)'\0', 10);

		g_snprintf(key, G_N_ELEMENTS(key), "uri%d", index);
		uri = kz_profile_get_string(kz_global_profile, "LocationEntry", key);

		g_snprintf(key, G_N_ELEMENTS(key), "encode%d", index);
		kz_profile_get_value(kz_global_profile, "LocationEntry", key,
				     &encode, sizeof(encode),
				     KZ_PROFILE_VALUE_TYPE_BOOL);

		prop = g_new0(KzBookmarkSmartProperty, 1);
		
		prop->uri = uri;
		prop->regex = regex;
		prop->urlencode = encode;

		smart_list = g_list_append(smart_list, prop);
	}
	g_list_free(list);

	/* remove old one */
	list = kz_profile_enum_key(kz_global_profile, "LocationEntry", TRUE);
	for (node = list; node; node = g_list_next(node))
	{
		const gchar *key = node->data;

		if (!key_seems_sequential(key, "regex") &&
		    !key_seems_sequential(key, "uri")   &&
		    !key_seems_sequential(key, "encode")) continue;

		kz_profile_delete_key(kz_global_profile, "LocationEntry", key);
	}
	g_list_free(list);
	
	if (smart_list)
		kz_bookmark_set_smart_list(action->bookmark, smart_list);
}
