#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;
}