#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

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


static GMainLoop *mainloop = NULL;
static gint64 d = 0;
static gint64 gap = 10;

typedef enum
{
  MY_STREAM_TYPE_AUDIO = 0,
  MY_STREAM_TYPE_VIDEO = 1
} MyStreamType;

typedef struct _StreamData StreamData;
struct _StreamData
{
  GstElement *bin;
  MyStreamType type;
};

static void
_stream_decode_pad_added_cb(GstElement * decode,
							GstPad * pad, gboolean arg1, gpointer user_data)
{
  StreamData *data = (StreamData *) user_data;
  GstElement *queue;
  GstPad *sink_pad;
  GstCaps *caps = gst_pad_get_caps(pad);
  gchar *str_caps = gst_caps_to_string(caps);

  g_debug("decode caps: [%d] [%s]", data->type, str_caps);

  switch (data->type)
	{
	case MY_STREAM_TYPE_AUDIO:
	  g_debug("Audio");
	  if (strstr(str_caps, "audio") == NULL)
		goto done;
	  break;
	case MY_STREAM_TYPE_VIDEO:
	  g_debug("Video");
	  if (strstr(str_caps, "video") == NULL)
		goto done;
	  break;
	}

  queue = gst_bin_get_by_name(GST_BIN(data->bin), "queue");
  sink_pad = gst_element_get_pad(queue, "sink");

  if (gst_pad_link(pad, sink_pad) != GST_PAD_LINK_OK)
	{
	  g_warning("Failed to link decode");
	}

  gst_object_unref(queue);
  gst_object_unref(sink_pad);
 //g_free (data);
  g_debug("Linked");

done:
  gst_caps_unref(caps);
  g_free(str_caps);
}


static GstElement *
_create_src_element(const gchar * name,
					const gchar * uri, MyStreamType type, guint priority)
{
  StreamData *data;
  GstElement *bin;
  GstElement *src;
  GstElement *decode;
  GstElement *queue;
  GstPad *src_pad;

  GstElement *gnl_src;

  g_debug("element from uri: %s", uri);

  bin = gst_bin_new("bin");
  src = gst_element_make_from_uri(GST_URI_SRC, uri, "src");
  g_return_val_if_fail(src != NULL, NULL);

  decode = gst_element_factory_make("decodebin", NULL);
  g_return_val_if_fail(decode != NULL, NULL);

  queue = gst_element_factory_make("queue", "queue");
  g_return_val_if_fail(queue != NULL, NULL);

  gst_bin_add_many(GST_BIN(bin), src, decode, queue, NULL);
  gst_element_link(src, decode);

  data = g_new0(StreamData, 1);
  data->bin = bin;
  data->type = type;
  g_debug("Type : %d = %d", type, data->type);

  g_signal_connect(G_OBJECT(decode), "new-decoded-pad",
				   G_CALLBACK(_stream_decode_pad_added_cb), data);


  src_pad = gst_element_get_pad(queue, "src");
  g_return_val_if_fail(src_pad != NULL, NULL);

  gst_element_add_pad(bin, gst_ghost_pad_new("src", src_pad));

  gst_object_unref(src_pad);

  gnl_src = gst_element_factory_make("gnlsource", name);
  g_return_val_if_fail(gnl_src != NULL, NULL);
  gst_bin_add(GST_BIN(gnl_src), bin);

  g_debug("ADDING WITH: START [%lli] DUR [%lli]", d, gap);
  if (d == 0)
	{
	  g_object_set(G_OBJECT(gnl_src),
				  //"start", 0L,
				   "duration", 10 * GST_SECOND,
				  //"media-start", 0L,
				  //"media-duration", 10 * GST_SECOND,
				   "priority", priority, NULL);

	}
  else
	{
	  g_object_set(G_OBJECT(gnl_src),
				   "start", 10 * GST_SECOND, "duration", 10 * GST_SECOND,
				  ///"media-start", 10 * GST_SECOND,
				  //"media-duration", 10 * GST_SECOND,
				   "priority", priority, NULL);

	}
  d++;

  return gnl_src;
}

static void
_composition_pad_added_cb(GstElement * composition,
						  GstPad * pad, gpointer data)
{
  GstPad *sink_pad = gst_element_get_pad(GST_ELEMENT(data), "sink");
  g_debug("compose pad added");

  if (gst_pad_link(pad, sink_pad) != GST_PAD_LINK_OK)
	{
	  g_warning("Failed to link decode");
	}

  g_debug("Linked ok");
}

static void
_compose_add_file(GstElement * compose,
				  const gchar * e_name,
				  const gchar * uri, MyStreamType type, guint priority)
{
  GstElement *src;

  src = _create_src_element(e_name, uri, type, priority);
  gst_bin_add(GST_BIN(compose), src);
}


int
main(int argc, char **argv)
{
  GstElement *pipe;
  GstElement *gnl_compose_a;
  GstElement *gnl_compose_v;
  GstElement *asink;
  GstElement *vsink;
  GstElement *aqueue;
  GstElement *vqueue;

  g_type_init();
  gst_init(&argc, &argv);

  mainloop = g_main_loop_new(NULL, FALSE);

  pipe = gst_pipeline_new("test_pipeline");

  gnl_compose_a = gst_element_factory_make("gnlcomposition", "acompose");
  g_return_val_if_fail(gnl_compose_a != NULL, 1);

  gnl_compose_v = gst_element_factory_make("gnlcomposition", "vcompose");
  g_return_val_if_fail(gnl_compose_v != NULL, 1);


 //_compose_add_file (gnl_compose_a, "src0", argv[1], MY_STREAM_TYPE_AUDIO, 1);
 //_compose_add_file (gnl_compose_a, "src1", argv[2], MY_STREAM_TYPE_AUDIO, 1);

  d = 0;

  _compose_add_file(gnl_compose_v, "src2", argv[1], MY_STREAM_TYPE_VIDEO, 1);
  _compose_add_file(gnl_compose_v, "src3", argv[2], MY_STREAM_TYPE_VIDEO, 1);


 //aqueue = gst_element_factory_make ("queue", "aqueue");
 //asink = gst_element_factory_make ("alsasink", "asink");

  vqueue = gst_element_factory_make("queue", "vqueue");
  vsink = gst_element_factory_make("xvimagesink", "vsink");

  gst_bin_add_many(GST_BIN(pipe), gnl_compose_a, gnl_compose_v, vqueue, vsink,
				  //aqueue, asink, 
				   NULL);

  gst_element_link(vqueue, vsink);
 //gst_element_link (aqueue, asink);

 //g_signal_connect (G_OBJECT (gnl_compose_a), "pad-added",
 //                  G_CALLBACK (_composition_pad_added_cb), aqueue);

  g_signal_connect(G_OBJECT(gnl_compose_v), "pad-added",
				   G_CALLBACK(_composition_pad_added_cb), vqueue);


 //g_idle_add (_play, pipe);
  gst_element_set_state(GST_ELEMENT(pipe), GST_STATE_PLAYING);
  g_main_loop_run(mainloop);

  return 0;
}