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

static gboolean timeoutcall (GMainLoop *loop) {
	g_main_loop_quit(loop);
	return FALSE;
}

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

  switch (GST_MESSAGE_TYPE (msg)) {

    case GST_MESSAGE_EOS:
      g_print ("End of stream\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;
}

GstElement *m_pipeline;
GstElement *m_audioGraph;
GstElement *m_videoGraph;
GstElement *m_datasource;
GstElement *m_decodebin;
GstElement *m_audioPipe;
GstElement *m_videoPipe;
GstElement *m_fakeAudioSink;
GstElement *m_fakeVideoSink;

static void
on_pad_added (GstElement *element,
	      GstPad     *pad,
	      gboolean    last,
              gpointer    data)
{
  GstPad *sinkpad;

  g_print ("Dynamic pad created\n");

  sinkpad = gst_element_get_static_pad (m_videoGraph, "sink");

  /* Quick hack, pads can be linked to wrong graphs here */
  GstPadLinkReturn x = gst_pad_link (pad, sinkpad);
  g_print("link to videograph, return %d\n", x);
  if (x) {
	  sinkpad = gst_element_get_static_pad (m_audioGraph, "sink");
	  GstPadLinkReturn x = gst_pad_link (pad, sinkpad);
	  g_print("link to audiograph, return %d\n", x);
  }

  gst_object_unref (sinkpad);
}

int
main (int   argc,
      char *argv[])
{
  GMainLoop *loop;
  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;
  }

  m_pipeline = gst_pipeline_new ("PIPELINE");
  m_decodebin = gst_element_factory_make ("decodebin2", "DECODEBIN");
  gst_bin_add(GST_BIN(m_pipeline), m_decodebin);
  
  m_audioGraph = gst_bin_new("AUDIOGRAPH");
  m_audioPipe = gst_element_factory_make("queue", "AUDIOPIPE");
  gst_bin_add(GST_BIN(m_audioGraph), m_audioPipe);
  GstPad *audiopad = gst_element_get_pad (m_audioPipe, "sink");
  gst_element_add_pad (m_audioGraph, gst_ghost_pad_new ("sink", audiopad));
  gst_bin_add(GST_BIN(m_pipeline), m_audioGraph);
  
  m_videoGraph = gst_bin_new("VIDEOGRAPH");
  m_videoPipe = gst_bin_new("VIDEOPIPE");
  gst_bin_add(GST_BIN(m_videoGraph), m_videoPipe);
  
  GstElement *videoQueue = gst_element_factory_make("queue", "VIDEOQUEUE");
  
  GstElement *subtitles = gst_element_factory_make("subtitleoverlay", "SUBIOVERLAY");

  gst_bin_add(GST_BIN(m_videoPipe), videoQueue);
  
  gst_bin_add(GST_BIN(m_videoPipe), subtitles);
  
  GstPad *videoPad;
  GstPad *subtitlePad;
  GstPad *videoOutPad;
  
  // Link queue to container bin
  videoPad = gst_element_get_pad(videoQueue, "sink");
  
  gst_element_add_pad(m_videoPipe, gst_ghost_pad_new("sink", videoPad));

//   VIDEOQUEUE => SUBIOVERLAY
  gst_element_link_pads(videoQueue, "src", subtitles, "video_sink");

  subtitlePad = gst_element_get_pad(subtitles, "subtitle_sink");
  gst_element_add_pad(m_videoPipe, gst_ghost_pad_new("subtitle_sink", subtitlePad));

  // SUBIOVERLAY => VIDEOPIPE_src
  videoOutPad = gst_element_get_pad(subtitles, "src");
  gst_element_add_pad(m_videoPipe, gst_ghost_pad_new("src", videoOutPad));

  // Link container bin to video graph bin
  videoPad = gst_element_get_pad(m_videoPipe, "sink");
  gst_element_add_pad(m_videoGraph, gst_ghost_pad_new("sink", videoPad));
  
  subtitlePad = gst_element_get_pad(m_videoPipe, "subtitle_sink");
  gst_element_add_pad(m_videoGraph, gst_ghost_pad_new("subtitle_sink", subtitlePad));
  gst_bin_add(GST_BIN(m_pipeline), m_videoGraph);

  g_signal_connect (m_decodebin, "new-decoded-pad", G_CALLBACK (on_pad_added), m_audioGraph);
  
  m_datasource = gst_element_make_from_uri(GST_URI_SRC, argv[1], (const char*)NULL);
  if (!m_datasource)
    return false;
    
  gst_bin_add(GST_BIN(m_pipeline), m_datasource);
  gst_element_link(m_datasource, m_decodebin);
 
  m_fakeAudioSink = gst_element_factory_make("fakesink", NULL);
  m_fakeVideoSink = gst_element_factory_make("fakesink", NULL);

  gst_bin_add(GST_BIN(m_audioGraph), m_fakeAudioSink);
  gst_bin_add(GST_BIN(m_videoGraph), m_fakeVideoSink);
  gst_element_link_pads(m_audioPipe, "src", m_fakeAudioSink, "sink");
  gst_element_link_pads(m_videoPipe, "src", m_fakeVideoSink, "sink");

  bus = gst_pipeline_get_bus (GST_PIPELINE (m_pipeline));
  gst_bus_add_watch (bus, bus_call, loop);

  /* Set the pipeline to "playing" state*/
  g_print ("Now playing: %s\n", argv[1]);
  
  gst_element_set_state (m_pipeline, GST_STATE_READY);
  g_timeout_add(1000, (GSourceFunc)timeoutcall, loop);
  g_print ("Running...\n");
  g_main_loop_run (loop);
  
  gst_element_set_state (m_pipeline, GST_STATE_PAUSED);
  g_timeout_add(1000, (GSourceFunc)timeoutcall, loop);
  g_print ("Running...\n");
  g_main_loop_run (loop);
  
  gst_element_set_state (m_pipeline, GST_STATE_READY);
  g_timeout_add(1000, (GSourceFunc)timeoutcall, loop);
  g_print ("Running...\n");
  g_main_loop_run (loop);

  gst_element_set_state (m_pipeline, GST_STATE_PAUSED);
  g_timeout_add(5000, (GSourceFunc)timeoutcall, loop);
  g_print ("Running...\n");
  g_main_loop_run(loop);

  GstState statee, pending;
  GstStateChangeReturn y = gst_element_get_state(m_pipeline, &statee, &pending, 1000000000);
  g_print("STATE state %d %d\n", statee, pending);
  _gst_debug_bin_to_dot_file (GST_BIN (m_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "gst-frozen-pipeline");
  return 0;
}
