#include <string.h>

#include <gst/gst.h>
#include <glib.h>

static gboolean
bus_call (GstBus     *bus,
          GstMessage *msg,
          gpointer    data)
{
  GMainLoop *loop = (GMainLoop *) data;

  g_print ("bus message from %s, type '%s'\n",
	   GST_OBJECT_NAME (GST_MESSAGE_SRC (msg)),
	   gst_message_type_get_name (GST_MESSAGE_TYPE (msg)));

  switch (GST_MESSAGE_TYPE (msg)) {

    case GST_MESSAGE_EOS:
      g_print ("End of stream\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_SEGMENT_DONE:
      g_print ("Segment done\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_ERROR: {
      gchar  *debug;
      GError *error;

      gst_message_parse_error (msg, &error, &debug);
      g_free (debug);

      g_printerr ("Error: %s\n", error->message);
      g_error_free (error);

      g_main_loop_quit (loop);
      break;
    }
    default:
      break;
  }

  return TRUE;
}


static void
on_video_pad_added (GstElement *element,
		    GstPad     *pad,
		    gpointer    data)
{
  GstPad *sinkpad;
  GstElement *decoder = (GstElement *) data;

  g_print ("Dynamic video pad created, linking demuxer/decoder (%s)\n",
	   gst_element_get_name (decoder));

  sinkpad = gst_element_get_static_pad (decoder, "sink");
  gst_pad_link (pad, sinkpad);
  
  gst_object_unref (sinkpad);
}

int
main (int   argc,
      char *argv[])
{
  GMainLoop *loop;

  GstElement *pipeline, *source, *demuxer;
  GstElement *video_queue, *video_dec, *video_sink;

  GstBus *bus;

  /* Initialisation */
  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);


  /* Check input arguments */
  if (argc != 2) {
    g_printerr ("Usage: %s <filename>\n", argv[0]);
    return -1;
  }


  /* Create gstreamer elements */
  pipeline = gst_pipeline_new ("pipe");
  source = gst_element_factory_make ("filesrc", "source");
  demuxer = gst_element_factory_make ("matroskademux","demuxer");
  video_queue = gst_element_factory_make ("queue", "video_queue");
  video_dec = gst_element_factory_make ("ffdec_h264",     "h264_decoder");
  video_sink = gst_element_factory_make ("xvimagesink", "video_output");

  if (!pipeline || !source || !demuxer
      || !video_queue || !video_dec || !video_sink
      ) {

    if (!pipeline)
      g_printerr ("pipeline could not be created. Exiting.\n");
    else if (!source)
      g_printerr ("source could not be created. Exiting.\n");
    else if (!demuxer)
      g_printerr ("demuxer could not be created. Exiting.\n");
    else if (!video_queue)
      g_printerr ("video_queue could not be created. Exiting.\n");
    else if (!video_dec)
      g_printerr ("video_dec could not be created. Exiting.\n");
    else if (!video_sink)
      g_printerr ("video_sink could not be created. Exiting.\n");
    else
      g_printerr ("Unknown  element could not be created. Exiting.\n");

    return -1;
  }

  /* Set up the pipeline */

  /* set the input filename to the source element */
  g_object_set (G_OBJECT (source), "location", argv[1], NULL);
  g_object_set (G_OBJECT (video_sink), "sync", TRUE, NULL);

#if 0 // Makes the seek below work even without the patch in the matroska demuxer.
  g_object_set (G_OBJECT (video_queue), "max-size-time", 11*GST_SECOND, NULL);
#endif

  /* add a message handler */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

  /* add all elements into the pipeline */
  gst_bin_add_many (GST_BIN (pipeline), source, demuxer, video_queue, video_dec,
                    video_sink, NULL);

  /* link the elements together */
  gst_element_link (source, demuxer);
  gst_element_link_many (video_queue, video_dec, video_sink, NULL);

  g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_video_pad_added), video_queue);

  /* Set the pipeline to "paused" state*/
  gst_element_set_state (pipeline, GST_STATE_PAUSED);

  /* wait for preroll, in a kind of ugly way ... */
  while (gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE) == 
         GST_STATE_CHANGE_ASYNC) {
      g_usleep (10000);
  }


  g_object_set (G_OBJECT (video_queue), "leaky", 0, NULL);
  if (!gst_element_seek (GST_ELEMENT_CAST (demuxer), 1.0, GST_FORMAT_TIME, 
                         GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
                         GST_SEEK_TYPE_SET, 5 * GST_SECOND,
                         GST_SEEK_TYPE_SET, 10 * GST_SECOND)) {
    g_print ("Seek failed!\n");
  } else {
    g_print ("Segment seek succeeded!\n");
  }

  /* Set the pipeline to "playing" state*/
  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* Iterate */
  g_print ("Running...\n");
  g_main_loop_run (loop);

  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (GST_OBJECT (pipeline));

  exit(0);
}
