/*
 * This file is part of GNAC - Gnome Audio Converter
 *
 * Copyright (C) 2007 - 2012 Gnac
 *    
 *    - DUPASQUIER  Benoit    <bdupasqu@src.gnome.org>
 *    - JOAQUIM     David     <djoaquim@src.gnome.org>
 *    - ROUX        Alexandre <alexroux@src.gnome.org>
 *
 * GNAC 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.
 *
 * GNAC 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 GNAC; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, 
 * Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gio/gio.h>
#include <glib/gi18n.h>

#include "gnac-main.h"
#include "gnac-profiles.h"
#include "gnac-profiles-default.h"
#include "gnac-profiles-manager.h"
#include "gnac-settings.h"
#include "gnac-ui.h"
#include "gnac-ui-utils.h"
#include "gnac-utils.h"

/* Translators: Suffix added to a copied profile: 'profile (copy).xml' */
#define GNAC_COPY_SUFFIX N_(" (copy)")

enum {
  COL_NAME,
  COL_FORMAT,
  COL_EXTENSION,
  COL_PROFILE,
  NUM_COLS
};

enum {
  DND_TARGET_URI,
  DND_TARGET_PLAIN
};

typedef struct {
  gchar **uris;
  guint   info;
} ThreadCopyData;

typedef struct {
  const gchar  *path;
  const gchar  *name;
  GError      **error;
  gboolean      toggle;
} CopyData;

GtkBuilder *profiles_mgr_builder = NULL;
gchar *saved_profiles_dir;

static GtkTargetEntry target_list[] =  {
  { "text/uri-list", 0, DND_TARGET_URI   },
  { "text/plain"   , 0, DND_TARGET_PLAIN }
};

static guint n_targets = G_N_ELEMENTS(target_list);

static void
gnac_profiles_mgr_populate(void);

static void
gnac_profiles_mgr_import_default_profiles(void);

static void
gnac_profiles_mgr_clear(void);

static void
gnac_profiles_mgr_insert(gpointer profile);

static void
gnac_profiles_mgr_remove(GtkTreeRowReference *ref);

static void
gnac_profiles_mgr_delete_profile_file(const gchar *name);

static void
gnac_profiles_mgr_get(GtkTreeRowReference  *ref,
                      gint                  col_number,
                      AudioProfileGeneric **profile);

static void
gnac_profiles_mgr_set(GtkTreeRowReference *ref, ...);

static GList *
gnac_profiles_mgr_get_selected_rows(void);

static void
gnac_profiles_mgr_activate_buttons(gboolean activate);

static void
gnac_profiles_mgr_set_progress_bar_fraction(gdouble fraction);

static void
gnac_profiles_mgr_display_status_message(const gchar *ok,
                                         const gchar *error);

static void
gnac_profiles_mgr_set_window_sensitive(gboolean sensitive);

static void
gnac_profiles_mgr_copy_and_load_files(gpointer data);

static gchar **
gnac_profiles_mgr_get_selected_uris(void);

static void
gnac_profiles_mgr_on_drag_profile_copied(goffset  current_num_bytes,
                                         goffset  total_num_bytes,
                                         gpointer user_data);

static void
gnac_profiles_mgr_on_edit_profile(GtkWidget *widget,
                                  gpointer   data);

static void
gnac_profiles_mgr_on_new_profile(GtkWidget *widget,
                                 gpointer   data);

static void
gnac_profiles_mgr_on_treeselection_changed(void);


static GObject *
gnac_profiles_mgr_get_object(const gchar *object_name)
{
  return gnac_ui_utils_get_object(profiles_mgr_builder, object_name);
}


static GtkWidget *
gnac_profiles_mgr_get_widget(const gchar *widget_name)
{
  return gnac_ui_utils_get_widget(profiles_mgr_builder, widget_name);
}


static void
gnac_profiles_mgr_set_widget_sensitive(const gchar *widget_name,
                                       gboolean     sensitive)
{
  gnac_ui_utils_set_widget_sensitive(profiles_mgr_builder, widget_name, sensitive);
}


static void
gnac_profiles_mgr_show_widget(const gchar *widget_name,
                              gboolean     show)
{
  GtkWidget *widget = gnac_profiles_mgr_get_widget(widget_name);
  if (show) {
    gtk_widget_show_all(widget);
  } else {
    gtk_widget_hide(widget);
  }
}


static void
gnac_profiles_mgr_show_description_frame(gboolean show)
{
  gnac_profiles_mgr_show_widget("frame-description", show);
}


static void
gnac_profiles_mgr_show_import_progressbar(gboolean show)
{
  gnac_profiles_mgr_show_widget("frame-import", show);
}


static void
gnac_profiles_mgr_set_parent(void)
{
  GtkWindow *parent = GTK_WINDOW(gnac_ui_get_widget("main_window"));
  GtkWindow *window = GTK_WINDOW(
      gnac_profiles_mgr_get_widget("profile_manager_window"));
  gtk_window_set_transient_for(window, parent);
  gnac_profiles_properties_set_parent(window);
}


void
gnac_profiles_mgr_init(void)
{
  profiles_mgr_builder = gnac_ui_utils_create_gtk_builder(
      PKGDATADIR "/profiles/gnac-profiles-manager.xml");

  GtkTreeView *view = GTK_TREE_VIEW(
      gnac_profiles_mgr_get_widget("profile_treeview"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection(view);

  g_signal_connect(G_OBJECT(selection), "changed", 
      G_CALLBACK(gnac_profiles_mgr_on_treeselection_changed), NULL);

  gtk_drag_dest_set(GTK_WIDGET(view), GTK_DEST_DEFAULT_ALL, target_list,
      n_targets, GDK_ACTION_COPY);

  gtk_drag_source_set(GTK_WIDGET(view), GDK_BUTTON1_MASK, target_list,
      n_targets, GDK_ACTION_COPY);

  gnac_profiles_properties_init();
  gnac_profiles_mgr_set_parent();

  gnac_profiles_mgr_show_import_progressbar(FALSE);
  gnac_profiles_mgr_display_status_message(NULL, NULL);

  gnac_profiles_mgr_populate();
}


static GFile *
gnac_profiles_mgr_get_profiles_dir(void)
{
  saved_profiles_dir = g_build_filename(g_get_user_data_dir(),
      PACKAGE, "profiles", NULL);
  GFile *dir = g_file_new_for_path(saved_profiles_dir);
  if (g_file_query_exists(dir, NULL)) {
    return dir;
  }

  GError *error = NULL;

  if (!g_file_make_directory_with_parents(dir, NULL, &error)) {
    g_warning("%s: %s",
        _("Unable to create the profiles directory"), error->message);
    g_clear_error(&error);
    return NULL;
  }

  return dir;
}


void
gnac_profiles_mgr_list_profiles(void)
{
  GFile *dir = gnac_profiles_mgr_get_profiles_dir();

  GError *error = NULL;
  GFileEnumerator *files = g_file_enumerate_children(dir,
      G_FILE_ATTRIBUTE_STANDARD_NAME ","
      G_FILE_ATTRIBUTE_STANDARD_TYPE,
      G_FILE_QUERY_INFO_NONE, NULL, &error);

  if (!files && error) {
    g_clear_error(&error);
    /* no profiles found, try to import the default ones */
    gnac_profiles_mgr_import_default_profiles();
    files = g_file_enumerate_children(dir,
        G_FILE_ATTRIBUTE_STANDARD_NAME ","
        G_FILE_ATTRIBUTE_STANDARD_TYPE,
        G_FILE_QUERY_INFO_NONE, NULL, &error);
    if (!files && error) {
      g_printerr(_("No profiles available"));
      g_warning("%s", error->message);
      g_clear_error(&error);
      return;
    }
  }

  g_print(_("Available audio profiles:"));
  g_print("\n\n");

  gchar *last_used_profile = gnac_settings_get_string(
      GNAC_KEY_LAST_USED_PROFILE);

  GFile *profile_file = g_file_enumerator_get_container(files);
  gchar *profile_file_path = g_file_get_path(profile_file);

  GFileInfo *file_info;
  GSList *profiles = NULL;

  while ((file_info = g_file_enumerator_next_file(files, NULL, NULL))) {
    if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_REGULAR) {
      AudioProfileGeneric *profile;
      const gchar *profile_file_name = g_file_info_get_name(file_info);
      gchar *profile_file_full_path = g_build_filename(profile_file_path,
          profile_file_name, NULL);
      gnac_profiles_default_load_generic_audio_profile(profile_file_full_path,
          &profile);
      if (profile) {
        gpointer name = (profile->generic)->name;
        profiles = g_slist_prepend(profiles, name);
      }

      g_free(profile_file_full_path);
    }

    g_object_unref(file_info);
  }

  g_free(profile_file_path);

  profiles = g_slist_reverse(profiles);

  guint count = g_slist_length(profiles);
  if (count == 0) {
    g_print("\t");
    g_print(_("No profiles available"));
    g_print("\n");
  } else {
    /* check if last_used_profile exists */
    if (!g_slist_find_custom(profiles, last_used_profile,
        (GCompareFunc) g_strcmp0))
    {
      last_used_profile = NULL;
    }

    GSList *tmp;
    for (tmp = profiles; tmp; tmp = g_slist_next(tmp)) {
      gint pos = g_slist_position(profiles, tmp);
      gchar *count_str = g_strdup_printf("%u", pos+1);
      /* if last_used_profile is not set, assume the
       * first profile was last used */
      gchar *name = tmp->data;
      gboolean found = ((pos == 0 && !last_used_profile)
          || gnac_utils_str_equal(name, last_used_profile));
      g_print("\t%2s) %s\n", found ? "*" : count_str, name);
      g_free(count_str);
    }
  }

  g_print("\n");

  g_slist_free(profiles);
  g_free(last_used_profile);
  g_object_unref(dir);
  g_file_enumerator_close(files, NULL, NULL);
  g_object_unref(files);
}


static void
gnac_profiles_mgr_populate(void)
{ 
  gnac_profiles_mgr_clear();

  GError *error = NULL;

  GFile *dir = g_file_new_for_path(saved_profiles_dir);
  if (!g_file_query_exists(dir, NULL)) {
    if (!g_file_make_directory_with_parents(dir, NULL, &error)) {
      const gchar *msg = N_("Unable to create the profiles directory");
      g_warning("%s %s: %s",
          msg, _("You may not be able to save your profiles"), error->message);
      gnac_profiles_mgr_display_status_message(NULL, msg);
      g_clear_error(&error);
      return;
    } else {
      gnac_profiles_mgr_import_default_profiles();
    }
  }

  GFileEnumerator *files = g_file_enumerate_children(dir,
      G_FILE_ATTRIBUTE_STANDARD_NAME ","
      G_FILE_ATTRIBUTE_STANDARD_TYPE,
      G_FILE_QUERY_INFO_NONE, NULL, &error);
  if (!files && error) {
    const gchar *msg = N_("Unable to browse the profiles directory");
    g_warning("%s: %s", msg, error->message);
    gnac_profiles_mgr_display_status_message(NULL, msg);
    g_clear_error(&error);
    return;
  }

  GFileInfo *file_info = g_file_enumerator_next_file(files, NULL, NULL);
  if (!file_info) {
    gnac_profiles_mgr_import_default_profiles();
    g_file_enumerator_close(files, NULL, NULL);
    files = g_file_enumerate_children(dir,
        G_FILE_ATTRIBUTE_STANDARD_NAME ","
        G_FILE_ATTRIBUTE_STANDARD_TYPE,
        G_FILE_QUERY_INFO_NONE, NULL, &error);
    file_info = g_file_enumerator_next_file(files, NULL, NULL);
  }

  GFile *profile_file = g_file_enumerator_get_container(files);
  gchar *profile_file_path = g_file_get_path(profile_file);

  while (file_info) {
    if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_REGULAR) {
      const gchar *profile_file_name = g_file_info_get_name(file_info);
      gchar *profile_file_full_path = g_build_filename(profile_file_path,
          profile_file_name, NULL);
      gpointer profile = gnac_profiles_properties_load_profile_from_file(
          profile_file_full_path, profile_file_name, &error);

      g_free(profile_file_full_path);

      if (profile) {
        gnac_profiles_mgr_insert(profile);
      } else if (error) {
        g_warning("%s", error->message);
        g_clear_error(&error);
      }
    }

    g_object_unref(file_info);
    file_info = g_file_enumerator_next_file(files, NULL, NULL);
  }

  gnac_profiles_mgr_display_status_message(NULL, NULL);

  g_object_unref(dir);
  g_free(profile_file_path);
  g_file_enumerator_close(files, NULL, NULL);
  g_object_unref(files);
}


static GFileEnumerator *
gnac_profiles_mgr_get_default_profiles_enumerator(void)
{
  GFile *dir = g_file_new_for_path(PKGDATADIR "/profiles/default");
  if (!g_file_query_exists(dir, NULL)) {
    g_warning("%s", _("Unable to find the default profiles directory"));
    return NULL;
  }
  
  GError *error = NULL;
  GFileEnumerator *files = g_file_enumerate_children(dir,
      G_FILE_ATTRIBUTE_STANDARD_NAME ","
      G_FILE_ATTRIBUTE_STANDARD_TYPE,
      G_FILE_QUERY_INFO_NONE, NULL, &error);
  if (!files) {
    g_warning("%s: %s",
        _("Unable to browse the default profiles directory"),
        error->message);
    g_clear_error(&error);
    g_object_unref(dir);
    return NULL;
  }

  g_object_unref(dir);

  return files;
}


static void
gnac_profiles_mgr_import_default_profiles(void)
{
  gnac_profiles_mgr_clear();

  GFileEnumerator *files = gnac_profiles_mgr_get_default_profiles_enumerator();

  GFile *profile_file = g_file_enumerator_get_container(files);
  gchar *profile_file_path = g_file_get_path(profile_file);

  GFileInfo *file_info;
  while ((file_info = g_file_enumerator_next_file(files, NULL, NULL))) {
    if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_REGULAR) {
      GError *error = NULL;
      const gchar *profile_file_name = g_file_info_get_name(file_info);
      gchar *profile_file_full_path = g_build_filename(profile_file_path,
          profile_file_name, NULL);
      gpointer profile = gnac_profiles_properties_load_profile_from_file(
          profile_file_full_path, profile_file_name, &error);

      if (profile) {
        gnac_profiles_properties_save_profile(profile);
      } else if (error) {
        g_warning("%s", error->message);
        g_clear_error(&error);
      }

      g_free(profile_file_full_path);
      gnac_profiles_properties_free_audio_profile(profile);
    }

    g_object_unref(file_info);
  }

  g_free(profile_file_path);
  g_file_enumerator_close(files, NULL, NULL);
  g_object_unref(files);
}


GList *
gnac_profiles_mgr_get_profiles_list(void)
{
  GtkTreeIter iter;
  GtkTreeModel *model = GTK_TREE_MODEL(
      gnac_profiles_mgr_get_object("liststore"));
  gboolean has_next = gtk_tree_model_get_iter_first(model, &iter);

  GList *list = NULL;
  while (has_next) {
    AudioProfileGeneric *profile;
    gtk_tree_model_get(model, &iter, COL_PROFILE, &profile, -1);
    list = g_list_prepend(list, profile);
    has_next = gtk_tree_model_iter_next(model, &iter);
  }

  return g_list_reverse(list);
}


void
gnac_profiles_mgr_destroy(void)
{
  if (profiles_mgr_builder) {
    gnac_profiles_mgr_clear();
    GtkWidget *widget = gnac_profiles_mgr_get_widget("profile_manager_window");
    gtk_widget_destroy(widget);
    g_object_unref(profiles_mgr_builder);
  }

  gnac_profiles_properties_destroy();
}


void
gnac_profiles_mgr_show(void)
{
  static gboolean first_show = TRUE;
 
  if (!first_show) {
    gnac_profiles_mgr_populate();
  } else {
    first_show = FALSE;
  }

  GtkWidget *window = gnac_profiles_mgr_get_widget("profile_manager_window");
  gtk_widget_show(window);

  GList *selected = gnac_profiles_mgr_get_selected_rows();
  if (!selected) {
    gnac_profiles_mgr_show_description_frame(FALSE);
    gnac_profiles_mgr_activate_buttons(FALSE);
  }

  g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);
}


static void
gnac_profiles_mgr_clear(void)
{
  GtkTreeModel *model = GTK_TREE_MODEL(
      gnac_profiles_mgr_get_object("liststore"));
  GtkTreeView *view = GTK_TREE_VIEW(
      gnac_profiles_mgr_get_widget("profile_treeview"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection(view);

  GtkTreeIter iter;
  gboolean has_next = gtk_tree_model_get_iter_first(model, &iter);

  g_signal_handlers_block_by_func(selection,
      gnac_profiles_mgr_on_treeselection_changed, NULL);

  while (has_next) {
    AudioProfileGeneric *profile;
    gtk_tree_model_get(model, &iter, COL_PROFILE, &profile, -1);
    gnac_profiles_properties_free_audio_profile(profile);
    has_next = gtk_tree_model_iter_next(model, &iter);
  }

  gtk_list_store_clear(GTK_LIST_STORE(model));

  g_signal_handlers_unblock_by_func(selection,
      gnac_profiles_mgr_on_treeselection_changed, NULL);
}


static void
gnac_profiles_mgr_insert(gpointer profile)
{
  GtkListStore *model = GTK_LIST_STORE(
      gnac_profiles_mgr_get_object("liststore"));
  AudioProfileGeneric *generic = AUDIO_PROFILE_GET_GENERIC(profile);
  gchar *formatted_name = gnac_profiles_properties_filter_text_for_display(
      generic->name, MAX_NAME_DISPLAY_SIZE);
  GtkTreeIter iter;

  gtk_list_store_append(model, &iter);
  gtk_list_store_set(model, &iter,
      COL_NAME, formatted_name,
      COL_FORMAT, generic->format_name,
      COL_EXTENSION, generic->extension,
      COL_PROFILE, profile,
      -1);

  g_free(formatted_name);
}


static void
gnac_profiles_mgr_remove(GtkTreeRowReference *ref)
{
  g_return_if_fail(ref);

  GtkTreeModel *model = GTK_TREE_MODEL(
      gnac_profiles_mgr_get_object("liststore"));
  GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
  if (!path) return;

  GtkTreeIter iter;

  if (gtk_tree_model_get_iter(model, &iter, path)) {
    AudioProfileGeneric *profile;
    gtk_tree_model_get(model, &iter, COL_PROFILE, &profile, -1);
    gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
    gnac_profiles_properties_free_audio_profile(profile);
  }

  gtk_tree_path_free(path);
}


static void
gnac_profiles_mgr_get(GtkTreeRowReference  *ref,
                      gint                  col_number,
                      AudioProfileGeneric **profile)
{
  g_return_if_fail(ref);

  GtkTreeModel *model = GTK_TREE_MODEL(
      gnac_profiles_mgr_get_object("liststore"));
  GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
  if (!path) return;

  GtkTreeIter iter;

  if (gtk_tree_model_get_iter(model, &iter, path)) {
    gtk_tree_model_get(model, &iter, col_number, profile, -1);
  }

  gtk_tree_path_free(path);
}


static void
gnac_profiles_mgr_set(GtkTreeRowReference *ref, ...)
{
  g_return_if_fail(ref);

  GtkListStore *list_store = GTK_LIST_STORE(
      gnac_profiles_mgr_get_object("liststore"));
  GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
  if (!path) return;

  GtkTreeIter iter;
  va_list ap;

  if (gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path)) {
    va_start(ap, ref);
    gtk_list_store_set_valist(list_store, &iter, ap);
  }

  gtk_tree_path_free(path);
}


static GList *
gnac_profiles_mgr_get_selected_rows(void)
{
  GtkTreeModel *model = GTK_TREE_MODEL(
      gnac_profiles_mgr_get_object("liststore"));
  GtkTreeView *view = GTK_TREE_VIEW(
      gnac_profiles_mgr_get_widget("profile_treeview"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection(view);

  /* Convert path to GtkTreeRowReference */
  GList *next = gtk_tree_selection_get_selected_rows(selection, &model);
  GList *next_temp = next;
  GList *row_references = NULL;

  while (next) {
    row_references = g_list_prepend(row_references,
        gtk_tree_row_reference_new(model, next->data));
    next = g_list_next(next);
  }

  row_references = g_list_reverse(row_references);

  g_list_free_full(next_temp, (GDestroyNotify) gtk_tree_path_free);

  return row_references;
}


static void
gnac_profiles_mgr_activate_buttons(gboolean activate)
{
  gnac_profiles_mgr_set_widget_sensitive("copy_button", activate);
  gnac_profiles_mgr_set_widget_sensitive("edit_button", activate);
  gnac_profiles_mgr_set_widget_sensitive("delete_button", activate);
}


static void
gnac_profiles_mgr_set_window_sensitive(gboolean sensitive)
{
  gnac_profiles_mgr_set_widget_sensitive("frame-profiles-list", sensitive);
  gnac_profiles_mgr_set_widget_sensitive("frame-description", sensitive);
  gnac_profiles_mgr_set_widget_sensitive("frame-status", sensitive);
  gnac_profiles_mgr_set_widget_sensitive("close_button", sensitive);
}


static void
gnac_profiles_mgr_set_progress_bar_fraction(gdouble fraction)
{
  GtkWidget *widget = gnac_profiles_mgr_get_widget("progressbar-import");
  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(widget), fraction);
}


static void
gnac_profiles_mgr_display_status_message(const gchar *success_message,
                                         const gchar *error_message)
{
  GtkWidget *frame_status = gnac_profiles_mgr_get_widget("frame-status");

  if (!success_message && !error_message) {
    gtk_widget_hide(frame_status);
    return;
  }

  gtk_widget_show_all(frame_status);

  GtkWidget *label_ok = gnac_profiles_mgr_get_widget("label-status-ok");
  GtkWidget *image_ok = gnac_profiles_mgr_get_widget("image-status-ok");

  if (success_message) {
    gtk_label_set_markup(GTK_LABEL(label_ok), success_message);
    gtk_widget_show(label_ok);
    gtk_widget_show(image_ok);
  } else {
    gtk_widget_hide(label_ok);
    gtk_widget_hide(image_ok);
  }

  GtkWidget *label_error = gnac_profiles_mgr_get_widget("label-status-error");
  GtkWidget *image_error = gnac_profiles_mgr_get_widget("image-status-error");

  if (error_message) {
    gtk_label_set_markup(GTK_LABEL(label_error), error_message);
    gtk_widget_show(label_error);
    gtk_widget_show(image_error);
  } else {
    gtk_widget_hide(label_error);
    gtk_widget_hide(image_error);
  }
}


void
gnac_profiles_mgr_on_drag_data_received(GtkWidget        *widget,
                                        GdkDragContext   *context,
                                        gint              x,
                                        gint              y,
                                        GtkSelectionData *selection_data,
                                        guint             info,
                                        guint             time,
                                        gpointer          data)
{
  gchar **uris = g_uri_list_extract_uris((const gchar *)
      gtk_selection_data_get_data(selection_data));
  if (!uris) {
    gtk_drag_finish(context, FALSE, FALSE, time);
    return;
  }

  ThreadCopyData  *tcopy_data = g_malloc(sizeof(ThreadCopyData));
  tcopy_data->uris = uris;
  tcopy_data->info = info;

  GError *error = NULL;

#if GLIB_CHECK_VERSION(2, 31, 0)
  g_thread_try_new("drag-data-thread",
      (GThreadFunc) gnac_profiles_mgr_copy_and_load_files, tcopy_data, &error);
#else
  g_thread_create((GThreadFunc) gnac_profiles_mgr_copy_and_load_files,
      tcopy_data, TRUE, &error);
#endif
  if (error) {
    g_debug("Failed to create thread: %s", error->message);
    gnac_profiles_mgr_display_status_message(NULL,
        _("Impossible to import file(s)"));
    g_clear_error(&error);
  }

  gtk_drag_finish(context, TRUE, FALSE, time);
}


static gchar *
gnac_profiles_mgr_get_file_name(GFile *file,
                                guint  target_type)
{
  GError *error = NULL;

  GFileInfo *file_info = g_file_query_info(file,
      G_FILE_ATTRIBUTE_STANDARD_NAME ","
      G_FILE_ATTRIBUTE_STANDARD_TYPE,
      G_FILE_QUERY_INFO_NONE, NULL, &error);
  if (error) {
    g_warning("%s", error->message);
    g_clear_error(&error);
    return NULL;
  }

  gchar *name = NULL;

  switch (target_type)
  {
    case DND_TARGET_URI:
      if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_REGULAR) {
        name = g_strdup(g_file_info_get_name(file_info));
      }
      break;

    case DND_TARGET_PLAIN:
      if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_UNKNOWN) {
        name = g_file_get_basename(file);
      }
      break;
  }

  g_object_unref(file_info);

  return name;
}


static void
gnac_profiles_mgr_display_import_status(guint profiles_ok,
                                        guint profiles_error)
{
  gchar *oks = NULL;
  gchar *errors = NULL;

  if (profiles_error > 0) {
    errors = g_strdup_printf(ngettext("%d file failed to be imported",
        "%d files failed to be imported", profiles_error), profiles_error);
  }

  if (profiles_ok > 0) {
    oks = g_strdup_printf(ngettext("%d file successfully imported",
        "%d files successfully imported", profiles_ok), profiles_ok);
  }

  gdk_threads_enter();
    gnac_profiles_mgr_set_window_sensitive(TRUE);
    gnac_profiles_mgr_show_import_progressbar(FALSE);
    gnac_profiles_mgr_display_status_message(oks, errors);
  gdk_threads_leave();

  g_free(oks);
  g_free(errors);
}


static gboolean
gnac_profiles_mgr_copy_file(GFile       *src_file,
                            const gchar *dest_name)
{
  GError *error = NULL;
  gchar *path = g_build_filename(g_get_tmp_dir(), dest_name, NULL);
  GFile *dest_file = g_file_new_for_path(path);
  CopyData copy_data = { path, dest_name, &error, FALSE };

  gdk_threads_enter();
    gnac_profiles_mgr_set_progress_bar_fraction(0.0);
  gdk_threads_leave();

  gboolean success = g_file_copy(src_file, dest_file, G_FILE_COPY_NONE, NULL,
      (GFileProgressCallback) gnac_profiles_mgr_on_drag_profile_copied,
      &copy_data, &error);
  if (error) {
    g_warning("%s", error->message);
    g_clear_error(&error);
  }

  g_free(path);
  g_object_unref(dest_file);

  return success;
}


static void
gnac_profiles_mgr_copy_and_load_files(gpointer data)
{
  ThreadCopyData *tcopy_data = (ThreadCopyData *) data;
  gchar **uris = tcopy_data->uris;

  gint index = 0;
  gchar *uri = uris[index];

  gdk_threads_enter();
    gnac_profiles_mgr_set_window_sensitive(FALSE);
    gnac_profiles_mgr_show_import_progressbar(TRUE);
  gdk_threads_leave();
  
  guint profiles_ok = 0;
  guint profiles_error = 0;

  while (uri) {
    GFile *file = g_file_new_for_uri(uri);
    gchar *name = gnac_profiles_mgr_get_file_name(file, tcopy_data->info);

    if (!name) {
      g_warning(
          _("Impossible to import file \"%s\". File type not supported."),
          uri);
      profiles_error++;
    } else if (gnac_profiles_properties_saved_profiles_contain_name(name)) {
      g_warning(
          _("Impossible to load file \"%s\": "
            "a profile with the same name already exists."), uri);
      profiles_error++;
    } else if (gnac_profiles_mgr_copy_file(file, name)) {
      profiles_ok++;
    } else {
      profiles_error++;
    }

    g_free(name);

    index++;
    uri = uris[index];

    g_object_unref(file);
  }

  gnac_profiles_mgr_display_import_status(profiles_ok, profiles_error);

  g_strfreev(tcopy_data->uris);
  g_free(tcopy_data);
}


void
gnac_profiles_mgr_on_drag_profile_copied(goffset  current_num_bytes,
                                         goffset  total_num_bytes,
                                         gpointer user_data)
{
  /* CLAMP ensures that frac is between 0.0 and 1.0 */
  gdouble frac = CLAMP(
      ((gdouble) current_num_bytes) / ((gdouble) total_num_bytes),
      0.0, 1.0);
  
  gdk_threads_enter();
    gnac_profiles_mgr_set_progress_bar_fraction(frac);
  gdk_threads_leave();

  if (current_num_bytes >= total_num_bytes) {
    CopyData *copy_data = (CopyData *) user_data;
    if (!copy_data->toggle) {
      copy_data->toggle = TRUE;
    } else {
      AudioProfileGeneric *profile =
          gnac_profiles_properties_load_profile_from_file(
              copy_data->path, copy_data->name, copy_data->error);

      if (profile && !(*(copy_data->error))) {
        gdk_threads_enter();
          gnac_profiles_properties_save_profile(profile);
          gnac_profiles_mgr_insert(profile);
        gdk_threads_leave();
      }

      GFile *file = g_file_new_for_path(copy_data->path);

      g_file_delete(file, NULL, NULL);
      g_object_unref(file);
    } 
  }
}


void
gnac_profiles_mgr_on_drag_data_get(GtkWidget        *widget,
                                   GdkDragContext   *drag_context,
                                   GtkSelectionData *data,
                                   guint             info,
                                   guint             time,
                                   gpointer          user_data)
{
  gchar **uris = gnac_profiles_mgr_get_selected_uris();
  gtk_selection_data_set_uris(data, uris);
}


static gchar **
gnac_profiles_mgr_get_selected_uris(void)
{
  GList *selected = gnac_profiles_mgr_get_selected_rows();
  if (!selected) return NULL;

  GtkTreeRowReference *reference = (GtkTreeRowReference *) selected->data;
  gchar **uris = g_malloc((g_list_length(selected)+1) * sizeof(gchar *));

  guint  i;
  GList *temp;

  for (i = 0, temp = selected; temp; temp = g_list_next(temp), i++) {
    AudioProfileGeneric *profile;
    gnac_profiles_mgr_get(reference, COL_PROFILE, &profile);
    profile = profile->generic;
    gchar *path = g_strconcat(GNAC_SAVED_PROFILES_URL_WITH_EXT(profile->name),
        NULL);
    GFile *file = g_file_new_for_path(path);
    uris[i] = g_file_get_uri(file); 

    g_free(path);
  }

  uris[i] = NULL;

  g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);

  return uris;
}


void
gnac_profiles_mgr_on_add(GtkWidget *widget,
                         gpointer   data)
{
  gnac_profiles_properties_show(NULL, _("New Profile"),
      G_CALLBACK(gnac_profiles_mgr_on_new_profile));
}


static void
gnac_profiles_mgr_on_new_profile(GtkWidget *widget,
                                 gpointer   data)
{
  gnac_profiles_mgr_insert(data); 
  gnac_profiles_mgr_display_status_message(NULL, NULL);
}


void
gnac_profiles_mgr_on_copy(GtkWidget *widget,
                          gpointer   data)
{
  GList *selected = gnac_profiles_mgr_get_selected_rows();
  if (!selected) return;

  GtkTreeRowReference *reference = (GtkTreeRowReference *) selected->data;

  AudioProfileGeneric *profile;
  gnac_profiles_mgr_get(reference, COL_PROFILE, &profile);
  AudioProfileGeneric *generic = profile->generic;

  gchar *full_path = g_strconcat(GNAC_SAVED_PROFILES_URL_WITH_EXT(generic->name),
      NULL);
  gchar *new_name = g_strconcat(GNAC_SAVED_PROFILES_URL(generic->name),
      gettext(GNAC_COPY_SUFFIX), NULL);
  gchar *new_path = g_strconcat(new_name, ".xml", NULL);

  GFile *src = g_file_new_for_path(full_path);
  GFile *dst = g_file_new_for_path(new_path);

  g_free(full_path);
  
  GError *error = NULL;
  g_file_copy(src, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
  if (error)
  {
    g_debug("Failed to copy the profile %s: %s",
        generic->name, error->message);
    gnac_profiles_mgr_display_status_message(
        NULL, _("Failed to copy the profile"));

    g_clear_error(&error);
    g_object_unref(src);
    g_object_unref(dst);
    g_free(new_path);
    g_free(new_name);
    g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);

    return;
  }

  // FIXME seems we have a leak here
  AudioProfileGeneric *copy_profile =
      gnac_profiles_properties_load_profile_from_file(
          new_path, new_name, &error);
  if (copy_profile)
  {
    (copy_profile->generic)->name = g_strconcat(generic->name, 
        gettext(GNAC_COPY_SUFFIX), NULL);
    gnac_profiles_properties_save_profile(copy_profile);
    gnac_profiles_mgr_insert(copy_profile); 
  } 
  else if (error) 
  {
    g_debug("Failed to copy the profile %s: %s",
        (copy_profile->generic)->name, error->message);
    gnac_profiles_mgr_display_status_message(
        NULL, _("Failed to copy the profile"));
    g_clear_error(&error);
  }

  g_object_unref(src);
  g_object_unref(dst);
  g_free(new_path);
  g_free(new_name);
  g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);
}


void
gnac_profiles_mgr_on_edit(GtkWidget *widget,
                          gpointer   data)
{
  GList *selected = gnac_profiles_mgr_get_selected_rows();
  if (!selected) return;

  GtkTreeRowReference *reference = (GtkTreeRowReference *) selected->data;
  AudioProfileGeneric *profile;
  gnac_profiles_mgr_get(reference, COL_PROFILE, &profile);
  profile->generic->user_data = selected->data;

  g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);
  
  gnac_profiles_properties_show(profile, _("Edit Profile"),
      G_CALLBACK(gnac_profiles_mgr_on_edit_profile));
}


static void
gnac_profiles_mgr_on_edit_profile(GtkWidget *widget,
                                  gpointer   data)
{
  GList *selected = gnac_profiles_mgr_get_selected_rows();
  if (!selected) return;

  GtkTreeRowReference *reference = (GtkTreeRowReference *) selected->data;

  AudioProfileGeneric *old_profile;
  gnac_profiles_mgr_get(reference, COL_PROFILE, &old_profile);
  AudioProfileGeneric *old_generic = old_profile->generic;

  AudioProfileGeneric *profile = (AudioProfileGeneric *) data;
  AudioProfileGeneric *generic = profile->generic;

  if (!gnac_utils_str_equal(generic->name, old_generic->name)) {
    gnac_profiles_mgr_delete_profile_file(old_generic->name);
  }

  gchar *formatted_name = gnac_profiles_properties_filter_text_for_display(
      generic->name, MAX_NAME_DISPLAY_SIZE);
  gnac_profiles_mgr_set(reference,
      COL_NAME, formatted_name,
      COL_FORMAT, generic->format_name,
      COL_EXTENSION, generic->extension,
      COL_PROFILE, profile,
      -1);
  
  g_free(formatted_name);
  gnac_profiles_properties_free_audio_profile(old_profile);
  g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);

  gnac_profiles_mgr_display_status_message(NULL, NULL);

  gnac_profiles_mgr_on_treeselection_changed();
}


void
gnac_profiles_mgr_on_remove(GtkWidget *widget,
                            gpointer   data)
{
  GList *selected = gnac_profiles_mgr_get_selected_rows();
  if (!selected) return;

  GtkTreeRowReference *reference = (GtkTreeRowReference *) selected->data;

  AudioProfileGeneric *profile;
  gnac_profiles_mgr_get(reference, COL_PROFILE, &profile);
  gnac_profiles_mgr_delete_profile_file(profile->generic->name);

  gnac_profiles_mgr_display_status_message(NULL, NULL);

  GtkTreeModel *model = gtk_tree_row_reference_get_model(reference);
  GtkTreePath *path = gtk_tree_row_reference_get_path(reference);

  GtkTreeIter iter;
  gboolean new_selected = FALSE;
  if (gtk_tree_model_get_iter(model, &iter, path)) {
    if (gtk_tree_model_iter_next(model, &iter)
        || (gtk_tree_path_prev(path) &&
            gtk_tree_model_get_iter(model, &iter, path)))
    {
      GtkTreeView *view = GTK_TREE_VIEW(
          gnac_profiles_mgr_get_widget("profile_treeview"));
      GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
      gtk_tree_selection_select_iter(selection, &iter);
      new_selected = TRUE;
    }
  }

  gnac_profiles_mgr_remove(reference);

  gnac_profiles_mgr_activate_buttons(new_selected);
  gnac_profiles_mgr_show_description_frame(new_selected);

  gtk_tree_path_free(path);
  g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);
}


static void
gnac_profiles_mgr_delete_profile_file(const gchar *name)
{
  gchar *full_path = g_strconcat(GNAC_SAVED_PROFILES_URL_WITH_EXT(name), NULL);
  GFile *file = g_file_new_for_path(full_path);

  g_file_delete(file, NULL, NULL);

  g_object_unref(file);
  g_free(full_path);
}


static void
gnac_profiles_mgr_on_treeselection_changed(void)
{
  GList *selected = gnac_profiles_mgr_get_selected_rows();
  if (!selected) return;

  GtkTreeRowReference *reference = (GtkTreeRowReference *)
      g_list_nth_data(selected, 0);

  gnac_profiles_mgr_activate_buttons(TRUE);
  gnac_profiles_mgr_display_status_message(NULL, NULL);

  AudioProfileGeneric *profile;
  gnac_profiles_mgr_get(reference, COL_PROFILE, &profile);
  GtkWidget *widget = gnac_profiles_mgr_get_widget("label-description-content");
  gchar *descr = gnac_profiles_properties_filter_text_for_display(
      profile->generic->description, MAX_DESCR_DISPLAY_SIZE);
  gtk_label_set_text(GTK_LABEL(widget), descr);

  g_free(descr);

  gnac_profiles_mgr_show_description_frame(TRUE);

  g_list_free_full(selected, (GDestroyNotify) gtk_tree_row_reference_free);
}


void
gnac_profiles_mgr_on_close(GtkWidget *widget,
                           gpointer   data)
{
  GtkWidget *window = gnac_profiles_mgr_get_widget("profile_manager_window");
  gtk_widget_hide(window);
  gnac_profiles_populate_combo();
}


gboolean
gnac_profiles_mgr_on_delete_event(GtkWidget *widget,
                                  GdkEvent  *event,
                                  gpointer   user_data)
{
  gnac_profiles_mgr_on_close(NULL, NULL);
  return TRUE;
}


gboolean
gnac_profiles_mgr_list_on_key_pressed(GtkWidget   *widget,
                                      GdkEventKey *event,
                                      gpointer     data)
{
  if (gnac_ui_utils_event_is_delete_key(event)) {
    gnac_profiles_mgr_on_remove(NULL, NULL);
    return TRUE;
  }

  return FALSE;
}


gboolean
gnac_profiles_mgr_on_key_pressed(GtkWidget   *widget,
                                 GdkEventKey *event,
                                 gpointer     data)
{
  if (gnac_ui_utils_event_is_escape_key(event)) {
    gnac_profiles_mgr_on_close(NULL, NULL);
    return TRUE;
  }

  return FALSE;
}
