/*
  NAME
    duration-bug.c - prototype to investigate wrong duration for cuts in GES

  DESCRIPTION
    Demonstrates bug when GStreamer Editing Services cuts a video file.
    Let say one specifies a in-point of 3.0 seconds, and a duration of 6.0
    seconds, the resulting clip should last 6.0 seconds, but it lasts only
    3.0 seconds.
    So, the duration is taken as an outpoint instead.
    But the outpoint should be inpoint + duration

  HISTORY
    2013-05-16  Created by Kim Lam
    2013-08-08  Modified to work on GNU/Linux by Alexandre Quesssy (Collabora)

  Copyright (c) 2013 Gradient Software Inc.

  Compile with:

  (potentially)
  export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig

  gcc -o duration-bug duration-bug.c -g `pkg-config --libs --cflags gst-editing-services-0.10`

  Run it:

  ./duration-bug file:///home/aalex/Videos/video-01.webm 3 6

  (replace with the proper URI)
*/
#include <gst/gst.h>
#include <ges/ges.h>

typedef struct _App
{
  GstBus *bus;
  GESTimelinePipeline *pipeline;
  GESTimeline *timeline;
  GESTimelineLayer *VideoLayer1;
  GESTimelineLayer *AudioLayer;
  GESTrack *audioTrack;
  GESTrack *videoTrack1;  
} App;

static GMainLoop *mainloop;

static void play (App *app)
{
  g_print ("set state playing\n");
  gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_PLAYING);
}

static void stop (App *app)
{
  gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_NULL);
}

void pipeline1_bus_message_cb (GstBus *bus, GstMessage *message, App *app)
{
  GError *err;
  gchar *debug_info;

  const GstStructure *s;
  s = gst_message_get_structure(message);
  switch (GST_MESSAGE_TYPE(message))
  {
    case GST_MESSAGE_ERROR:
      g_print("ERROR\n");
      gst_message_parse_error(message, &err, &debug_info);
      g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME (message->src), err->message);
      g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
      g_clear_error(&err);
      g_free(debug_info);
      g_print ("ERROR: quit\n");
      g_main_loop_quit(mainloop);
      /* exit(1);
       */
      break;
    case GST_MESSAGE_EOS:
    {
      gst_element_set_state(GST_ELEMENT(app->pipeline), GST_STATE_PAUSED);
      g_print ("EOS: quit\n");
      g_main_loop_quit(mainloop);
      break;
    }
    case GST_MESSAGE_STATE_CHANGED:
    {
      GstState old, new, pending;
      gst_message_parse_state_changed (message, &old, &new, &pending);
      if (GST_ELEMENT (message->src) == GST_ELEMENT (app->pipeline))
      {
        if (new == GST_STATE_PLAYING)
          g_print ("state changed: playing %s \n", GST_OBJECT_NAME (message->src));
        else if (new == GST_STATE_PAUSED)
          g_print ("state changed: paused %s\n", GST_OBJECT_NAME (message->src));
        else if (new == GST_STATE_READY)
          g_print ("state changed: ready %s\n", GST_OBJECT_NAME (message->src));
        else if (new == GST_STATE_NULL)
          g_print ("state changed: null %s\n", GST_OBJECT_NAME (message->src));
      }
      break;
    }
    case GST_MESSAGE_ASYNC_DONE:
      break;
    case GST_MESSAGE_BUFFERING:
    {
      gint percent;      
      gst_message_parse_buffering(message, &percent);
      g_message("Buffering message received %i", percent);
      break;
    }
    default: 
      break;
  }
}

static void setupPlayer(App *app)
{
  app->timeline = ges_timeline_new ();
  app->pipeline = ges_timeline_pipeline_new ();
  ges_timeline_pipeline_add_timeline (app->pipeline, app->timeline);
  app->bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline)); 
  gst_bus_add_signal_watch (app->bus); 
  g_signal_connect (app->bus, "message", G_CALLBACK (pipeline1_bus_message_cb), app);

  // Create tracks and layer
  app->VideoLayer1 = ges_timeline_layer_new ();
  ges_timeline_layer_set_priority (app->VideoLayer1, 0);  
  ges_timeline_add_layer (app->timeline, app->VideoLayer1);  

  app->AudioLayer = ges_timeline_layer_new ();    
  ges_timeline_layer_set_priority (app->AudioLayer, 2);  
  ges_timeline_add_layer (app->timeline, app->AudioLayer);

  app->videoTrack1 = ges_track_video_raw_new ();
  ges_timeline_add_track (app->timeline, app->videoTrack1);  
  app->audioTrack = ges_track_audio_raw_new ();
  ges_timeline_add_track (app->timeline, app->audioTrack);
}

static void
setup_video_sink (App *app)
{
  GstElement *sink;
  gchar *name;
  sink = gst_element_factory_make ("xvimagesink", "xvimagesink0");
  ges_timeline_pipeline_preview_set_video_sink (app->pipeline, sink);
  name = gst_element_get_name (sink);
  g_free (name);
}

static void createTimeline(App *app,  gchar *fileUri, GstClockTime inpoint, GstClockTime duration)
{
  GESTimelineObject *clip1;
  clip1 = GES_TIMELINE_OBJECT (ges_timeline_filesource_new (fileUri));

  if (clip1 == NULL)
  {
    g_print ("Could not create the GESTimelieFileSource with URI %s", fileUri);
  }

  g_print ("set start=%ld duration=%ld\n", inpoint, duration);
  ges_timeline_object_set_start(clip1, 0);
  ges_timeline_object_set_inpoint(clip1, inpoint);
  ges_timeline_object_set_duration(clip1, duration);
  ges_timeline_object_set_priority(clip1, 5);

  ges_timeline_layer_add_object(app->VideoLayer1, clip1);
}

int main(int argc, char *argv[])
{
  App *app;
  GstClockTime inpoint, duration;
  if (argc != 4)
  {
    g_print ("Usage: %s fileuri inpoint duration\n Time units in seconds.\n", argv[0]);
    return 0;
  }
  gst_init (NULL, NULL);
  ges_init ();
  /* gst_debug_set_default_threshold(GST_LEVEL_ERROR);
   */

  mainloop = g_main_loop_new(NULL, FALSE);
  /* gst_debug_set_default_threshold(GST_LEVEL_INFO);
   */
  inpoint = (GstClockTime) ((atoi (argv[2])) * GST_SECOND);
  duration = (GstClockTime) ((atoi (argv[3])) * GST_SECOND);

  app = (App *) g_new0 (App, 1);
  setupPlayer (app);
  createTimeline (app, argv[1], inpoint, duration);
  setup_video_sink (app);

  play (app);

  g_print ("running main loop\n");
  g_main_loop_run(mainloop); 

  g_print ("quitting");
  return 0;
}
