/*
 * 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 <glib/gi18n.h>
#include <gst/gst.h>

#include "gnac-file-list.h"
#include "gnac-main.h"
#include "gnac-properties.h"
#include "gnac-ui.h"
#include "gnac-ui-utils.h"
#include "gnac-utils.h"

#include "libgnac-metadata.h"


static GtkBuilder   *gnac_properties_builder = NULL;
static GHashTable   *gnac_properties_table;
static GtkTreeModel *properties_filter;

static gboolean properties_displayed = FALSE;

static GtkTreeRowReference *current_ref;

static const gchar *displayed_tags[] = {
  GST_TAG_ALBUM,
  GST_TAG_ALBUM_VOLUME_COUNT,
  GST_TAG_ALBUM_VOLUME_NUMBER,
  GST_TAG_ARTIST,
  GST_TAG_COMMENT,
  GST_TAG_DATE,
  GST_TAG_GENRE,
  GST_TAG_IMAGE,
  GST_TAG_TITLE,
  GST_TAG_TRACK_COUNT,
  GST_TAG_TRACK_NUMBER,
  NULL
};

static const gchar *displayed_properties[][2] = {
  { GNAC_TAG_FILENAME,        N_("Filename")    },
  { GST_TAG_LOCATION,         N_("Location")    },
  { GST_TAG_DURATION,         N_("Duration")    },
  { GNAC_TAG_FILE_SIZE,       N_("File size")   },
  { GNAC_TAG_CHANNELS,        N_("Channels")    },
  /* Translators: Audio mode, e.g., mono or stereo */
  { GNAC_TAG_MODE,            N_("Mode")        },
  /* Translators: sample rate */
  { GNAC_TAG_RATE,            N_("Rate")        },
  { GST_TAG_CONTAINER_FORMAT, N_("Container")   },
  { GST_TAG_AUDIO_CODEC,      N_("Audio codec") },
  { GST_TAG_VIDEO_CODEC,      N_("Video codec") },
  { GST_TAG_BITRATE,          N_("Bitrate")     },
  { GST_TAG_TRACK_GAIN,       N_("Track gain")  },
  { GST_TAG_TRACK_PEAK,       N_("Track peak")  },
  { GNAC_TAG_FRAMERATE,       N_("Framerate")   },
  { GST_TAG_ENCODER,          N_("Encoder")     },
  { NULL, NULL }
};

static LibgnacTags *tags;


static void
gnac_properties_set_entry(const gchar  *entry_name,
                          const GValue *entry_value);

static void
gnac_properties_build_table(void);

static gchar *
gnac_properties_get_property_from_value(const gchar  *name,
                                        const GValue *value);

static void
gnac_properties_reset_basic_tab(void);

static void
gnac_properties_reset_properties_tab(void);

static void
gnac_properties_update_display(GtkTreeRowReference *reference);

static void
gnac_properties_set_property(const gchar  *name,
                             const GValue *value);

static gchar *
gnac_properties_dup_bitrate(const GValue *value);

static gchar *
gnac_properties_dup_rate(const GValue *value);

static gchar *
gnac_properties_dup_framerate(const GValue *value);

static gchar *
gnac_properties_dup_channels(const GValue *value);

static void
gnac_properties_update_forward_arrow(void);

static void
gnac_properties_update_backward_arrow(void);

static void
gnac_properties_reset_spin_if_empty(const gchar *name);


static GObject *
gnac_properties_get_object(const gchar *object_name)
{
  return gnac_ui_utils_get_object(gnac_properties_builder, object_name);
}


static GtkWidget *
gnac_properties_get_widget(const gchar *widget_name)
{
  return gnac_ui_utils_get_widget(gnac_properties_builder, widget_name);
}


static gboolean
gnac_properties_visible_func(GtkTreeModel *model,
                             GtkTreeIter  *iter,
                             gpointer      data)
{
  gboolean visible;
  gtk_tree_model_get(model, iter, PROPERTY_VISIBLE, &visible, -1);
  return visible;
}


static void
gnac_properties_build_table(void)
{
  GtkCellRenderer *renderer;
  GtkTreeModel    *model;
  GtkTreeView     *view;

  model = GTK_TREE_MODEL(gnac_properties_get_object("properties_store"));
  view = GTK_TREE_VIEW(gnac_properties_get_widget("properties_view"));

  gtk_tree_view_set_tooltip_column(view, PROPERTY_TOOLTIP);

  properties_filter = gtk_tree_model_filter_new(model, NULL);
  gtk_tree_model_filter_set_visible_func(
      GTK_TREE_MODEL_FILTER(properties_filter),
      gnac_properties_visible_func, NULL, NULL);
  gtk_tree_view_set_model(view, properties_filter);

  renderer = gtk_cell_renderer_text_new();
  g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
  g_object_set(G_OBJECT(renderer), "xpad", 6, NULL);
  g_object_set(G_OBJECT(renderer), "weight", 700, NULL);
  gtk_tree_view_insert_column_with_attributes(view, -1,
      "Property", renderer, "text", PROPERTY_NAME, NULL);

  renderer = gtk_cell_renderer_text_new();
  g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
  gtk_tree_view_insert_column_with_attributes(view, -1,
      "Value", renderer, "text", PROPERTY_VALUE, NULL);

  gnac_properties_table = g_hash_table_new_full(g_str_hash,
      g_str_equal, g_free, (GDestroyNotify) gtk_tree_row_reference_free);
  
  guint i;
  for (i = 0; displayed_properties[i][0]; i++) {
    GtkTreeIter iter;

    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter,
        PROPERTY_NAME, gettext(displayed_properties[i][1]),
        PROPERTY_VISIBLE, TRUE,
        -1);

    GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
    GtkTreeRowReference *ref = gtk_tree_row_reference_new(model, path);

    g_hash_table_insert(gnac_properties_table,
        g_strdup(displayed_properties[i][0]), ref);

    gtk_tree_path_free(path);
  }
}


static void
gnac_properties_reset_properties(void)
{
  gnac_properties_reset_basic_tab();
  gnac_properties_reset_properties_tab();
  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(properties_filter));
}


static void
gnac_properties_reset_basic_tab(void)
{
  guint i;
  for (i = 0; displayed_tags[i]; i++) {
    gnac_properties_set_entry(displayed_tags[i], NULL);
  }
}


static void
gnac_properties_reset_properties_tab(void)
{
  guint i;
  for (i = 0; displayed_properties[i][0]; i++) {
    gnac_properties_set_property(displayed_properties[i][0], NULL);
  }
}


static void
gnac_properties_set_property(const gchar  *name, 
                             const GValue *value)
{
  gchar *str_value = NULL;
  GtkTreeRowReference *ref = NULL;
  GtkTreeModel *model;
  GtkTreePath *path;
  gboolean visible = TRUE;

  if (value) str_value = gnac_properties_get_property_from_value(name, value);

  if (!str_value) visible = FALSE;

  model = GTK_TREE_MODEL(gnac_properties_get_object("properties_store"));
  ref = g_hash_table_lookup(gnac_properties_table, name);
  if (!ref) {
    return;
  }

  path = gtk_tree_row_reference_get_path(ref);
  if (!path) {
    g_free(str_value);
    return;
  }

  GtkTreeIter iter;

  if (gtk_tree_model_get_iter(model, &iter, path)) {
    gtk_list_store_set(GTK_LIST_STORE(model), &iter,
        PROPERTY_VALUE, str_value,
        PROPERTY_VISIBLE, visible,
        -1);

    if (LIBGNAC_METADATA_TAG_IS_LOCATION(name) ||
        LIBGNAC_METADATA_TAG_IS_FILENAME(name))
    {
      gtk_list_store_set(GTK_LIST_STORE(model), &iter,
          PROPERTY_TOOLTIP, str_value, -1);
    }
  }

  g_free(str_value);
  gtk_tree_path_free(path);
  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(properties_filter));
}


static void
gnac_properties_reset_field(GtkWidget *widget)
{
  if (GTK_IS_ENTRY(widget)) {
    gtk_entry_set_text(GTK_ENTRY(widget), "");
  } else if (GTK_IS_IMAGE(widget)) {
    gtk_image_clear(GTK_IMAGE(widget));
  }
}


static void
gnac_properties_set_image(GtkWidget    *widget,
                          const GValue *value)
{
  GdkPixbuf *pixbuf = g_value_get_object(value);
  pixbuf = gnac_ui_utils_scale_pixbuf(pixbuf, 96, 96);
  pixbuf = gnac_ui_utils_add_border_to_pixbuf(pixbuf);
  gtk_image_set_from_pixbuf(GTK_IMAGE(widget), pixbuf);
  g_object_unref(pixbuf);
}


static void 
gnac_properties_set_entry(const gchar  *entry_name, 
                          const GValue *entry_value)
{
  GtkWidget *widget = gnac_properties_get_widget(entry_name);
  if (!widget) {
    gnac_properties_set_property(entry_name, entry_value);
    return;
  }

  if (!entry_value) {
    gnac_properties_reset_field(widget);
    return;
  }

  if (LIBGNAC_METADATA_TAG_IS_IMAGE(entry_name)) {
    gnac_properties_set_image(widget, entry_value);
    return;
  }

  GType type = G_VALUE_TYPE(entry_value);
  switch (type) {

    case G_TYPE_STRING:
      gtk_entry_set_text(GTK_ENTRY(widget), g_value_get_string(entry_value));
      return;

    case G_TYPE_UINT: {
      gdouble value;
      value = (gdouble) g_value_get_uint(entry_value);
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), value);
      gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget), value, value);
      return;
    }
    
    default:
      gtk_entry_set_text(GTK_ENTRY(widget),
          g_strdup_value_contents(entry_value));
      g_debug("%s has an unknown type: %s",
          entry_name, g_type_name(type));
      return;
  }
}


static gchar *
gnac_properties_get_property_from_value(const gchar  *name,
                                        const GValue *value)
{
  if (LIBGNAC_METADATA_TAG_IS_DURATION(name)) {
    guint64 duration = g_value_get_uint64(value);
    return gnac_utils_format_duration_for_display(duration);
  }

  if (LIBGNAC_METADATA_TAG_IS_BITRATE(name)) {
    return gnac_properties_dup_bitrate(value);
  }

  if (LIBGNAC_METADATA_TAG_IS_FILE_SIZE(name)) {
    gint64 val = g_value_get_int64(value);
    if (val == 0) return NULL;
    gchar *size = g_format_size(val);
    return size;
  }

  if (LIBGNAC_METADATA_TAG_IS_RATE(name)) {
    return gnac_properties_dup_rate(value);
  }

  if (LIBGNAC_METADATA_TAG_IS_FRAMERATE(name)) {
    return gnac_properties_dup_framerate(value);
  }

  if (LIBGNAC_METADATA_TAG_IS_CHANNELS(name)) {
    return gnac_properties_dup_channels(value);
  }

  if (LIBGNAC_METADATA_TAG_IS_TRACK_GAIN(name) ||
      LIBGNAC_METADATA_TAG_IS_TRACK_PEAK(name))
  {
    gdouble val = g_value_get_double(value);
    if (val == 0) return NULL;
    else return g_strdup_printf("%g", val);
  }

  if (LIBGNAC_METADATA_TAG_IS_AUDIO_CODEC(name) ||
      LIBGNAC_METADATA_TAG_IS_CONTAINER(name)   ||
      LIBGNAC_METADATA_TAG_IS_ENCODER(name)     ||
      LIBGNAC_METADATA_TAG_IS_FILENAME(name)    ||
      LIBGNAC_METADATA_TAG_IS_LOCATION(name)    ||
      LIBGNAC_METADATA_TAG_IS_MODE(name)        ||
      LIBGNAC_METADATA_TAG_IS_VIDEO_CODEC(name))
  {
    const gchar *str = g_value_get_string(value);
    if (!str) return NULL;
    return g_markup_escape_text(str, -1);
  }

  return NULL;
}


static gchar *
gnac_properties_dup_bitrate(const GValue *value)
{
  guint val = g_value_get_uint(value);
  if (val == 0) return NULL;

  val = gnac_utils_convert_bps_to_kbps(val);

  if (libgnac_metadata_tag_exists(tags, GNAC_TAG_VBR)) {
    /* Translators: variable bitrate */
    return  g_strdup_printf(_("~%d kbps (VBR)"), val);
  } else {
    /* Translators: bitrate */
    return g_strdup_printf(_("%d kbps"), val);
  }
}


static gchar *
gnac_properties_dup_rate(const GValue *value)
{
  guint val = g_value_get_uint(value);
  if (val == 0) return NULL;

  /* Translators: rate */
  return g_strdup_printf(_("%d Hz"), val);
}


static gchar *
gnac_properties_dup_framerate(const GValue *value)
{
  gfloat val = g_value_get_float(value);
  if (val == 0) return NULL;

  /* Translators: framerate */
  return g_strdup_printf(_("%.3lf fps"), val);
}


static gchar *
gnac_properties_dup_channels(const GValue *value)
{
  guint val = g_value_get_uint(value);
  if (val == 0) return NULL;

  /* Translators: channels */
  return g_strdup_printf("%d%s", val,
      val == 1 ? _(" (mono)") : val == 2 ? _(" (stereo)") : "");
}


static void
gnac_properties_set_parent(void)
{
  GtkWidget *window = gnac_properties_get_widget("properties_window");
  GtkWidget *parent = gnac_ui_get_widget("main_window");
  gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent));
}


static void
gnac_properties_window_new(void)
{
  gnac_properties_builder = gnac_ui_utils_create_gtk_builder(
      PKGDATADIR "/gnac-properties-window.xml");
  gnac_properties_set_parent();
  gnac_properties_build_table();
}


static void
gnac_properties_update_display(GtkTreeRowReference *reference)
{
  if (!properties_displayed) return;

  gchar *uri;
  if (!gnac_file_list_get(reference, &uri)) return;

  GFile *file = g_file_new_for_uri(uri);
  GError *error = NULL;
  tags = libgnac_metadata_extract(metadata, file, &error);
  if (error) {
    g_debug("Failed to extract metadata for %s: %s", uri, error->message);
    g_clear_error(&error);
  }

  g_free(uri);
  g_object_unref(file);

  gnac_properties_reset_properties();

  if (tags) {
    g_hash_table_foreach(tags, (GHFunc) gnac_properties_set_entry, NULL);
  }
}


void
gnac_properties_update_arrows(void)
{
  if (current_ref && gnac_properties_builder) {
    gnac_properties_update_forward_arrow();
    gnac_properties_update_backward_arrow();
  }
}


static void
gnac_properties_update_forward_arrow(void)
{
  GtkWidget *forward_arrow = gnac_properties_get_widget("forward_btn");

  if (gnac_file_list_has_next_row(current_ref)) {
    gtk_widget_set_sensitive(forward_arrow, TRUE);
  } else {
    gtk_widget_set_sensitive(forward_arrow, FALSE);
  }
}


static void
gnac_properties_update_backward_arrow(void)
{
  GtkWidget *backward_arrow = gnac_properties_get_widget("backward_btn");

  if (gnac_file_list_has_prev_row(current_ref)) {
    gtk_widget_set_sensitive(backward_arrow, TRUE);
  } else {
    gtk_widget_set_sensitive(backward_arrow, FALSE);
  }
}


static void
gnac_properties_reset_spin_if_empty(const gchar *name)
{
  GtkWidget *spin = gnac_properties_get_widget(name);

  if (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) == 0) {
    gtk_entry_set_text(GTK_ENTRY(spin), "");
  }
}


void
gnac_properties_window_show(void)
{
  if (!gnac_properties_builder) {
    gnac_properties_window_new();
  }

  properties_displayed = TRUE;

  if (gnac_file_list_get_current_row(&current_ref)) {
    gnac_properties_update_arrows();
    gnac_properties_update_display(current_ref);
  }

  GtkWidget *window = gnac_properties_get_widget("properties_window");
  gtk_widget_show_all(window);

  gnac_properties_reset_spin_if_empty("track-number");
  gnac_properties_reset_spin_if_empty("track-count");
  gnac_properties_reset_spin_if_empty("album-disc-number");
  gnac_properties_reset_spin_if_empty("album-disc-count");
  gnac_properties_reset_spin_if_empty("date");

  GtkWidget *notebook = gnac_properties_get_widget("notebook1");
  gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
  gtk_window_present(GTK_WINDOW(window));
}


static void
gnac_properties_window_hide(void)
{
  if (!gnac_properties_builder) return;

  GtkWidget *window = gnac_properties_get_widget("properties_window");
  gtk_widget_hide(window);
  properties_displayed = FALSE;
}


void
gnac_properties_set_row(GtkTreeRowReference *current)
{
  gtk_tree_row_reference_free(current_ref);
  current_ref = NULL;

  if (!gtk_tree_row_reference_valid(current)) return;

  current_ref = gtk_tree_row_reference_copy(current);
  gnac_properties_update_arrows();
  gnac_file_list_select_row(current_ref);
  gnac_properties_update_display(current_ref);
}


void
gnac_properties_on_back(GtkWidget *widget,
                        gpointer   data)
{
  if (!gtk_tree_row_reference_valid(current_ref)) return;

  GtkTreeRowReference *old_ref = gtk_tree_row_reference_copy(current_ref);

  if (gnac_file_list_get_prev_row(&current_ref)) {
    gnac_file_list_select_row(current_ref);
    gnac_properties_update_display(current_ref);
    gtk_tree_row_reference_free(old_ref);
  } else {
    gtk_tree_row_reference_free(current_ref);
    current_ref = old_ref;
  }

  gnac_properties_update_arrows();
}


void
gnac_properties_on_forward(GtkWidget *widget,
                           gpointer   data)
{
  if (!gtk_tree_row_reference_valid(current_ref)) return;

  GtkTreeRowReference *old_ref = gtk_tree_row_reference_copy(current_ref);

  if (gnac_file_list_get_next_row(&current_ref)) {
    gnac_file_list_select_row(current_ref);
    gnac_properties_update_display(current_ref);
    gtk_tree_row_reference_free(old_ref);
  } else {
    gtk_tree_row_reference_free(current_ref);
    current_ref = old_ref;   
  }

  gnac_properties_update_arrows();
}


void
gnac_properties_on_close(GtkWidget *widget,
                         gpointer   data)
{
  gnac_properties_window_hide();
}


gboolean
gnac_properties_on_delete_event(GtkWidget *widget,
                                GdkEvent  *event,
                                gpointer   data)
{
  gnac_properties_on_close(widget, data);
  /* do not send the "destroy" signal */
  return TRUE;
}


void
gnac_properties_destroy(void)
{
  if (!gnac_properties_builder) return;

  GtkWidget *window = gnac_properties_get_widget("properties_window");
  if (window) gtk_widget_destroy(window);
  g_object_unref(gnac_properties_builder);
  gnac_properties_builder = NULL;
}
