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

#include "libgnac-error.h"
#include "libgnac-metadata.h"
#include "libgnac-output.h"

extern LibgnacMetadata *metadata;

GSList *rename_patterns = NULL;

const gchar rename_pattern_id[] = {
  RENAME_PATTERN_ARTIST,
  RENAME_PATTERN_ALBUM,
  RENAME_PATTERN_COMMENT,
  RENAME_PATTERN_DATE,
  RENAME_PATTERN_DISC_NUMBER,
  RENAME_PATTERN_DISC_COUNT,
  RENAME_PATTERN_FILENAME,
  RENAME_PATTERN_GENRE,
  RENAME_PATTERN_TITLE,
  RENAME_PATTERN_TRACK_COUNT,
  RENAME_PATTERN_TRACK_NUMBER,
  -1,
};

static gchar *
libgnac_output_replace_pattern(LibgnacRenamePattern *pattern,
                               LibgnacTags          *tags);

static LibgnacRenamePattern *
libgnac_output_rename_pattern_new(const gchar pattern);

static void
libgnac_output_rename_patterns_free(void);

static void
libgnac_output_rename_pattern_set_replace(LibgnacRenamePattern *pattern,
                                          LibgnacTags          *tags);


static gchar *
libgnac_output_remove_extension(const gchar *filename)
{
  g_return_val_if_fail(filename, NULL);
  g_return_val_if_fail(g_utf8_validate(filename, -1, NULL), NULL);

  gchar *extension = g_utf8_strrchr(filename, -1, '.');
  if (extension) {
    glong filename_len = g_utf8_strlen(filename, -1);
    glong extension_len = g_utf8_strlen(extension, -1);
    glong new_len = filename_len - extension_len;
    gchar output[new_len + 1];
    g_utf8_strncpy(output, filename, new_len);
    return g_strdup(output);
  }

  return g_strdup(filename);
}


static gchar *
libgnac_output_remove_extension_from_file(GFile   *source_uri, 
                                          GError **error)
{
  g_return_val_if_fail(!error || !*error, NULL);
  g_return_val_if_fail(source_uri, NULL);

  GError *err = NULL;
  GFileInfo *info = g_file_query_info(source_uri,
      G_FILE_ATTRIBUTE_STANDARD_NAME,
      G_FILE_QUERY_INFO_NONE, NULL, &err);
  if (err) {
    g_debug("Unable to query GFile information");
    g_propagate_error(error, err);
    return NULL;
  }

  const gchar *filename = g_file_info_get_name(info);
  g_object_unref(info);

  return libgnac_output_remove_extension(filename);
}


static gchar *
libgnac_output_sanitize_path(const gchar *str,
                             gboolean     strip_special)
{
  /* Skip leading periods, otherwise files disappear... */
  while (*str == '.') str++;
  
  gchar *temp = g_strdup(str);

  if (strip_special) {
    /* Replace separators with a hyphen */
    g_strdelimit(temp, "\\:|", '-');
    /* Replace all other weird characters to whitespace */
    g_strdelimit(temp, "*?&!\'\"$`{}()[]<>", ' ');
    /* Replace all whitespace with underscores */
    g_strdelimit(temp, "\t ", '_');
  }

  gchar *result = g_filename_from_utf8(temp, -1, NULL, NULL, NULL);
  g_free(temp);

  return result ? result : g_strdup(str);
}


static LibgnacRenamePattern *
libgnac_output_rename_pattern_new(const gchar pattern)
{
  g_return_val_if_fail(pattern, NULL);

  LibgnacRenamePattern *rename_pattern = g_malloc(sizeof(LibgnacRenamePattern));
  g_return_val_if_fail(rename_pattern, NULL);

  gchar *full_pattern = g_strdup_printf("%c%c",
      RENAME_PATTERN_SEPARATOR, pattern);

  rename_pattern->id = pattern;
  rename_pattern->replace = NULL;
  rename_pattern->regex = g_regex_new(full_pattern, G_REGEX_OPTIMIZE, 0, NULL);
  g_free(full_pattern);
  
  return rename_pattern;
}


static void
libgnac_output_rename_pattern_set_replace(LibgnacRenamePattern *pattern,
                                          LibgnacTags          *tags)
{
  g_free(pattern->replace);
  pattern->replace = libgnac_output_replace_pattern(pattern, tags);
  /* Replace path seperators with a hyphen */
  g_strdelimit(pattern->replace, G_DIR_SEPARATOR_S, '-');
}


static gchar *
libgnac_output_replace_pattern(LibgnacRenamePattern *pattern, 
                               LibgnacTags          *tags)
{
  g_return_val_if_fail(pattern, NULL);

  switch (LIBGNAC_RENAME_PATTERN_GET_ID(pattern))
  {
    case RENAME_PATTERN_ARTIST:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_ARTIST)) {
        GValue *val = LIBGNAC_METADATA_TAG_ARTIST(tags);
        return g_value_dup_string(val);
      }
      return g_strdup(RENAME_PATTERN_DEFAULT_ARTIST);

    case RENAME_PATTERN_ALBUM:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_ALBUM)) {
        GValue *val = LIBGNAC_METADATA_TAG_ALBUM(tags);
        return g_value_dup_string(val);
      }
      return g_strdup(RENAME_PATTERN_DEFAULT_ALBUM);

    case RENAME_PATTERN_DISC_NUMBER:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_ALBUM_VOLUME_NUMBER)) {
        GValue *val = LIBGNAC_METADATA_TAG_ALBUM_VOLUME_NUMBER(tags);
        return g_strdup_printf("%d", g_value_get_uint(val));
      }
      return g_strdup(RENAME_PATTERN_DEFAULT_DISC_NUMBER);

    case RENAME_PATTERN_DISC_COUNT:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_ALBUM_VOLUME_COUNT)) {
        GValue *val = LIBGNAC_METADATA_TAG_ALBUM_VOLUME_COUNT(tags);
        return g_strdup_printf("%d", g_value_get_uint(val));
      } 
      return g_strdup(RENAME_PATTERN_DEFAULT_DISC_NUMBER);

    case RENAME_PATTERN_COMMENT:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_COMMENT)) {
        GValue *val = LIBGNAC_METADATA_TAG_COMMENT(tags);
        return g_value_dup_string(val);
      }
      return g_strdup(RENAME_PATTERN_DEFAULT_COMMENT);

    case RENAME_PATTERN_DATE:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_DATE)) {
        GValue *val = LIBGNAC_METADATA_TAG_DATE(tags);
        return g_strdup_printf("%d", g_value_get_uint(val));
      } 
      return g_strdup(RENAME_PATTERN_DEFAULT_DATE);

    case RENAME_PATTERN_FILENAME:
      if (libgnac_metadata_tag_exists(tags, GNAC_TAG_FILENAME)) {
        GValue *val = LIBGNAC_METADATA_TAG_FILENAME(tags);
        return libgnac_output_remove_extension(g_value_get_string(val));
      }
      return g_strdup(RENAME_PATTERN_DEFAULT_FILENAME);

    case RENAME_PATTERN_GENRE:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_GENRE)) {
        GValue *val = LIBGNAC_METADATA_TAG_GENRE(tags);
        return g_value_dup_string(val);
      } 
      return g_strdup(RENAME_PATTERN_DEFAULT_GENRE);

    case RENAME_PATTERN_TITLE:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_TITLE)) {
        GValue *val = LIBGNAC_METADATA_TAG_TITLE(tags);
        return g_value_dup_string(val);
      } 
      return g_strdup(RENAME_PATTERN_DEFAULT_TITLE);

    case RENAME_PATTERN_TRACK_COUNT:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_TRACK_COUNT)) {
        GValue *val = LIBGNAC_METADATA_TAG_TRACK_COUNT(tags);
        return g_strdup_printf("%02d", g_value_get_uint(val));
      } 
      return g_strdup(RENAME_PATTERN_DEFAULT_TRACK_NUMBER);

    case RENAME_PATTERN_TRACK_NUMBER:
      if (libgnac_metadata_tag_exists(tags, GST_TAG_TRACK_NUMBER)) {
        GValue *val = LIBGNAC_METADATA_TAG_TRACK_NUMBER(tags);
        return g_strdup_printf("%02d", g_value_get_uint(val));
      } 
      return g_strdup(RENAME_PATTERN_DEFAULT_TRACK_NUMBER);

    default:
      g_debug("Unknown pattern: %s", LIBGNAC_RENAME_PATTERN_GET_PATTERN(pattern));
  }

  return g_strdup_printf("%s", LIBGNAC_RENAME_PATTERN_GET_PATTERN(pattern));
}


static void
libgnac_output_rename_patterns_init(void)
{
  guint i;
  for (i = 0; rename_pattern_id[i] != -1; i++) {
    rename_patterns = g_slist_prepend(rename_patterns,
        libgnac_output_rename_pattern_new(rename_pattern_id[i]));
  }
  rename_patterns = g_slist_reverse(rename_patterns);
}


static void
libgnac_output_rename_patterns_update(LibgnacTags *tags)
{
  if (!rename_patterns) libgnac_output_rename_patterns_init();

  /* update the replacement values according to 
   * the tags of the current file */
  g_slist_foreach(rename_patterns, 
      (GFunc) libgnac_output_rename_pattern_set_replace, tags);
}


static void
libgnac_output_rename_pattern_free(LibgnacRenamePattern *pattern)
{
  if (!pattern) return;

  g_regex_unref(pattern->regex);
  g_free(pattern->replace);
  g_free(pattern);
}


static void
libgnac_output_rename_patterns_free(void)
{
  g_slist_free_full(rename_patterns,
      (GDestroyNotify) libgnac_output_rename_pattern_free);
}


static void
libgnac_output_rename_pattern_process(LibgnacRenamePattern  *pattern, 
                                      gchar                **output)
{
  gchar *temp = g_regex_replace(pattern->regex, *output, -1, 0,
      pattern->replace, 0, NULL);
  g_free(*output);
  *output = g_strdup(temp);
  g_free(temp);
}


static gchar *
libgnac_output_get_filename(GFile        *source,
                            const gchar  *rename_pattern,
                            GError      **error)
{
  GError *err = NULL;
  gchar *output = NULL;

  LibgnacTags *tags = libgnac_metadata_extract(metadata, source, &err);
  if (err) {
    g_clear_error(&err);
  }

  if (tags) {
    libgnac_output_rename_patterns_update(tags);
    output = g_strdup(rename_pattern);
    /* replace all known patterns by their value for the given file */
    g_slist_foreach(rename_patterns, 
        (GFunc) libgnac_output_rename_pattern_process, &output);
  }

  if (!output) {
    output = libgnac_output_remove_extension_from_file(source, &err);
    if (err) {
      g_debug("Unable to remove extension");
      g_propagate_error(error, err);
      return NULL;
    }
  }

  return output;
}


void
libgnac_output_finalize(void)
{
  libgnac_output_rename_patterns_free();
}


static GFile *
libgnac_output_get_output_directory(GFile               *source,
                                    LibgnacOutputConfig *config)
{
  GFile *out_directory;

  switch (config->folder_type) {

    case FOLDER_SUBDIRECTORY: {
      GFile *parent = g_file_get_parent(source);
      out_directory = g_file_get_child(parent, config->folder_path);
      g_object_unref(parent);
      break;
    }

    case FOLDER_SELECTED:
      out_directory = g_file_new_for_uri(config->folder_path);
      break;

    case FOLDER_CURRENT:
    default:
      out_directory = g_file_get_parent(source);
      break;
  }

  return out_directory;
}


GFile *
libgnac_output_build_output(GFile                *source,
                            LibgnacOutputConfig  *config,
                            GError              **error)
{
  g_return_val_if_fail(!error || !*error, NULL);

  GError *output_error = NULL;
  gchar *filename = libgnac_output_get_filename(source,
      config->rename_pattern, &output_error);
  if (output_error) {
    g_debug("Filename creation failed");
    g_propagate_error(error, output_error);
    return NULL;
  }

  gchar *output_name;
  GFile *out_directory = libgnac_output_get_output_directory(source, config);

  /* check whether we have to build a folder hierarchy */
  if (config->folder_hierarchy
      && !g_str_equal(g_strstrip(config->folder_hierarchy), ""))
  {
    /* replace all known patterns by their value */
    g_slist_foreach(rename_patterns,
        (GFunc) libgnac_output_rename_pattern_process,
        &(config->folder_hierarchy));
    /* build the output filename */
    output_name = g_strdup_printf("%s%c%s.%s",
        config->folder_hierarchy, G_DIR_SEPARATOR, filename, config->extension);
  }
  else
  {
    output_name = g_strdup_printf("%s.%s", filename, config->extension);
  }

  /* sanitize the whole path */
  gchar *sanitized = libgnac_output_sanitize_path(output_name,
      config->strip_special);

  /* create the folder hierarchy and destination file */
  GFile *destination = g_file_get_child(out_directory, sanitized);

  g_object_unref(out_directory);
  g_free(output_name);
  g_free(sanitized);
  g_free(filename);

  return destination;
}


gchar *
libgnac_output_get_preview_from_pattern(const gchar *pattern,
                                        gboolean     strip_special)
{
  gchar *preview = g_strdup(pattern);
  LibgnacTags *tags = libgnac_metadata_get_dummy_tags();
  libgnac_output_rename_patterns_update(tags);

  /* replace all known patterns by their value for the given file */
  g_slist_foreach(rename_patterns, 
      (GFunc) libgnac_output_rename_pattern_process, &preview);

  /* sanitize the filename */
  gchar *sanitized = libgnac_output_sanitize_path(preview, strip_special);

  g_free(preview);
  
  return sanitized;
}


gboolean
libgnac_output_overwrite_dest_file(LibgnacMediaItem *item,
                                   const gchar      *uri)
{
  g_debug("Overwrite file");

  GError *err = NULL;

  g_file_delete(item->destination, NULL, &err);
  if (err) {
    g_warning("Unable to overwrite file %s: %s", uri, err->message);
    g_clear_error(&err);
    return FALSE;
  }

  return TRUE;
}
