/*
 * Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>

#include "matekbd-indicator-plugins-capplet.h"

#include <string.h>
#include <sys/stat.h>

#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <gdk/gdkx.h>

static MatekbdKeyboardConfig initialSysKbdConfig;
static GMainLoop *loop;

extern void
CappletFillActivePluginList (MatekbdIndicatorPluginsCapplet * gipc)
{
	GtkWidget *activePlugins =
	    CappletGetUiWidget (gipc, "activePlugins");
	GtkListStore *activePluginsModel =
	    GTK_LIST_STORE (gtk_tree_view_get_model
			    (GTK_TREE_VIEW (activePlugins)));
	GSList *pluginPathNode = gipc->applet_cfg.enabled_plugins;
	GHashTable *allPluginRecs = gipc->plugin_manager.all_plugin_recs;

	gtk_list_store_clear (activePluginsModel);
	if (allPluginRecs == NULL)
		return;

	while (pluginPathNode != NULL) {
		GtkTreeIter iter;
		const char *fullPath = (const char *) pluginPathNode->data;
		const MatekbdIndicatorPlugin *plugin =
		    matekbd_indicator_plugin_manager_get_plugin (&gipc->
							      plugin_manager,
							      fullPath);
		if (plugin != NULL) {
			gtk_list_store_append (activePluginsModel, &iter);
			gtk_list_store_set (activePluginsModel, &iter,
					    NAME_COLUMN, plugin->name,
					    FULLPATH_COLUMN, fullPath, -1);
		}

		pluginPathNode = g_slist_next (pluginPathNode);
	}
}

static char *
CappletGetSelectedActivePluginPath (MatekbdIndicatorPluginsCapplet * gipc)
{
	GtkTreeView *pluginsList =
	    GTK_TREE_VIEW (CappletGetUiWidget (gipc, "activePlugins"));
	return CappletGetSelectedPluginPath (pluginsList, gipc);
}

char *
CappletGetSelectedPluginPath (GtkTreeView * pluginsList,
			      MatekbdIndicatorPluginsCapplet * gipc)
{
	GtkTreeModel *model =
	    gtk_tree_view_get_model (GTK_TREE_VIEW (pluginsList));
	GtkTreeSelection *selection =
	    gtk_tree_view_get_selection (GTK_TREE_VIEW (pluginsList));
	GtkTreeIter selectedIter;

	if (gtk_tree_selection_get_selected
	    (selection, NULL, &selectedIter)) {
		char *fullPath = NULL;

		gtk_tree_model_get (model, &selectedIter,
				    FULLPATH_COLUMN, &fullPath, -1);
		return fullPath;
	}
	return NULL;
}

static void
CappletActivePluginsSelectionChanged (GtkTreeSelection *
				      selection,
				      MatekbdIndicatorPluginsCapplet * gipc)
{
	GtkWidget *activePlugins =
	    CappletGetUiWidget (gipc, "activePlugins");
	GtkTreeModel *model =
	    gtk_tree_view_get_model (GTK_TREE_VIEW (activePlugins));
	GtkTreeIter selectedIter;
	gboolean isAnythingSelected = FALSE;
	gboolean isFirstSelected = FALSE;
	gboolean isLastSelected = FALSE;
	gboolean hasConfigurationUi = FALSE;
	GtkWidget *btnRemove = CappletGetUiWidget (gipc, "btnRemove");
	GtkWidget *btnUp = CappletGetUiWidget (gipc, "btnUp");
	GtkWidget *btnDown = CappletGetUiWidget (gipc, "btnDown");
	GtkWidget *btnProperties = CappletGetUiWidget (gipc, "btnProperties");
	GtkWidget *lblDescription = CappletGetUiWidget (gipc, "lblDescription");

	gtk_label_set_text (GTK_LABEL (lblDescription),
			    g_strconcat ("<small><i>",
					 _("No description."),
					 "</i></small>", NULL));
	gtk_label_set_use_markup (GTK_LABEL (lblDescription), TRUE);

	if (gtk_tree_selection_get_selected
	    (selection, NULL, &selectedIter)) {
		int counter = gtk_tree_model_iter_n_children (model, NULL);
		GtkTreePath *treePath =
		    gtk_tree_model_get_path (model, &selectedIter);
		gint *indices = gtk_tree_path_get_indices (treePath);
		char *fullPath = CappletGetSelectedActivePluginPath (gipc);
		const MatekbdIndicatorPlugin *plugin =
		    matekbd_indicator_plugin_manager_get_plugin (&gipc->
							      plugin_manager,
							      fullPath);

		isAnythingSelected = TRUE;

		isFirstSelected = indices[0] == 0;
		isLastSelected = indices[0] == counter - 1;

		if (plugin != NULL) {
			hasConfigurationUi =
			    (plugin->configure_properties_callback !=
			     NULL);
			gtk_label_set_text (GTK_LABEL (lblDescription),
					    g_strconcat ("<small><i>",
							 plugin->
							 description,
							 "</i></small>",
							 NULL));
			gtk_label_set_use_markup (GTK_LABEL
						  (lblDescription), TRUE);
		}
		g_free (fullPath);

		gtk_tree_path_free (treePath);
	}
	gtk_widget_set_sensitive (btnRemove, isAnythingSelected);
	gtk_widget_set_sensitive (btnUp, isAnythingSelected
				  && !isFirstSelected);
	gtk_widget_set_sensitive (btnDown, isAnythingSelected
				  && !isLastSelected);
	gtk_widget_set_sensitive (btnProperties, isAnythingSelected
				  && hasConfigurationUi);
}

static void
CappletPromotePlugin (GtkWidget * btnUp,
		      MatekbdIndicatorPluginsCapplet * gipc)
{
	char *fullPath = CappletGetSelectedActivePluginPath (gipc);
	if (fullPath != NULL) {
		matekbd_indicator_plugin_manager_promote_plugin (&gipc->
							      plugin_manager,
							      gipc->
							      applet_cfg.
							      enabled_plugins,
							      fullPath);
		g_free (fullPath);
		CappletFillActivePluginList (gipc);
		matekbd_indicator_config_save_to_mateconf (&gipc->applet_cfg);
	}
}

static void
CappletDemotePlugin (GtkWidget * btnUp, MatekbdIndicatorPluginsCapplet * gipc)
{
	char *fullPath = CappletGetSelectedActivePluginPath (gipc);
	if (fullPath != NULL) {
		matekbd_indicator_plugin_manager_demote_plugin (&gipc->
							     plugin_manager,
							     gipc->
							     applet_cfg.
							     enabled_plugins,
							     fullPath);
		g_free (fullPath);
		CappletFillActivePluginList (gipc);
		matekbd_indicator_config_save_to_mateconf (&gipc->applet_cfg);
	}
}

static void
CappletDisablePlugin (GtkWidget * btnRemove,
		      MatekbdIndicatorPluginsCapplet * gipc)
{
	char *fullPath = CappletGetSelectedActivePluginPath (gipc);
	if (fullPath != NULL) {
		matekbd_indicator_plugin_manager_disable_plugin (&gipc->
							      plugin_manager,
							      &gipc->
							      applet_cfg.
							      enabled_plugins,
							      fullPath);
		g_free (fullPath);
		CappletFillActivePluginList (gipc);
		matekbd_indicator_config_save_to_mateconf (&gipc->applet_cfg);
	}
}

static void
CappletConfigurePlugin (GtkWidget * btnRemove,
			MatekbdIndicatorPluginsCapplet * gipc)
{
	char *fullPath = CappletGetSelectedActivePluginPath (gipc);
	if (fullPath != NULL) {
		matekbd_indicator_plugin_manager_configure_plugin (&gipc->
								plugin_manager,
								&gipc->
								plugin_container,
								fullPath,
								GTK_WINDOW
								(gipc->
								 capplet));
		g_free (fullPath);
	}
}

static void
CappletResponse (GtkDialog * dialog, gint response)
{
	if (response == GTK_RESPONSE_HELP) {
		GError *error = NULL;
		GdkAppLaunchContext *ctx = gdk_app_launch_context_new ();

		g_app_info_launch_default_for_uri
		    ("ghelp:matekbd?gkb-indicator-applet-plugins",
		     G_APP_LAUNCH_CONTEXT (ctx), &error);

		if (error) {
			GtkWidget *d = NULL;

			d = gtk_message_dialog_new (GTK_WINDOW (dialog),
						    GTK_DIALOG_MODAL |
						    GTK_DIALOG_DESTROY_WITH_PARENT,
						    GTK_MESSAGE_ERROR,
						    GTK_BUTTONS_CLOSE,
						    "%s", _
						    ("Unable to open help file"));
			gtk_message_dialog_format_secondary_text
			    (GTK_MESSAGE_DIALOG (d), "%s", error->message);
			g_signal_connect (d, "response",
					  G_CALLBACK (gtk_widget_destroy),
					  NULL);
			gtk_window_present (GTK_WINDOW (d));

			g_error_free (error);
		}

		return;
	}

	g_main_loop_quit (loop);
}

static void
CappletSetup (MatekbdIndicatorPluginsCapplet * gipc)
{
	GtkBuilder *builder;
	GError *error = NULL;
	GtkWidget *button;
	GtkWidget *capplet;
	GtkWidget *activePlugins;
	GtkTreeModel *activePluginsModel;
	GtkCellRenderer *renderer =
	    GTK_CELL_RENDERER (gtk_cell_renderer_text_new ());
	GtkTreeViewColumn *column =
	    gtk_tree_view_column_new_with_attributes (NULL, renderer,
						      "text", 0,
						      NULL);
	GtkTreeSelection *selection;
	builder = gtk_builder_new ();

	gtk_window_set_default_icon_name ("input-keyboard");

	/* default domain! */
	if (!gtk_builder_add_from_file (builder,
	                                UIDIR "/matekbd-indicator-plugins.ui",
	                                &error)) {
		g_warning ("Could not load builder file: %s", error->message);
		g_error_free(error);
		return;
	}

	gipc->capplet = capplet =
	    GTK_WIDGET (gtk_builder_get_object (builder, "matekbd_indicator_plugins"));

	gtk_builder_connect_signals (builder, NULL);

	g_object_set_data (G_OBJECT (capplet), "uiData", builder);

	#if GTK_CHECK_VERSION(3, 0, 0)
		g_signal_connect_swapped(G_OBJECT(capplet), "destroy", G_CALLBACK(g_object_unref), builder);
	#else
		g_signal_connect_swapped(GTK_OBJECT(capplet), "destroy", G_CALLBACK(g_object_unref), builder);
	#endif

	g_signal_connect_swapped (G_OBJECT (capplet), "unrealize", G_CALLBACK (g_main_loop_quit), loop);

	#if GTK_CHECK_VERSION(3, 0, 0)
		g_signal_connect(G_OBJECT(capplet), "response", G_CALLBACK(CappletResponse), NULL);
	#else
		g_signal_connect(GTK_OBJECT(capplet), "response", G_CALLBACK(CappletResponse), NULL);
	#endif


	button = GTK_WIDGET (gtk_builder_get_object (builder, "btnUp"));
	g_signal_connect (button,  "clicked",
				       G_CALLBACK
				       (CappletPromotePlugin), gipc);
	button = GTK_WIDGET (gtk_builder_get_object (builder, "btnDown"));
	g_signal_connect (button,
				       "clicked",
				       G_CALLBACK
				       (CappletDemotePlugin), gipc);
	button = GTK_WIDGET (gtk_builder_get_object (builder, "btnAdd"));
	g_signal_connect (button,  "clicked",
				       G_CALLBACK
				       (CappletEnablePlugin), gipc);
	button = GTK_WIDGET (gtk_builder_get_object (builder, "btnRemove"));
	g_signal_connect (button,  "clicked",
				       G_CALLBACK
				       (CappletDisablePlugin), gipc);
	button = GTK_WIDGET (gtk_builder_get_object (builder, "btnProperties"));
	g_signal_connect (button,  "clicked",
				       G_CALLBACK
				       (CappletConfigurePlugin), gipc);

	activePlugins = CappletGetUiWidget (gipc, "activePlugins");
	activePluginsModel =
	    GTK_TREE_MODEL (gtk_list_store_new
			    (2, G_TYPE_STRING, G_TYPE_STRING));
	gtk_tree_view_set_model (GTK_TREE_VIEW (activePlugins),
				 activePluginsModel);
	gtk_tree_view_append_column (GTK_TREE_VIEW (activePlugins),
				     column);
	selection =
	    gtk_tree_view_get_selection (GTK_TREE_VIEW (activePlugins));
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
	g_signal_connect (G_OBJECT (selection), "changed",
			  G_CALLBACK
			  (CappletActivePluginsSelectionChanged), gipc);
	CappletFillActivePluginList (gipc);
	CappletActivePluginsSelectionChanged (selection, gipc);
	gtk_widget_show_all (capplet);
}

int
main (int argc, char **argv)
{
	MatekbdIndicatorPluginsCapplet gipc;

	GError *mateconf_error = NULL;
	MateConfClient *confClient;

	bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
	textdomain (GETTEXT_PACKAGE);
	memset (&gipc, 0, sizeof (gipc));
	gtk_init_with_args (&argc, &argv, "matekbd", NULL, NULL, NULL);
	if (!mateconf_init (argc, argv, &mateconf_error)) {
		g_warning (_("Failed to init MateConf: %s\n"),
			   mateconf_error->message);
		g_error_free (mateconf_error);
		return 1;
	}
	mateconf_error = NULL;
	/*MatekbdIndicatorInstallGlibLogAppender(  ); */
	gipc.engine = xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()));
	gipc.config_registry =
	    xkl_config_registry_get_instance (gipc.engine);

	confClient = mateconf_client_get_default ();
	matekbd_indicator_plugin_container_init (&gipc.plugin_container,
					      confClient);
	g_object_unref (confClient);

	matekbd_keyboard_config_init (&gipc.kbd_cfg, confClient, gipc.engine);
	matekbd_keyboard_config_init (&initialSysKbdConfig, confClient,
				   gipc.engine);

	matekbd_indicator_config_init (&gipc.applet_cfg, confClient,
				    gipc.engine);

	matekbd_indicator_plugin_manager_init (&gipc.plugin_manager);

	matekbd_keyboard_config_load_from_x_initial (&initialSysKbdConfig,
						  NULL);
	matekbd_keyboard_config_load_from_mateconf (&gipc.kbd_cfg,
					      &initialSysKbdConfig);

	matekbd_indicator_config_load_from_mateconf (&gipc.applet_cfg);

	loop = g_main_loop_new (NULL, TRUE);

	CappletSetup (&gipc);

	g_main_loop_run (loop);

	matekbd_indicator_plugin_manager_term (&gipc.plugin_manager);

	matekbd_indicator_config_term (&gipc.applet_cfg);

	matekbd_keyboard_config_term (&gipc.kbd_cfg);
	matekbd_keyboard_config_term (&initialSysKbdConfig);

	matekbd_indicator_plugin_container_term (&gipc.plugin_container);
	g_object_unref (G_OBJECT (gipc.config_registry));
	g_object_unref (G_OBJECT (gipc.engine));
	return 0;
}

/* functions just for plugins - otherwise ldd is not happy */
void
matekbd_indicator_plugin_container_reinit_ui (MatekbdIndicatorPluginContainer *
					   pc)
{
}

gchar **matekbd_indicator_plugin_load_localized_group_names
    (MatekbdIndicatorPluginContainer * pc) {
	MatekbdDesktopConfig *config =
	    &((MatekbdIndicatorPluginsCapplet *) pc)->cfg;
	XklConfigRegistry *config_registry =
	    ((MatekbdIndicatorPluginsCapplet *) pc)->config_registry;

	int i;
	const gchar **native_names =
	    xkl_engine_get_groups_names (config->engine);
	guint total_groups = xkl_engine_get_num_groups (config->engine);
	guint total_layouts;
	gchar **rv = g_new0 (char *, total_groups + 1);
	gchar **current_descr = rv;

	if ((xkl_engine_get_features (config->engine) &
	     XKLF_MULTIPLE_LAYOUTS_SUPPORTED)
	    && config->layout_names_as_group_names) {
		XklConfigRec *xkl_config = xkl_config_rec_new ();
		if (xkl_config_rec_get_from_server
		    (xkl_config, config->engine)) {
			gchar **sgn = NULL;
			gchar **fgn = NULL;
			matekbd_desktop_config_load_group_descriptions
			    (config, config_registry,
			     (const gchar **) xkl_config->layouts,
			     (const gchar **) xkl_config->variants, &sgn,
			     &fgn);
			g_strfreev (sgn);
			rv = fgn;
		}
		g_object_unref (G_OBJECT (xkl_config));
		/* Worst case - multiple layous - but SOME of them are multigrouped :(((
		 * We cannot do much - just add empty descriptions.
		 * The UI is going to be messy.
		 * Canadian layouts are famous for this sh.t. */
		total_layouts = g_strv_length (rv);
		if (total_layouts != total_groups) {
			xkl_debug (0,
				   "The mismatch between "
				   "the number of groups: %d and number of layouts: %d\n",
				   total_groups, total_layouts);
			current_descr = rv + total_layouts;
			for (i = total_groups - total_layouts; --i >= 0;)
				*current_descr++ = g_strdup ("");
		}
	}
	total_layouts = g_strv_length (rv);
	if (!total_layouts)
		for (i = total_groups; --i >= 0;)
			*current_descr++ = g_strdup (*native_names++);

	return rv;
}
