/* Erwann Chenede 2002 */

#include <config.h>

#include <string.h>
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>
#include <gdk/gdkx.h>
#include <glade/glade.h>
#include <X11/Xatom.h>

#include "wm-common.h"
#include "capplet-util.h"
#include "eggcellrendererkeys.h"
#include "activate-settings-daemon.h"
#include "custom-binding.h"

extern KeyListEntry *custom_binding_list;
extern GladeXML *main_dialog;

void
cmd_line_changed (GConfClient *client,
		  guint        cnxn_id,
		  GConfEntry  *entry,
		  gpointer     user_data)
{
  KeyEntry *key_entry;

  key_entry = (KeyEntry *)user_data;
  g_free (key_entry->action);
  key_entry->action = g_strdup (gconf_value_get_string (entry->value));
  key_entry->editable = gconf_entry_get_is_writable (entry);

  /* update the model */
  gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry);
}

KeyEntry *
find_keyentry_selected (GtkTreeView *tree_view)
{
  GtkTreeSelection* sel = NULL;
  sel = gtk_tree_view_get_selection (tree_view);
  
  if (sel)
    {
      GtkTreeIter iter;
      GtkTreeModel *model;
      if (gtk_tree_selection_get_selected (sel, &model, &iter))
	{
	  KeyEntry *key_entry;

	  gtk_tree_model_get (model, &iter, KEYENTRY_COLUMN, &key_entry, -1);
	  return key_entry;
	}
    }
  return NULL;
}
	
static gboolean key_delete_cleanup (KeyEntry *key_entry)
{
  GConfClient *client = gconf_client_get_default();

  gconf_client_recursive_unset (client, key_entry->gconf_key, 0, NULL);

  create_custom_bindings_list ();
  reload_key_entries (wm_common_get_current_window_manager(), main_dialog);

  return FALSE;
}

void 
disable_custom (GtkWidget *widget, GtkTreeView *tree_view)
{
  KeyEntry *key_entry = find_keyentry_selected (tree_view);
  
  if (key_entry)
    {
      if (key_entry->custom_key)
	{
	  GConfClient *client = gconf_client_get_default();
	  gchar *binding = g_strdup_printf ("%s/binding", key_entry->gconf_key);
	  gchar *binding_str;

	  gconf_client_notify_remove (client, key_entry->gconf_cnxn);
	  gconf_client_notify_remove (client, key_entry->cmd_line_cnxn);

	  key_entry->gconf_cnxn = 0;
	  key_entry->cmd_line_cnxn = 0;

	  binding_str = gconf_client_get_string (client,binding, NULL);
	
	  if (binding_str && strcmp (binding_str, "disabled")) {
		/* This is a neccessery evil. We need to follow a similar
		   pattern of sequence in setting the binding to disabled state
		   before deleting it. The timeout is to ensure that when
		   g-s-d is getting the changes it does not find a key which
		   is non-existent and crash.
		*/
		gconf_client_set_string (client,
					binding,
					"disabled",
					NULL);
	  	g_timeout_add (1000,  (GSourceFunc) key_delete_cleanup, key_entry);

		g_free (binding_str);
	  } else 
		key_delete_cleanup (key_entry);

	  g_free (binding);
	}
      else
	gconf_client_set_string (gconf_client_get_default(),key_entry->gconf_key, "", NULL);
    }
}

static int
get_trailing_num (char *str)
{
  char **result = g_strsplit (str, "_", 2);
  int i = atoi (result[1]);
  g_strfreev (result);
  return i;
}

static char *
find_free_custom_gconf_key ()
{
  GConfClient *client;
  GSList *list, *li;
  int num_bindings = 0;
  int max_num = 0;
  
  client  = gconf_client_get_default ();

  list = gconf_client_all_dirs (client, GCONF_CUSTOM_BINDING_DIR, NULL);

  num_bindings = g_slist_length (list);

  /*find the highest num */

  for (li = list; li != NULL; li = li->next)
    {
      char *subdir = li->data;
      char *key = g_path_get_basename(subdir);
      li->data = NULL;

      if (g_ascii_strncasecmp(key, "custom_", 7) == 0)
	{
	  int i = get_trailing_num (key);
	  if (i > max_num)
	    max_num = i;
	}
    }
  g_slist_free (list);
  
  return g_strdup_printf ("%s/custom_%d",GCONF_CUSTOM_BINDING_DIR, max_num + 1);
}


static void 
add_real_key (GtkWidget *widget, GladeXML *dialog)
{
  KeyEntry *entry = NULL;
  GError *err = NULL;
  GtkWidget *area_tf = WID ("action_tf");
  char *binding_key, *action_key;
  char *action = g_strdup (gtk_entry_get_text (GTK_ENTRY (area_tf)));

  entry = g_object_steal_data (G_OBJECT (area_tf), "key_entry");
  
  if (!entry)
    {
      char *new_gconf_key = find_free_custom_gconf_key ();
      GConfClient *client = gconf_client_get_default ();
      
      binding_key = g_strdup_printf ("%s/binding", new_gconf_key);
      action_key = g_strdup_printf ("%s/action", new_gconf_key);
      
      gconf_client_set_string (client, new_gconf_key, "", &err);
      
      gconf_client_set_string  (client, binding_key, "", &err);
      
      gconf_client_set_string  (client,	action_key, action, &err);
      
      create_custom_bindings_list ();
      reload_key_entries (wm_common_get_current_window_manager(), main_dialog);
      g_free (binding_key);
      g_free (new_gconf_key);
    }
  else
    {
      action_key = g_strdup_printf ("%s/action", entry->gconf_key);
      gconf_client_set_string  (gconf_client_get_default (),
				action_key, action, &err);
    }

  g_free (action);
  g_free (action_key);

  custom_cancel (widget, dialog);
}

void 
custom_cancel (GtkWidget *widget, GladeXML *dialog)
{
  gtk_entry_set_text (GTK_ENTRY (WID ("action_tf")), "");

  gtk_widget_hide (WID ("custom-binding-dialog"));
}

void
delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer *user_data)
{
  GtkWidget *entry = GTK_WIDGET (user_data);
  gtk_entry_set_text (GTK_ENTRY (entry), "");

  gtk_widget_hide (widget);
}

static void 
cb_dialog_response (GtkWidget *widget, GladeXML *dialog)
{
	capplet_help (GTK_WINDOW (gtk_widget_get_toplevel (widget)),
		      "user-guide.xml",
		      "goscustdesk-39");
}

static void 
set_command (GtkWidget *widget, GladeXML *dialog)
{
  GtkWidget *window;

  window = gtk_file_chooser_dialog_new (_("Select Command"), NULL,
					GTK_FILE_CHOOSER_ACTION_OPEN,
					GTK_STOCK_CANCEL, 
					GTK_RESPONSE_CANCEL,
					GTK_STOCK_OK, 
					GTK_RESPONSE_OK,
					NULL);	

  if (gtk_dialog_run(GTK_DIALOG(window)) == GTK_RESPONSE_OK)
	{
	  GtkWidget *action_tf = glade_xml_get_widget (dialog, "action_tf");
	  gchar *selection;
		  
	  selection = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(window));
	  gtk_entry_set_text (GTK_ENTRY (action_tf), selection);
	}
  gtk_widget_destroy(GTK_WIDGET(window));
}

GladeXML *
setup_custom_binding_dialog ()
{
  static GladeXML *dialog = NULL;
  GtkWidget *widget, *entry;

  if (dialog)
    return dialog;

  /* setup dialog */
  dialog = glade_xml_new (GNOMECC_DATA_DIR "/interfaces/gnome-keybinding-properties.glade", "custom-binding-dialog", NULL);
  
  widget = WID ("custom-binding-dialog");
  entry = WID ("action_tf");
  g_signal_connect (G_OBJECT (widget), "delete_event",
		    G_CALLBACK (delete_event_cb),
		    entry);

  g_signal_connect (G_OBJECT (WID ("browse_button")), "clicked", 
		    G_CALLBACK (set_command), dialog);

  g_signal_connect (G_OBJECT (WID ("cancelbutton1")), "clicked", 
		    G_CALLBACK (custom_cancel), dialog);

  g_signal_connect (G_OBJECT (WID ("okbutton1")), "clicked", 
		    G_CALLBACK (add_real_key), dialog);
  
  g_signal_connect (G_OBJECT (WID ("helpbutton2")), "clicked", 
		    G_CALLBACK (cb_dialog_response), NULL);

  /* gtk_widget_hide (WID ("custom-binding-dialog")); */
  return dialog;
}
void 
edit_custom (GtkWidget *widget)
{
  KeyEntry *entry = find_keyentry_selected (GTK_TREE_VIEW (glade_xml_get_widget (main_dialog,"shortcut_treeview")));
  if (entry)
    {
      if (entry->custom_key)
	{
	  GladeXML *dialog = setup_custom_binding_dialog ();
	  GtkWidget *widget;
	  widget =  WID ("action_tf");
	  g_object_set_data (G_OBJECT (widget), "key_entry", entry);
	  gtk_entry_set_text (GTK_ENTRY (widget), entry->action);
	  gtk_widget_show_all (WID ("custom-binding-dialog"));
	}
    }
}
void 
add_custom (GtkWidget *widget) 
{
  GladeXML *dialog = setup_custom_binding_dialog ();
  GtkWidget *action_widget;
  action_widget =  WID ("action_tf");
  g_object_set_data (G_OBJECT (action_widget), "key_entry", NULL);
  gtk_widget_show_all (WID ("custom-binding-dialog"));
}

void
create_custom_bindings_list ()
{
  GConfClient *client;
  GSList *list, *li;
  int num_bindings = 0, i = 0;
  
  client  = gconf_client_get_default ();

  list = gconf_client_all_dirs (client, GCONF_CUSTOM_BINDING_DIR, NULL);

  num_bindings = g_slist_length (list);

  if (custom_binding_list)
      g_free (custom_binding_list);
    
  custom_binding_list = g_new0 (KeyListEntry, num_bindings+1);

  for (li = list; li != NULL; li = li->next)
    {
      char *subdir = li->data;
      li->data = NULL;
      custom_binding_list[i].name = subdir;
      custom_binding_list[i].visibility = ALWAYS_VISIBLE;
      i++;
    }
  g_slist_free (list);
}

KeyEntry *
custom_key_new (char *dir)
{
  GConfValue *value;
  KeyEntry *new_binding;
  GSList *list = NULL, *li;
  char *gconf_key;
  char *action = NULL;
  char *key = NULL;
  
  g_return_val_if_fail (dir != NULL, NULL);
  
  /* value = gconf_entry_get_value (entry); */
  gconf_key = dir;

  if (!gconf_key)
    return NULL;

  /* Get entries for this binding */
  list = gconf_client_all_entries (gconf_client_get_default (), dir, NULL);

  for (li = list; li != NULL; li = li->next)
    {
      GConfEntry *entry = li->data;
      char *key_name = g_path_get_basename (gconf_entry_get_key (entry));
      if (strcmp (key_name, "action") == 0)
	{
	  if (!action)
	    {
	      value = gconf_entry_get_value (entry);
              if (value) 
               {
	          if (value->type != GCONF_VALUE_STRING)
		     return NULL;
	          action = g_strdup (gconf_value_get_string (value));
              }
	    }
	  else
	    g_warning (_("Key Binding (%s) has its action defined multiple times\n"),
		       gconf_key);
	}
      if (strcmp (key_name, "binding") == 0)
	{
	  if (!key)
	    {
	      value = gconf_entry_get_value (entry);
              if (value)
                {
	          if (value->type != GCONF_VALUE_STRING)
		     return NULL;
	          key = g_strdup (gconf_value_get_string (value));
               }
	    }
	  else
	    g_warning (_("Key Binding (%s) has its binding defined multiple times\n"),
		       gconf_key);
	}
    }
  if (!action || !key)
    { 
      g_warning (_("Key Binding (%s) is incomplete\n"), gconf_key);
      return NULL;
    }
  
  new_binding = g_new0 (KeyEntry, 1);
  
  
  new_binding->binding = key;
  new_binding->action = action;
  new_binding->gconf_key = gconf_key;
  return new_binding;
}

gboolean
is_gconf_key_custom_binding (char *key)
{
  char *str = NULL;

  str = g_strrstr (key, GCONF_CUSTOM_BINDING_DIR);

  return str ? 1 : 0;
  
}
