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