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

/*
 *  Copyright (C) 2004 Hiroyuki Ikezoe
 *
 *  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 /* HAVE_CONFIG_H */

#include "kz-app.h"
#include <stdlib.h>
#ifdef USE_SSL
#include <gcrypt.h>
#include <gnutls/gnutls.h>
#include <errno.h>
#include <pthread.h>
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
#ifdef HAVE_LIBSM
#  include <X11/SM/SMlib.h>
#  include <X11/ICE/ICElib.h>
#  ifdef G_OS_UNIX
#     include <sys/types.h>
#  endif
#  ifdef HAVE_UNISTD_H
#    include <unistd.h>
#  endif
#  include <fcntl.h>
#endif /* HAVE_LIBSM */

#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include "kazehakase.h"
#include "kz-actions-tab.h"
#include "kz-ext.h"
#include "kz-icons.h"
#include "kz-session.h"
#include "kz-tab-label.h"
#include "kz-window.h"
#include "kz-history-utils.h"
#include "kz-marshalers.h"
#include "glib-utils.h"
#include "utils.h"

#include "egg-pixbuf-thumbnail-type-builtins.h"

enum {
	THUMBNAIL_CREATED_SIGNAL,
	LAST_SIGNAL
};

enum {
	PROP_0,
	PROP_ARGC,
	PROP_ARGV
};

typedef struct _KzAppPrivate	KzAppPrivate;
struct _KzAppPrivate
{
	gint	        argc;
	gchar         **argv;
	GList          *window_list;
	KzFavicon      *favicon;
	KzSearch       *search;
	KzRootBookmark *bookmarks;
	KzProfile      *profile;
	KzProfile      *proxy;
#if USE_MIGEMO
	KzMigemo       *migemo;
#endif
#ifdef HAVE_LIBSM
	gpointer        smc_conn;
	gchar          *session_id;
#endif /* HAVE_LIBSM */

	/* paths */
	gchar		*user_dir;
	gchar		*history_dir;
	gchar		*history_time_stamp;
	gchar		*popup_dir;
	gchar		*favicon_dir;
	gchar		*smartbookmark_history_dir;
	gchar		*form_data_dir;
	gchar		*clips_dir;
	gchar		*thumbnails_dir;

	gchar		*system_data_dir;
	gchar		*system_kz_data_dir;
	gchar		*system_lib_dir;
	gchar		*system_config_dir;

	gchar		*system_icons_dir;
	gchar		*system_pixmaps_dir;

	gchar		*system_embed_module_dir;
	gchar		*system_search_module_dir;

	gchar		*system_ext_dir;
	gchar		*system_ruby_ext_dir;
	gchar		*system_ruby_ext_data_dir;
};

#define KZ_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_APP, KzAppPrivate))

static GObject *constructor  (GType type,
                              guint n_props,
                              GObjectConstructParam *props);
static void     dispose      (GObject *object);
static void     finalize     (GObject *object);
static void     set_property (GObject *object,
                              guint prop_id,
                              const GValue *value,
                              GParamSpec *pspec);
static void     get_property (GObject *object,
                              guint prop_id,
                              GValue *value,
                              GParamSpec *pspec);

static void     cb_destroy_window (GtkObject *object, gpointer data);

#ifdef HAVE_LIBSM
static void     connect_sm   (KzApp *app);
#endif /* HAVE_LIBSM */

static KzApp    *the_kz_app = NULL;

KzApp *
kz_app_get (void)
{
	return the_kz_app;
}

G_DEFINE_TYPE(KzApp, kz_app, G_TYPE_OBJECT)

static gint kz_app_signals[LAST_SIGNAL] = {0};

static void
kz_app_class_init (KzAppClass *klass)
{
	GObjectClass *object_class;

	object_class = G_OBJECT_CLASS(klass);

	object_class->constructor  = constructor;
	object_class->dispose	   = dispose;
	object_class->finalize	   = finalize;
	object_class->set_property = set_property;
	object_class->get_property = get_property;

	klass->thumbnail_created = NULL;

	kz_app_signals[THUMBNAIL_CREATED_SIGNAL]
		= g_signal_new("thumbnail-created",
			       G_TYPE_FROM_CLASS (klass),
			       G_SIGNAL_RUN_FIRST,
			       G_STRUCT_OFFSET (KzAppClass, thumbnail_created),
			       NULL, NULL,
			       _kz_marshal_VOID__STRING_ENUM,
			       G_TYPE_NONE, 2,
			       G_TYPE_STRING, EGG_TYPE_PIXBUF_THUMBNAIL_SIZE);

	g_object_class_install_property(
		object_class,
		PROP_ARGC,
		g_param_spec_int(
			"argc",
			_("Argument number"),
			_("The number of argument of program"),
			0, G_MAXINT,
			0,
			G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
	g_object_class_install_property(
		object_class,
		PROP_ARGV,
		g_param_spec_pointer ("argv",
				      _("Argument list"),
				      _("The argument list of program"),
				      G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); 
	g_type_class_add_private(object_class, sizeof(KzAppPrivate));
}

static GObject *
constructor (GType                  type,
             guint                  n_props,
             GObjectConstructParam *props)
{
	GObject *object;

	if (!the_kz_app) 
	{
		GObjectClass *klass = G_OBJECT_CLASS(kz_app_parent_class);
		object = klass->constructor(type, n_props, props);

#ifdef HAVE_LIBSM
		/* session management */
		connect_sm(KZ_APP(object));
#endif /* HAVE_LIBSM */

		the_kz_app = KZ_APP(object);
	}
	else
	{
		object = g_object_ref(the_kz_app);
	}
	return object;
}

static void
set_property (GObject *object,
              guint prop_id,
              const GValue *value,
              GParamSpec *pspec)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(object);

	switch (prop_id)
	{
	case PROP_ARGC:
		priv->argc = g_value_get_int(value);
		break;
	case PROP_ARGV:
		priv->argv = g_strdupv(g_value_get_pointer(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


static void
get_property (GObject *object,
              guint prop_id,
              GValue *value,
              GParamSpec *pspec)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(object);

	switch (prop_id)
	{
	case PROP_ARGC:
		g_value_set_int(value, priv->argc);
		break;
	case PROP_ARGV:
		g_value_set_pointer(value, priv->argv);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


static void
ensure_directories (KzApp *app)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	g_mkdir_with_parents(priv->history_dir, 0700);
	g_mkdir_with_parents(priv->favicon_dir, 0700);
	g_mkdir_with_parents(priv->popup_dir, 0700);
	g_mkdir_with_parents(priv->smartbookmark_history_dir, 0700);
	g_mkdir_with_parents(priv->form_data_dir, 0700);
	g_mkdir_with_parents(priv->clips_dir, 0700);
}

static void
kz_app_init_path (KzApp *app)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

#ifdef G_OS_WIN32
	priv->user_dir = g_build_filename(g_get_user_config_dir(),
					  PACKAGE, NULL);
#else
	priv->user_dir = g_build_filename(g_get_home_dir(), "."PACKAGE, NULL);
#endif
	priv->history_dir = g_build_filename(priv->user_dir,
					     "history",
					     NULL);
	priv->history_time_stamp = g_build_filename(priv->history_dir,
						    "time-stamp",
						    NULL);
	priv->popup_dir = g_build_filename(priv->user_dir, "popup", NULL);
	priv->favicon_dir = g_build_filename(priv->user_dir, "favicon", NULL);
	priv->smartbookmark_history_dir =
		g_build_filename(priv->user_dir, "smartbookmark_history", NULL);
	priv->form_data_dir =
		g_build_filename(priv->user_dir, "form_data", NULL);
	priv->clips_dir = g_build_filename(priv->user_dir, "clips", NULL);
	priv->thumbnails_dir = g_build_filename(g_get_home_dir(),
						".thumbnails",
						NULL);

#ifdef G_OS_WIN32
	priv->system_data_dir = g_build_filename(kz_win32_base_path(),
						 "share", NULL);
	priv->system_kz_data_dir = g_build_filename(priv->system_data_dir,
						    PACKAGE, NULL);
	priv->system_lib_dir = g_build_filename(kz_win32_base_path(),
						"lib", PACKAGE, NULL);
	priv->system_config_dir = g_build_filename(kz_win32_base_path(),
						   "etc", PACKAGE, NULL);

	priv->system_embed_module_dir = g_build_filename(priv->system_lib_dir,
							 "web", NULL);
	priv->system_search_module_dir = g_build_filename(priv->system_lib_dir,
							  "search", NULL);

	priv->system_ext_dir = g_strdup(priv->system_lib_dir);
	priv->system_ruby_ext_dir = g_build_filename(priv->system_ext_dir,
						     "ruby", NULL);
#else
	priv->system_data_dir = g_strdup(SYSDATADIR);
	priv->system_kz_data_dir = g_strdup(KZ_DATADIR);
	priv->system_config_dir = g_strdup(KZ_SYSCONFDIR);
	priv->system_lib_dir = g_strdup(KZ_LIBDIR);

	priv->system_embed_module_dir = g_strdup(KZ_EMBED_MODULEDIR);
	priv->system_search_module_dir = g_strdup(KZ_SEARCH_MODULEDIR);

	priv->system_ext_dir = g_strdup(KZ_EXTDIR);
	priv->system_ruby_ext_dir = g_strdup(KZ_RUBY_EXTDIR);
#endif

	priv->system_icons_dir = g_build_filename(priv->system_kz_data_dir,
						  "icons", NULL);
	priv->system_pixmaps_dir = g_build_filename(priv->system_data_dir,
						    "pixmaps", NULL);

	priv->system_ruby_ext_data_dir =
		g_build_filename(priv->system_kz_data_dir, "ext", "ruby", NULL);

	ensure_directories(app);
}

static void
kz_app_dispose_path (GObject *app)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	g_free(priv->user_dir);
	g_free(priv->history_dir);
	g_free(priv->history_time_stamp);
	g_free(priv->popup_dir);
	g_free(priv->favicon_dir);
	g_free(priv->smartbookmark_history_dir);
	g_free(priv->form_data_dir);
	g_free(priv->clips_dir);
	g_free(priv->thumbnails_dir);

	g_free(priv->system_data_dir);
	g_free(priv->system_kz_data_dir);
	g_free(priv->system_lib_dir);
	g_free(priv->system_config_dir);

	g_free(priv->system_icons_dir);
	g_free(priv->system_pixmaps_dir);

	g_free(priv->system_embed_module_dir);
	g_free(priv->system_search_module_dir);

	g_free(priv->system_ext_dir);
	g_free(priv->system_ruby_ext_dir);
	g_free(priv->system_ruby_ext_data_dir);
}

#define DEFINE_PATH_GETTER(name)			\
const gchar *						\
kz_app_get_ ## name (KzApp *app)			\
{							\
	g_return_val_if_fail(KZ_IS_APP(app), NULL);	\
							\
	return KZ_APP_GET_PRIVATE(app)->name;		\
}

DEFINE_PATH_GETTER(user_dir)
DEFINE_PATH_GETTER(history_dir)
DEFINE_PATH_GETTER(history_time_stamp)
DEFINE_PATH_GETTER(popup_dir)
DEFINE_PATH_GETTER(favicon_dir)
DEFINE_PATH_GETTER(smartbookmark_history_dir)
DEFINE_PATH_GETTER(form_data_dir)
DEFINE_PATH_GETTER(clips_dir)
DEFINE_PATH_GETTER(thumbnails_dir)

DEFINE_PATH_GETTER(system_data_dir)
DEFINE_PATH_GETTER(system_kz_data_dir)
DEFINE_PATH_GETTER(system_lib_dir)
DEFINE_PATH_GETTER(system_config_dir)

DEFINE_PATH_GETTER(system_icons_dir)
DEFINE_PATH_GETTER(system_pixmaps_dir)

DEFINE_PATH_GETTER(system_embed_module_dir)
DEFINE_PATH_GETTER(system_search_module_dir)

DEFINE_PATH_GETTER(system_ext_dir)
DEFINE_PATH_GETTER(system_ruby_ext_dir)
DEFINE_PATH_GETTER(system_ruby_ext_data_dir)


static void
kz_app_init (KzApp *app)
{
	gchar *sysconf_file, *conf_file;
	gchar *bookmark_file, *sys_bookmark_file;
	gchar *clip_file, *current_session_file;
	gchar *bookmark_bar_file, *sys_bookmark_bar_file;
	gchar *smartbookmark_file, *sys_smartbookmark_file;
	gchar *accel_prefs_file;
	gchar *proxy_file, *sysproxy_file;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	kz_app_init_path(app);

	priv->window_list = NULL;
	priv->favicon = kz_favicon_get_instance();

	/* load prefs */
	sysconf_file = g_build_filename(priv->system_config_dir, "kzrc", NULL);
	conf_file = g_build_filename(priv->user_dir, "kzrc", NULL);
	
	priv->profile = kz_profile_open(conf_file, sysconf_file);

	/* load bookmarks */
	bookmark_file         = g_build_filename(priv->user_dir,
					    	 "bookmarks.xml",
					    	 NULL);
	sys_bookmark_file     = g_build_filename(priv->system_config_dir,
					    	 "bookmarks.xml",
					    	 NULL);

        bookmark_bar_file     = g_build_filename(priv->user_dir,
						 "bookmarkbar.xml",
					    	 NULL);
	sys_bookmark_bar_file = g_build_filename(priv->system_config_dir,
					    	 "bookmarkbar.xml",
					    	 NULL);

	clip_file             = g_build_filename(priv->user_dir,
						 "clip.xml",
					    	 NULL);

        smartbookmark_file    = g_build_filename(priv->user_dir,
					    	 "smartbookmarks.xml",
					    	 NULL);
	sys_smartbookmark_file= g_build_filename(priv->system_config_dir,
					    	 "smartbookmarks.xml",
					    	 NULL);

	current_session_file  = g_build_filename(priv->user_dir,
						 "current_session.xml",
						 NULL);

	priv->bookmarks = kz_root_bookmark_new(bookmark_file, sys_bookmark_file,
					       clip_file, NULL);

	kz_root_bookmark_add_smartbookmark_file(priv->bookmarks,
						smartbookmark_file,
						sys_smartbookmark_file);
	kz_root_bookmark_add_bookmark_bar_file(priv->bookmarks,
					       bookmark_bar_file,
					       sys_bookmark_bar_file);
	kz_root_bookmark_add_current_session_file(priv->bookmarks,
						  current_session_file);
	kz_session_set_profile(KZ_SESSION(priv->bookmarks->current_session),
			       priv->profile);

	/* Load other prefs... */
	accel_prefs_file = g_build_filename(priv->user_dir, "keyaccelrc", NULL);
	gtk_accel_map_load(accel_prefs_file);

	sysproxy_file = g_build_filename(priv->system_config_dir,
					 "proxyrc", NULL);
	proxy_file = g_build_filename(priv->user_dir, "proxyrc", NULL);
	priv->proxy = kz_profile_open(proxy_file, sysproxy_file);

#ifdef USE_SSL
        /* initialize gnutls. this function should be called once. */
	gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
	gnutls_global_init();
#endif

#if USE_MIGEMO
	priv->migemo = kz_migemo_new();
#endif

	g_free(sysconf_file);
	g_free(conf_file);
	g_free(bookmark_file);
	g_free(sys_bookmark_file);
	g_free(clip_file);
	g_free(bookmark_bar_file);
	g_free(sys_bookmark_bar_file);
	g_free(smartbookmark_file);
	g_free(sys_smartbookmark_file);
	g_free(current_session_file);
	g_free(accel_prefs_file);
	g_free(sysproxy_file);
	g_free(proxy_file);
}


static void
clean_history_cache (void)
{
	gboolean limit = FALSE;

	KZ_CONF_GET("History", "limit_cache", limit, BOOL);
	if (limit)
	{
		guint limit_days = 30;
		time_t limit_seconds;

		KZ_CONF_GET("History", "limit_days", limit_days, INT);
		limit_seconds = limit_days * 86400;
		if (!kz_history_time_stamp_exists())
			kz_history_make_time_stamp();
		kz_history_purge_by_time_stamp(limit_seconds);
		kz_utils_purge_files(KZ_GET_POPUP_DIR, limit_seconds);
		kz_utils_purge_files(KZ_GET_THUMBNAILS_DIR, limit_seconds);
	}
}

static void
dispose (GObject *object)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(object);

	if (priv->bookmarks)
	{
		kz_root_bookmark_save_all(priv->bookmarks);
		g_object_unref(priv->bookmarks);
		priv->bookmarks = NULL;
	}

	if (priv->profile)
	{
		kz_profile_close(priv->profile);
		priv->profile   = NULL;
	}

	if (priv->proxy)
	{
		kz_profile_close(priv->proxy);
		priv->proxy     = NULL;
	}

	if (priv->favicon)
	{
		g_object_unref(priv->favicon);
		priv->favicon   = NULL;
	}

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

static void
finalize (GObject *object)
{
	gchar *accel_prefs_file;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(object);

	accel_prefs_file = g_build_filename(g_get_home_dir(),
					    "."PACKAGE,
					    "keyaccelrc",
					    NULL);

	gtk_accel_map_save(accel_prefs_file);
	g_free(accel_prefs_file);

#ifdef USE_SSL
	gnutls_global_deinit();
#endif

#ifdef USE_MIGEMO
	if (priv->migemo)
		kz_migemo_free(priv->migemo);
#endif

#ifdef HAVE_LIBSM
	if (priv->smc_conn)
		SmcCloseConnection ((SmcConn) priv->smc_conn, 0, NULL);
#endif /* HAVE_LIBSM */

	g_strfreev(priv->argv);
	kz_app_dispose_path(object);

	if (G_OBJECT_CLASS (kz_app_parent_class)->finalize)
		G_OBJECT_CLASS (kz_app_parent_class)->finalize(object);
}

KzApp *
kz_app_new (gint argc, gchar **argv)
{
	KzApp *kzapp = g_object_new(KZ_TYPE_APP,
				    "argc", argc,
				    "argv", argv,
				    NULL);

	return kzapp;
}

void
kz_app_init_dependencies (KzApp *app, gpointer initial_address)
{
	KzAppPrivate *priv;
	gchar *search_engine;

	g_return_if_fail(KZ_IS_APP(app));

	kz_icons_init();

	if (!kz_history_time_stamp_exists())
		kz_history_make_time_stamp();

	priv = KZ_APP_GET_PRIVATE(app);

	search_engine = kz_profile_get_string(priv->profile,
					      "History", "search_engine");
	if (search_engine)
	{
		kz_app_set_search(app, search_engine);
		g_free(search_engine);
	}

	kz_ext_init(&initial_address);
}

void
kz_app_quit_dependencies (KzApp *app)
{
	KzAppPrivate *priv;

	g_return_if_fail(KZ_IS_APP(app));

	priv = KZ_APP_GET_PRIVATE(app);

	kz_ext_exit();
	kz_embed_exit();

	clean_history_cache();
	if (priv->search)
		g_object_unref(priv->search);
	priv->search = NULL;
	kz_search_exit();
}

void
kz_app_save_session (KzApp *app)
{
	KzAppPrivate *priv;

	g_return_if_fail(KZ_IS_APP(app));

	priv = KZ_APP_GET_PRIVATE(app);
	kz_bookmark_file_save(KZ_BOOKMARK_FILE(priv->bookmarks->current_session));
}

GtkWidget *
kz_app_restore_session (KzApp *app)
{
	GList *list;
	const GList *node, *window_node;
	const gchar *location;
	KzAppPrivate *priv;
	KzBookmark *session;

	g_return_val_if_fail(KZ_IS_APP(app), NULL);

	priv = KZ_APP_GET_PRIVATE(app);

	/* close all tabs before loading session file */
	kz_app_freeze_session(app);
	for (node = priv->window_list; node; node = g_list_next(node))
	{
		KzWindow *kz = KZ_WINDOW(node->data);
		kz_bookmark_folder_remove(KZ_BOOKMARK_FOLDER(priv->bookmarks->current_session),
				  	  KZ_BOOKMARK(kz->tabs));
		kz_window_close_all_tab(kz);
	}

	session = priv->bookmarks->current_session;
	location = kz_bookmark_file_get_location(KZ_BOOKMARK_FILE(session));
	if (g_file_test(location, G_FILE_TEST_EXISTS))
		kz_bookmark_file_load(KZ_BOOKMARK_FILE(session));

	list = kz_bookmark_folder_get_children(KZ_BOOKMARK_FOLDER(session));

	window_node = priv->window_list;
	/* windows */
	for (node = list; node; node = g_list_next(node))
	{
		KzWindow *window;
		KzBookmark *window_bookmark = KZ_BOOKMARK(node->data);

		if (!window_node)
		{
			window = KZ_WINDOW(kz_app_create_new_window(app, NULL));
			gtk_widget_show(GTK_WIDGET(window));
		}
		else
		{
			window = KZ_WINDOW(window_node->data);
			window_node = g_list_next(window_node);
		}

		kz_window_restore_tabs(window, window_bookmark);
	}
	g_list_free(list);

	/* close extra windows */
	for (;window_node; window_node = g_list_next(window_node))
	{
		KzWindow *window = KZ_WINDOW(window_node->data);
		gtk_widget_destroy(GTK_WIDGET(window));
	}
	kz_app_thaw_session(app);

	/* if there is no window, create a window */
	if (!priv->window_list)
		kz_app_create_new_window(app, NULL);

	return GTK_WIDGET(priv->window_list->data);
}

GtkWidget *
kz_app_create_new_window (KzApp *app, const gchar *uri)
{
	GtkWidget *window;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	window = kz_window_new(uri);

	if (!kz_session_is_frozen(KZ_SESSION(priv->bookmarks->current_session)))
	{
		kz_bookmark_folder_append(KZ_BOOKMARK_FOLDER(priv->bookmarks->current_session),
				   	  KZ_BOOKMARK(KZ_WINDOW(window)->tabs));
	}

	g_signal_connect(window, "destroy",
			 G_CALLBACK(cb_destroy_window), app);
	priv->window_list = g_list_append(priv->window_list, window);

	return window;
}

static KzUILevel
kz_ui_level_from_str (const gchar *level)
{
	if (!level || !*level)
		return KZ_UI_LEVEL_INVALID;

	if (!strcmp(level, "custom"))
		return KZ_UI_LEVEL_CUSTOM;
	else if (!strcmp(level, "expert"))
		return KZ_UI_LEVEL_EXPERT;
	else if (!strcmp(level, "medium"))
		return KZ_UI_LEVEL_MEDIUM;
	else if (!strcmp(level, "beginner"))
		return KZ_UI_LEVEL_BEGINNER;

	return KZ_UI_LEVEL_INVALID;
}

#if 0
static const gchar *
kz_ui_level_to_str (KzUILevel level)
{
	switch (level)
	{
	case KZ_UI_LEVEL_CUSTOM:
		return "custom";
	case KZ_UI_LEVEL_EXPERT:
		return "expert";
	case KZ_UI_LEVEL_MEDIUM:
		return "medium";
	case KZ_UI_LEVEL_BEGINNER:
		return "beginner";
	default:
		break;
	}

	return NULL;
}
#endif

KzUILevel
kz_app_get_ui_level (KzApp *app)
{
	KzUILevel val;
	gchar level[16];
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	g_return_val_if_fail(priv->profile, KZ_UI_LEVEL_BEGINNER);

	level[0] = '\0';
	kz_profile_get_value(priv->profile,
			     "Global", "ui_level",
			     level, sizeof(level),
			     KZ_PROFILE_VALUE_TYPE_STRING);

	val = kz_ui_level_from_str(level);
	if (!val)
		return KZ_UI_LEVEL_BEGINNER;

	return val;
}

KzFavicon *
kz_app_get_favicon (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->favicon;
}

KzSearch *
kz_app_get_search (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->search;
}

void
kz_app_set_search (KzApp *app, const gchar *engine_name)
{
	KzAppPrivate *priv;

	priv = KZ_APP_GET_PRIVATE(app);
	if (priv->search)
		g_object_unref(priv->search);

	if (engine_name)
		priv->search = kz_search_new(engine_name);
	else
		priv->search = NULL;

	if (priv->search)
	{
		if (!kz_search_exist_index_dir(priv->search))
			kz_search_make_index(priv->search);
	}
}

KzRootBookmark *
kz_app_get_root_bookmark (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->bookmarks;
}

KzProfile *
kz_app_get_profile (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->profile;
}

KzProfile *
kz_app_get_proxy (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->proxy;
}

const GList *
kz_app_get_window_list (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->window_list;
}

GtkWidget *
kz_app_get_window_from_web (KzApp *app, KzWeb *web)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);
	GList *node;

	for (node = (GList*)priv->window_list; node; node = g_list_next (node))
	{
		KzWindow *kz = node->data;
		KzTabLabel *label;

		if (!KZ_IS_WINDOW(kz)) continue;

		label = kz_notebook_get_tab_label(KZ_NOTEBOOK(kz->notebook),
						  web);
		if (label)
			return GTK_WIDGET(kz);
	}

	return NULL;
}

void
kz_app_freeze_session (KzApp *app)
{
	KzAppPrivate *priv;

	g_return_if_fail (KZ_IS_APP(app));

	priv = KZ_APP_GET_PRIVATE(app);
	kz_session_freeze(KZ_SESSION(priv->bookmarks->current_session));
}

void
kz_app_thaw_session (KzApp *app)
{
	KzAppPrivate *priv;

	g_return_if_fail (KZ_IS_APP(app));

	priv = KZ_APP_GET_PRIVATE(app);
	kz_session_thaw(KZ_SESSION(priv->bookmarks->current_session));
}

static void
cb_destroy_window (GtkObject *object, gpointer data)
{
	KzApp *app = KZ_APP(data);
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);
	KzWindow *kz = KZ_WINDOW (object);

	g_signal_handlers_disconnect_by_func(object,
					     G_CALLBACK(cb_destroy_window), data);
	priv->window_list = g_list_remove(priv->window_list, object);

	if (!priv->window_list)
	{
		static gboolean main_quited = FALSE;
		if (!main_quited)
		{
			/* If the window is the last window, keep its tabs for saving the
			 * session. */
			kz_app_freeze_session(app);
			gtk_main_quit();
			main_quited = TRUE;
		}
	} 
	else
	{
		if (kz->tabs)
			kz_bookmark_folder_remove(KZ_BOOKMARK_FOLDER(priv->bookmarks->current_session),
					   	  KZ_BOOKMARK(kz->tabs));
	}
}

#if USE_MIGEMO
KzMigemo *
kz_app_get_migemo (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->migemo;
}
#endif

gboolean
kz_app_create_thumbnail (KzApp *app,
			 GdkPixbuf *pixbuf,
			 const gchar *uri,
			 time_t mtime,
			 EggPixbufThumbnailSize size)
{
	GError *error = NULL;

	if (!uri)
		return FALSE;

	if (uri[0] == '\0')
		return FALSE;

	egg_pixbuf_set_thumbnail_uri(pixbuf, uri);
        egg_pixbuf_set_thumbnail_mtime(pixbuf, mtime);
        egg_pixbuf_set_thumbnail_size(pixbuf, size);

	if (!egg_pixbuf_save_thumbnail(pixbuf, &error, NULL))
	{
		g_warning("create_thumbnail: %s", error->message);
		g_error_free(error);
		return FALSE;
	}

	g_signal_emit(app, kz_app_signals[THUMBNAIL_CREATED_SIGNAL], 0,
		      uri, size);

	return TRUE;
}


#ifdef HAVE_LIBSM

/* these codes are taken from libgnomeui.*/

static void ice_io_error_handler (IceConn connection);

static void new_ice_connection (IceConn connection, IcePointer client_data,
				Bool opening, IcePointer *watch_data);

/* This is called when data is available on an ICE connection.  */
static gboolean
process_ice_messages (GIOChannel   *source,
		      GIOCondition  condition,
		      gpointer      data)
{
	IceConn connection = (IceConn) data;
	IceProcessMessagesStatus status;

	status = IceProcessMessages (connection, NULL, NULL);

	if (status == IceProcessMessagesIOError)
	{
		IcePointer context = IceGetConnectionContext (connection);

		if (context && GTK_IS_OBJECT (context))
		{
			guint disconnect_id = g_signal_lookup ("disconnect", G_OBJECT_TYPE (context));

			if (disconnect_id > 0)
				g_signal_emit (context, disconnect_id, 0);
		}
		else
		{
			IceSetShutdownNegotiation (connection, False);
			IceCloseConnection (connection);
		}
	}

	return TRUE;
}

/* This is called when a new ICE connection is made.  It arranges for
   the ICE connection to be handled via the event loop.  */
static void
new_ice_connection (IceConn connection, IcePointer client_data, Bool opening,
		IcePointer *watch_data)
{
	guint input_id;

	if (opening)
	{
		GIOChannel *channel;
		/* Make sure we don't pass on these file descriptors to any
		   exec'ed children */
		fcntl(IceConnectionNumber(connection),F_SETFD,
				fcntl(IceConnectionNumber(connection),F_GETFD,0) | FD_CLOEXEC);

		channel = g_io_channel_unix_new (IceConnectionNumber (connection));
		input_id = g_io_add_watch (channel,
				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
				process_ice_messages,
				connection);
		g_io_channel_unref (channel);

		*watch_data = (IcePointer) GUINT_TO_POINTER (input_id);
	}
	else
	{
		input_id = GPOINTER_TO_UINT ((gpointer) *watch_data);

		g_source_remove (input_id);
	}
}

static IceIOErrorHandler ice_installed_handler;

/* We call any handler installed before (or after) gnome_ice_init but
   avoid calling the default libICE handler which does an exit() */
static void
ice_io_error_handler (IceConn connection)
{
	if (ice_installed_handler)
		(*ice_installed_handler) (connection);
}

static void
ice_init (void)
{
	static gboolean ice_init = FALSE;

	if (! ice_init)
	{
		IceIOErrorHandler default_handler;

		ice_installed_handler = IceSetIOErrorHandler (NULL);
		default_handler = IceSetIOErrorHandler (ice_io_error_handler);

		if (ice_installed_handler == default_handler)
			ice_installed_handler = NULL;

		IceAddConnectionWatch (new_ice_connection, NULL);

		ice_init = TRUE;
	}
}

static void
save_yourself_callback (SmcConn   smc_conn,
			SmPointer client_data,
			int       save_style,
			gboolean  shutdown,
			int       interact_style,
			gboolean  fast)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(client_data);

	SmcSaveYourselfDone((SmcConn) priv->smc_conn, TRUE);
}

static void
die_callback (SmcConn smc_conn, SmPointer client_data)
{
	gtk_main_quit();
}

static void
save_complete_callback (SmcConn smc_conn, SmPointer client_data)
{
}

static void
shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data)
{
}

static void
session_set_value (SmcConn      smc_conn,
		   gchar       *name,
		   char        *type,
		   int          num_vals,
		   SmPropValue *vals)
{
	SmProp *proplist[1];
	SmProp prop;

	prop.name = name;
	prop.type = type;
	prop.num_vals = num_vals;
	prop.vals = vals;

	proplist[0]= &prop;
	SmcSetProperties ((SmcConn) smc_conn, 1, proplist);
}

static void
session_set_string (SmcConn smc_conn, gchar *name, gchar *value)
{
	SmPropValue val;

	g_return_if_fail (name);

	val.length = strlen (value)+1;
	val.value  = value;

	session_set_value (smc_conn, name, SmARRAY8, 1, &val);
}

static void
session_set_gchar (SmcConn smc_conn, gchar *name, gchar value)
{
	SmPropValue val;

	g_return_if_fail (name);

	val.length = 1;
	val.value  = &value;

	session_set_value (smc_conn, name, SmCARD8, 1, &val);
}

static void
session_set_clone_command (KzApp *app)
{
	SmPropValue *vals;
	gint i;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	vals = g_new(SmPropValue, priv->argc);

	for (i = 0; i < priv->argc; i++)
	{
		vals[i].length = strlen(priv->argv[i]);
		vals[i].value  = priv->argv[i];
	}
	session_set_value(priv->smc_conn, SmCloneCommand, SmLISTofARRAY8, i, vals);
	session_set_value(priv->smc_conn, SmRestartCommand, SmLISTofARRAY8, i, vals);

	g_free(vals);
}

static void
connect_sm (KzApp *app)
{
	SmcCallbacks      callbacks;
	gchar            *session_id;
	gchar error_string[256] = "";
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	g_return_if_fail(app);

	if (!g_getenv("SESSION_MANAGER"))
		return;

	ice_init();

	callbacks.save_yourself.callback      = save_yourself_callback;
	callbacks.die.callback                = die_callback;
	callbacks.save_complete.callback      = save_complete_callback;
	callbacks.shutdown_cancelled.callback = shutdown_cancelled_callback;

	callbacks.save_yourself.client_data =
		callbacks.die.client_data =
		callbacks.save_complete.client_data =
		callbacks.shutdown_cancelled.client_data = (SmPointer) app;

	priv->session_id = NULL;
	priv->smc_conn= (gpointer)
		SmcOpenConnection(NULL, app,
				SmProtoMajor, SmProtoMinor,
				SmcSaveYourselfProcMask | SmcDieProcMask |
				SmcSaveCompleteProcMask |
				SmcShutdownCancelledProcMask,
				&callbacks,
				priv->session_id, &session_id,
				sizeof(error_string), error_string);

	if (error_string[0])
	{
		g_warning("While connecting to session manager:\n%s.",
				error_string);
	}
	if (priv->smc_conn)
	{
		gchar *tmp;

		gdk_set_sm_client_id(session_id);

		tmp = g_get_current_dir();
		session_set_string(priv->smc_conn, SmCurrentDirectory, tmp);
		g_free(tmp);

		tmp = g_strdup_printf("%d", (int) getpid());
		session_set_string(priv->smc_conn, SmProcessID, tmp);
		g_free(tmp);

		session_set_string(priv->smc_conn, SmUserID, (gchar *)g_get_user_name());

		session_set_gchar(priv->smc_conn, SmRestartStyleHint, (gchar) SmRestartIfRunning);
		session_set_string(priv->smc_conn, SmProgram, g_get_prgname());

		session_set_clone_command(app);
		g_free(session_id);
	}
}
#endif /* HAVE_LIBSM */
