/**
* This file is a part of the Cairo-Dock project
*
* Copyright : (C) see the 'copyright' file.
* E-mail    : see the 'copyright' file.
*
* 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 3
* of the License, 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, see <http://www.gnu.org/licenses/>.
*/

#include <cairo/cairo-gobject.h>

#include "config.h"
#include "gldi-icon-names.h"
#include "cairo-dock-struct.h"
#include "cairo-dock-gui-factory.h"
#include "cairo-dock-icon-manager.h"  // cairo_dock_search_icon_size
#include "cairo-dock-gui-manager.h"
#include "cairo-dock-container-priv.h"
#include "cairo-dock-log.h"
#include "cairo-dock-keyfile-utilities.h"
#include "cairo-dock-keybinder.h"
#include "cairo-dock-themes-manager.h"  // cairo_dock_update_conf_file
#include "cairo-dock-desktop-manager.h"  // gldi_desktop_get_width
#include "cairo-dock-widget-shortkeys.h"

#define CAIRO_DOCK_PREVIEW_HEIGHT 250

static void _shortkeys_widget_reload (CDWidget *pCdWidget);

typedef enum {
	CD_SHORTKEY_MODEL_NAME = 0,  // demander
	CD_SHORTKEY_MODEL_DESCRIPTION,  // description
	CD_SHORTKEY_MODEL_SURFACE,  // icon image
	CD_SHORTKEY_MODEL_SHORTKEY,  // shortkey
	CD_SHORTKEY_MODEL_STATE,  // grabbed or not
	CD_SHORTKEY_MODEL_BINDING,  // the binding
	CD_SHORTKEY_MODEL_NB_COLUMNS
	} CDModelColumns;
static void _on_key_grab_cb (GtkWidget *pInputDialog, GdkEventKey *event, GtkTreeView *pTreeView)
{
	gchar *key;
	cd_debug ("key pressed");
	if (gtk_accelerator_valid (event->keyval, event->state))
	{
		/* This lets us ignore all ignorable modifier keys, including
		* NumLock and many others. :)
		*
		* The logic is: keep only the important modifiers that were pressed
		* for this event. */
		event->state &= gtk_accelerator_get_default_mod_mask();

		/* Generate the correct name for this key */
		key = gtk_accelerator_name (event->keyval, event->state);

		cd_debug ("KEY GRABBED: '%s'", key);

		// get the key binding
		GtkTreeSelection *pSelection = gtk_tree_view_get_selection (pTreeView);
		GtkTreeModel *pModel;
		GtkTreeIter iter;
		if (gtk_tree_selection_get_selected (pSelection, &pModel, &iter))  // we retrieve it from the current selection, this way if the binding has been destroyed meanwhile (very unlikely), we're safe.
		{
			// Bind the new shortkey
			GldiShortkey *binding = NULL;
			gtk_tree_model_get (pModel, &iter,
				CD_SHORTKEY_MODEL_BINDING, &binding, -1);
			
			gldi_shortkey_rebind (binding, key, NULL);
			
			// update the model
			gtk_list_store_set (GTK_LIST_STORE (pModel), &iter,
				CD_SHORTKEY_MODEL_SHORTKEY, binding->keystring,
				CD_SHORTKEY_MODEL_STATE, binding->bSuccess, -1);
			
			// store the new shortkey
			if (binding->cConfFilePath != NULL)
			{
				cairo_dock_update_conf_file (binding->cConfFilePath,
					G_TYPE_STRING, binding->cGroupName, binding->cKeyName, key,
					G_TYPE_INVALID);
			}
		}
			
		// Close the window
		g_free (key);
		gtk_widget_destroy (pInputDialog);
	}
}
static void on_cancel_shortkey (G_GNUC_UNUSED GtkButton *button, GtkWidget *pInputDialog)
{
	gtk_widget_destroy (pInputDialog);
}
static void _cairo_dock_initiate_change_shortkey (G_GNUC_UNUSED GtkMenuItem *pMenuItem, ShortkeysWidget *pShortkeysWidget)
{
	GtkTreeView *pTreeView = GTK_TREE_VIEW (pShortkeysWidget->pShortKeysTreeView);

	// ensure a line is selected
	GtkTreeSelection *pSelection = gtk_tree_view_get_selection (pTreeView);
	GtkTreeModel *pModel;
	GtkTreeIter iter;
	if (! gtk_tree_selection_get_selected (pSelection, &pModel, &iter))
		return ;

	// build a small modal input dialog, mainly to prevent any other interaction.
	GtkWidget *pInputDialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	// gtk_window_set_decorated (GTK_WINDOW (pInputDialog), FALSE); -- does the opposite on Wayland, better not use it
	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (pInputDialog), TRUE);
	gtk_window_set_skip_pager_hint (GTK_WINDOW (pInputDialog), TRUE);
	gtk_window_set_transient_for (GTK_WINDOW (pInputDialog), pShortkeysWidget->pMainWindow);
	gtk_window_set_modal (GTK_WINDOW (pInputDialog), TRUE);

	gtk_widget_add_events (pInputDialog, GDK_KEY_PRESS_MASK);
	g_signal_connect (GTK_WIDGET(pInputDialog), "key-press-event", G_CALLBACK(_on_key_grab_cb), pTreeView);

	GtkWidget *pMainVBox = gtk_box_new (GTK_ORIENTATION_VERTICAL, CAIRO_DOCK_FRAME_MARGIN);
	gtk_container_add (GTK_CONTAINER (pInputDialog), pMainVBox);

	GtkWidget *pLabel = gtk_label_new (_("Press the shortkey"));
	gtk_box_pack_start(GTK_BOX (pMainVBox), pLabel, FALSE, FALSE, 0);

	GtkWidget *pCancelButton = gtk_button_new_with_label (_("Cancel"));
	g_signal_connect (G_OBJECT (pCancelButton), "clicked", G_CALLBACK(on_cancel_shortkey), pInputDialog);
	gtk_box_pack_start (GTK_BOX (pMainVBox), pCancelButton, FALSE, FALSE, 0);

	gtk_widget_show_all (pInputDialog);
}
static gboolean _on_click_shortkey_tree_view (G_GNUC_UNUSED GtkTreeView *pTreeView, GdkEventButton* pButton, gpointer data)
{
	if ((pButton->button == 3 && pButton->type == GDK_BUTTON_RELEASE)  // right click
	|| (pButton->button == 1 && pButton->type == GDK_2BUTTON_PRESS))  // double click
	{
		ShortkeysWidget *pShortkeysWidget = (ShortkeysWidget*)data;
		if (pButton->button == 3)
		{
			GtkWidget *pMenu = gtk_menu_new ();
			cairo_dock_gui_menu_item_add (pMenu, _("Change the shortkey"), GLDI_ICON_NAME_PROPERTIES, G_CALLBACK (_cairo_dock_initiate_change_shortkey), pShortkeysWidget);
			gtk_widget_show_all (pMenu);
			gtk_menu_popup_at_pointer (GTK_MENU (pMenu), NULL);
		}
		else
		{
			_cairo_dock_initiate_change_shortkey (NULL, pShortkeysWidget);
		}
	}
	return FALSE;
}
static void _cairo_dock_render_shortkey (G_GNUC_UNUSED GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *model,GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
{
	gboolean bActive = FALSE;
	gchar *cShortkey = NULL;
	gtk_tree_model_get (model, iter,
		CD_SHORTKEY_MODEL_STATE, &bActive,
		CD_SHORTKEY_MODEL_SHORTKEY, &cShortkey, -1);
	
	if (bActive)
	{
		g_object_set (cell, "foreground", "#108000", NULL);  // vert
		g_object_set (cell, "foreground-set", TRUE, NULL);
	}
	else if (cShortkey != NULL)
	{
		g_object_set (cell, "foreground", "#B00000", NULL);  // rouge
		g_object_set (cell, "foreground-set", TRUE, NULL);
	}
	else
	{
		g_object_set (cell, "foreground-set", FALSE, NULL);
	}
	g_free (cShortkey);
}
void cairo_dock_add_shortkey_to_model (GldiShortkey *binding, GtkListStore *pModel)
{
	cd_debug ("Add shortkey with image: ",  binding->cIconFilePath);
	int iSize = cairo_dock_search_icon_size (GTK_ICON_SIZE_LARGE_TOOLBAR);
	cairo_surface_t *surface = cairo_dock_create_surface_from_icon (binding->cIconFilePath,
		iSize, iSize);
	GtkTreeIter iter;
	memset (&iter, 0, sizeof (GtkTreeIter));
	gtk_list_store_append (GTK_LIST_STORE (pModel), &iter);
	// cd_debug (" + %s (%s, %d)", binding->keystring, binding->cDemander, binding->bSuccess);
	gtk_list_store_set (GTK_LIST_STORE (pModel), &iter,
		CD_SHORTKEY_MODEL_NAME, binding->cDemander,
		CD_SHORTKEY_MODEL_SHORTKEY, binding->keystring,
		CD_SHORTKEY_MODEL_DESCRIPTION, binding->cDescription,
		CD_SHORTKEY_MODEL_SURFACE, surface,
		CD_SHORTKEY_MODEL_STATE, binding->bSuccess,
		CD_SHORTKEY_MODEL_BINDING, binding, -1);
	if (surface) cairo_surface_destroy (surface);
}
static GtkWidget *_cairo_dock_build_shortkeys_widget (ShortkeysWidget *pShortkeysWidget)
{
	// fill the model
	GtkListStore *pModel = gtk_list_store_new (CD_SHORTKEY_MODEL_NB_COLUMNS,
		G_TYPE_STRING,  // demander
		G_TYPE_STRING,  // description
		CAIRO_GOBJECT_TYPE_SURFACE,  // icon
		G_TYPE_STRING,  // shortkey
		G_TYPE_BOOLEAN,  // grabbed or not
		G_TYPE_POINTER);  // binding
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (pModel), CD_SHORTKEY_MODEL_NAME, GTK_SORT_ASCENDING);
	gldi_shortkeys_foreach ((GFunc) cairo_dock_add_shortkey_to_model, pModel);
	
	// make the treeview
	GtkWidget *pOneWidget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (pModel));
	g_object_unref (pModel);
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (pOneWidget), TRUE);
	g_signal_connect (G_OBJECT (pOneWidget), "button-press-event", G_CALLBACK (_on_click_shortkey_tree_view), pShortkeysWidget);
	g_signal_connect (G_OBJECT (pOneWidget), "button-release-event", G_CALLBACK (_on_click_shortkey_tree_view), pShortkeysWidget);
	
	// define the rendering of the treeview
	GtkTreeViewColumn* col;
	GtkCellRenderer *rend;
	// icon
	rend = gtk_cell_renderer_pixbuf_new ();
	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (pOneWidget), -1, NULL, rend, "surface", CD_SHORTKEY_MODEL_SURFACE, NULL);
	// demander
	rend = gtk_cell_renderer_text_new ();
	col = gtk_tree_view_column_new_with_attributes (_("Origin"), rend, "text", CD_SHORTKEY_MODEL_NAME, NULL);
	gtk_tree_view_column_set_sort_column_id (col, CD_SHORTKEY_MODEL_NAME);
	gtk_tree_view_append_column (GTK_TREE_VIEW (pOneWidget), col);
	// action description
	rend = gtk_cell_renderer_text_new ();
	col = gtk_tree_view_column_new_with_attributes (_("Action"), rend, "text", CD_SHORTKEY_MODEL_DESCRIPTION, NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW (pOneWidget), col);
	// shortkey
	rend = gtk_cell_renderer_text_new ();
	col = gtk_tree_view_column_new_with_attributes (_("Shortkey"), rend, "text", CD_SHORTKEY_MODEL_SHORTKEY, NULL);
	gtk_tree_view_column_set_cell_data_func (col, rend, (GtkTreeCellDataFunc)_cairo_dock_render_shortkey, NULL, NULL);
	gtk_tree_view_column_set_sort_column_id (col, CD_SHORTKEY_MODEL_SHORTKEY);
	gtk_tree_view_append_column (GTK_TREE_VIEW (pOneWidget), col);
	
	return pOneWidget;
}


ShortkeysWidget *cairo_dock_shortkeys_widget_new (GtkWindow *pMainWindow)
{
	ShortkeysWidget *pShortkeysWidget = g_new0 (ShortkeysWidget, 1);
	pShortkeysWidget->widget.iType = WIDGET_SHORTKEYS;
	pShortkeysWidget->widget.reload = _shortkeys_widget_reload;
	
	pShortkeysWidget->pShortKeysTreeView = _cairo_dock_build_shortkeys_widget (pShortkeysWidget);
	pShortkeysWidget->pMainWindow = pMainWindow;
	
	GtkWidget *pScrolledWindow = gtk_scrolled_window_new (NULL, NULL);
	g_object_set (pScrolledWindow, "height-request", MIN (2*CAIRO_DOCK_PREVIEW_HEIGHT, gldi_desktop_get_height() - 175), NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pScrolledWindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	gtk_container_add (GTK_CONTAINER (pScrolledWindow), pShortkeysWidget->pShortKeysTreeView);
	
	if (! gldi_desktop_can_grab_shortkey ())
	{
		GtkWidget *pBoxV = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
		GtkWidget *pBoxH = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
		GtkWidget *pBoxT = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
		GtkWidget *pImg  = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_MENU);
		GtkWidget *pLbl  = gtk_label_new (_(
"You are running Cairo-Dock in a session where setting global keyboard shortcuts is not supported; "
"currently, this is the case for most Wayland compositors. You may need to check your compositor's "
"settings and explicitly enable keybindings by external apps. See our Wiki for more information:"));
		gtk_label_set_line_wrap (GTK_LABEL (pLbl), TRUE);
		GtkWidget *pLink = gtk_link_button_new ("https://github.com/Cairo-Dock/cairo-dock-core/wiki");
		gtk_box_pack_start (GTK_BOX (pBoxT), pLbl, FALSE, FALSE, 0);
		gtk_box_pack_start (GTK_BOX (pBoxT), pLink, FALSE, FALSE, 0);
		gtk_box_pack_start (GTK_BOX (pBoxH), pImg, FALSE, FALSE, 20);
		gtk_box_pack_start (GTK_BOX (pBoxH), pBoxT, FALSE, FALSE, 20);
		gtk_container_add (GTK_CONTAINER (pBoxV), pBoxH);
		gtk_widget_set_sensitive (pShortkeysWidget->pShortKeysTreeView, FALSE);
		gtk_container_add (GTK_CONTAINER (pBoxV), pScrolledWindow);
		pShortkeysWidget->widget.pWidget = pBoxV;
	}
	else pShortkeysWidget->widget.pWidget = pScrolledWindow;
	
	return pShortkeysWidget;
}


static void _shortkeys_widget_reload (CDWidget *pCdWidget)
{
	ShortkeysWidget *pShortkeysWidget = SHORKEYS_WIDGET (pCdWidget);
	GtkTreeModel *pModel = gtk_tree_view_get_model (GTK_TREE_VIEW (pShortkeysWidget->pShortKeysTreeView));
	g_return_if_fail (pModel != NULL);
	gtk_list_store_clear (GTK_LIST_STORE (pModel));
	gldi_shortkeys_foreach ((GFunc) cairo_dock_add_shortkey_to_model, pModel);
}
