/*
 * 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 "libgnac-error.h"
#include "libgnac-gst.h"
#include "libgnac-gst-utils.h"
#include "libgnac-media-item.h"
#include "libgnac-metadata.h"
#include "libgnac-output.h"


extern LibgnacMetadata *metadata;


LibgnacMediaItem *
libgnac_item_new(GFile          *source,
                 LibgnacProfile *profile,
                 gpointer        parent)
{
  LibgnacMediaItem *item = g_malloc(sizeof(LibgnacMediaItem));
  
  item->parent = parent;
  item->source = g_file_dup(source);
  item->profile = profile;
  item->destination = NULL;
  item->pipeline = NULL;
  item->audio_encoder = NULL;
  item->bus = NULL;
  item->video_channel = -1;
  item->timeout_id = 0;
  item->pos = 0;

  return item;
}


static void
libgnac_item_connect_signals(LibgnacMediaItem *item)
{
  /* Connect item callbacks*/
  g_signal_connect(G_OBJECT(item->bus), "message::eos",
      G_CALLBACK(libgnac_item_eos_cb), item);
  g_signal_connect(G_OBJECT(item->bus), "message::error",
      G_CALLBACK(libgnac_item_error_cb), item);

  /* Connect parent callbacks */
  g_signal_connect(G_OBJECT(item->bus), "message::eos",
      G_CALLBACK(libgnac_converter_eos_cb), item->parent);
  g_signal_connect(G_OBJECT(item->bus), "message::error",
      G_CALLBACK(libgnac_converter_error_cb), item->parent);
}


static void
libgnac_item_build(LibgnacMediaItem  *item, 
                   GError           **error)
{
  g_return_if_fail(!error || !*error);

  LibgnacOutputConfig *config = libgnac_converter_get_config(item->parent);

  if (item->destination) {
    g_object_unref(item->destination);
    item->destination = NULL;
  }

  GError *err = NULL;
  item->destination = libgnac_output_build_output(item->source, config, &err);

  libgnac_converter_free_config(config);

  if (err) {
    g_warning("Output creation failed");
    g_propagate_error(error, err);
    return;
  }

  libgnac_gst_build_pipeline(item, &err);
  if (err) {
    if (g_error_matches(err, LIBGNAC_ERROR, LIBGNAC_ERROR_MISSING_PLUGIN) &&
        gst_install_plugins_supported())
    {
      libgnac_error_handle_missing_plugin(item, err);
      return;
    } else {
      g_warning("Unable to build pipeline");
      libgnac_gst_clean_pipeline(item);
      g_propagate_error(error, err);
      return;
    }
  }

  libgnac_item_connect_signals(item);
}


gboolean
libgnac_item_has_audio(LibgnacMediaItem *item)
{
  GError *error = NULL;
  LibgnacTags *tags = libgnac_metadata_extract(metadata, item->source, &error);
  if (error) {
    gchar *filename = g_file_get_path(item->source);
    g_debug("Failed to extract metadata for %s: %s",
        filename, error->message);
    g_free(filename);
    g_clear_error(&error);
  }
  return libgnac_metadata_tag_exists(tags, GNAC_TAG_HAS_AUDIO);
}


gboolean
libgnac_item_has_video(LibgnacMediaItem *item)
{
  GError *error = NULL;
  LibgnacTags *tags = libgnac_metadata_extract(metadata, item->source, &error);
  if (error) {
    gchar *filename = g_file_get_path(item->source);
    g_debug("Failed to extract metadata for %s: %s",
        filename, error->message);
    g_free(filename);
    g_clear_error(&error);
  }
  return libgnac_metadata_tag_exists(tags, GNAC_TAG_HAS_VIDEO);
}


void
libgnac_item_run(LibgnacMediaItem  *item, 
                 GError           **error)
{
  g_return_if_fail(!error || !*error);

  GError *err = NULL;

  /* Build pipeline if doesn't exist */
  libgnac_item_build(item, &err);
  if (err) {
    g_propagate_error(error, err);
    return;
  }

  libgnac_item_init_event_src(item);
  
  libgnac_gst_run(item, &err);
  if (err) {
    g_warning("Unable to run pipeline");
    libgnac_gst_clean_pipeline(item);
    g_propagate_error(error, err);
    return;
  }
}


void
libgnac_item_stop(LibgnacMediaItem  *item, 
                            GError **error)
{
  g_return_if_fail(!error || !*error);

  if (!item) return;

  GError *err = NULL;

  libgnac_item_clear_event_src(item);
  libgnac_gst_stop(item, &err);

  /* Remove not completed file */
  if (G_IS_FILE(item->destination)) {
    g_file_delete(item->destination, NULL, &err);
    if (err) {
      g_warning("Unable to remove partial file");
      g_clear_error(&err);
    }
  }
}


void
libgnac_item_free(LibgnacMediaItem *item)
{
  libgnac_item_clear_event_src(item);

  g_object_unref(item->source);
  item->bus = NULL;
  item->source = NULL;
  item->parent = NULL;
  // the profile is freed by the converter
  item->profile = NULL;
  if (item->destination) {
    g_object_unref(item->destination);
    item->destination = NULL;
  }

  libgnac_gst_clean_pipeline(item);

  g_free(item);
}


void
libgnac_item_init_event_src(LibgnacMediaItem *item)
{
  item->timeout_id = g_timeout_add(PROGRESS_TIMEOUT, 
      (GSourceFunc) libgnac_converter_percentage_cb, item->parent);
}


void
libgnac_item_clear_event_src(LibgnacMediaItem *item)
{
  if (!item) return;

  if (item->timeout_id) {
    g_source_remove(item->timeout_id);
    item->timeout_id = 0;
  }
}

/* Callbacks */

void
libgnac_item_eos_cb(GstBus     *bus, 
                    GstMessage *message,
                    gpointer    data)
{
  LibgnacMediaItem *item = (LibgnacMediaItem *) data;
  libgnac_item_clear_event_src(item);
  libgnac_gst_clean_pipeline(item);
}


void
libgnac_item_error_cb(GstBus    *bus,
                     GstMessage *message, 
                     gpointer    data)
{
  LibgnacMediaItem *item = (LibgnacMediaItem *) data;
  libgnac_item_clear_event_src(item);
}
