
#include <gst/gst.h>

#define MP3_FILE "../test_sounds/test_long.mp3"
#define fail_unless(a,b,c) a
#define fail_if(a) a
#define ASSERT_OBJECT_REFCOUNT_BETWEEN


typedef struct _Segment {
    gdouble    rate;
    GstFormat    format;
    gint64    start, stop, position;
}   Segment;


typedef struct _CollectStructure {
    GstElement    *comp;
    GstElement    *sink;
    guint64    last_time;
    gboolean    gotsegment;
    GList        *expected_segments;
}    CollectStructure;


static GstElement *
gst_element_factory_make_or_warn (const gchar * factoryname, const gchar * name)
{
    GstElement * element;

    element = gst_element_factory_make (factoryname, name);
    fail_unless (element != NULL, "Failed to make element %s", factoryname);
    return element;
}


static void
composition_pad_added_cb (GstElement *composition, GstPad *pad, CollectStructure * collect)
{
    fail_if (!(gst_element_link (composition, collect->sink)));
}



/* return TRUE to discard the Segment */
static gboolean
compare_segments (Segment * segment, GstEvent * event)
{
    gboolean update;
    gdouble rate;
    GstFormat format;
    gint64 start, stop, position;

    gst_event_parse_new_segment (event, &update, &rate, &format, &start, &stop, &position);

    GST_DEBUG ("Got NewSegment update:%d, rate:%f, format:%d, start:%"GST_TIME_FORMAT
             ", stop:%"GST_TIME_FORMAT", position:%"GST_TIME_FORMAT,
             update, rate, format, GST_TIME_ARGS (start),
             GST_TIME_ARGS (stop),
             GST_TIME_ARGS (position));

    GST_DEBUG ("Expecting rate:%f, format:%d, start:%"GST_TIME_FORMAT
             ", stop:%"GST_TIME_FORMAT", position:%"GST_TIME_FORMAT,
             segment->rate, segment->format,
             GST_TIME_ARGS (segment->start),
             GST_TIME_ARGS (segment->stop),
             GST_TIME_ARGS (segment->position));

    if (update) {
        GST_DEBUG ("was update, ignoring");
        return FALSE;
    }
    fail_if (rate != segment->rate);
    fail_if (format != segment->format);
    fail_if (start != segment->start);
    fail_if (stop != segment->stop);
    fail_if (position != segment->position);

    GST_DEBUG ("Segment was valid, discarding expected Segment");

    return TRUE;
}


static gboolean
sinkpad_event_probe (GstPad * sinkpad, GstEvent * event, CollectStructure * collect)
{
    Segment * segment;
    
    if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
        fail_if (collect->expected_segments == NULL);
        segment = (Segment *) collect->expected_segments->data;

        if (compare_segments (segment, event)) {
            collect->expected_segments = g_list_next (collect->expected_segments);
            g_free (segment);
        }
        collect->gotsegment = TRUE;
    }

    return TRUE;
}



static gboolean
sinkpad_buffer_probe (GstPad * sinkpad, GstBuffer * buffer, CollectStructure * collect)
{
    fail_if(!collect->gotsegment);
    return TRUE;
}



static GstElement *
audio_source_bin(gchar* filename) {
    GstElement *bin, *source, *decoder, *conv;    
    GstPad *pad;

    bin = gst_bin_new ("my_bin");
    source = gst_element_factory_make_or_warn ("filesrc", "source");
    decoder = gst_element_factory_make_or_warn ("mad", "mp3-decoder");
    conv = gst_element_factory_make_or_warn ("audioconvert", "converter");

    g_object_set (G_OBJECT (source), "location", filename, NULL);

    /* take the 3 element into a bin */
    gst_bin_add_many(GST_BIN(bin), source,decoder,conv, NULL);
    gst_element_link_many(source,decoder,conv,NULL);

    /* add ghostpad */
    pad = gst_element_get_pad (conv, "src");
    gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
    gst_object_unref (GST_OBJECT (pad));

    return bin;
}




static GstElement *
audiofile_gnl_src ( const gchar * name, 
                                        gchar * filename, 
                                        guint64 start, 
                                        guint64 mstart, 
                                        gint64 duration, 
                                        guint priority)
{
    GstElement * gnlsource = NULL;
    GstElement * audiofilesrc = NULL;

    audiofilesrc = audio_source_bin (filename);
//    g_object_set (G_OBJECT (audiotestsrc), "wave", pattern, NULL);

    gnlsource = gst_element_factory_make_or_warn ("gnlsource", name);
    fail_if (gnlsource == NULL);

    if (duration == -1) {
        
        g_printerr("nincs duration\n");
    
            g_object_set (G_OBJECT (gnlsource),
                "start", start,
                "media-start", mstart,
                "priority", priority,
                NULL);
    } else {
        g_printerr("van duration\n");

            g_object_set (G_OBJECT (gnlsource),
                "start", start,
                "duration", duration,
                "media-start", mstart,
                "media-duration", duration,
                "priority", priority,
                NULL);
    }
    gst_bin_add (GST_BIN (gnlsource), audiofilesrc);
    
    return gnlsource;
}





static Segment *
segment_new (gdouble rate, GstFormat format, gint64 start, gint64 stop, gint64 position)
{
    Segment * segment;

    segment = g_new0 (Segment, 1);

    segment->rate = rate;
    segment->format = format;
    segment->start = start;
    segment->stop = stop;
    segment->position = position;

    return segment;
}






#define check_start_stop_duration(object, startval, stopval, durval)    \
    { \
        g_object_get (object, "start", &start, "stop", &stop, "duration", &duration, NULL); \
        fail_if (start != startval); \
        fail_if (stop != stopval); \
        fail_if (duration != durval); \
    }




int start ()
{
    GstElement *pipeline;
    GstElement *comp, *sink, *source1, *source2;
    CollectStructure *collect;
    GstBus *bus;
    GstMessage *message;
    gboolean    carry_on = TRUE;
    guint64 start, stop;
    gint64 duration;
    GstPad *sinkpad;

    pipeline = gst_pipeline_new ("test_pipeline");
    comp = gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
    fail_if (comp == NULL);

    source1 = audiofile_gnl_src ( 
                "source1",                            /* name */ 
                MP3_FILE,                             /* source file name */
                0,                                    /* start in pipeline */
                20,                                    /* start in media file */
                5 * GST_SECOND,                       /* duration */
                2                                     /* priority */
    );
    fail_if (source1 == NULL);

    source2 = audiofile_gnl_src (
                "source2",                            /* name */
                MP3_FILE,                             /* source file name */
                5 * GST_SECOND,                       /* start in pipeline */
                10 * GST_SECOND,                       /* start in media file */
                3 * GST_SECOND,                       /* duration */
                1                                     /* priority */
    );
    fail_if (source2 == NULL);

    /* Add one source */

    gst_bin_add (GST_BIN (comp), source1);

    /* Second source */

    gst_bin_add (GST_BIN (comp), source2);

    /* Remove second source 

    gst_object_ref (source1);
    gst_bin_remove (GST_BIN (comp), source1);
    
    

    /* Re-add second source

    gst_bin_add (GST_BIN (comp), source1);
    gst_object_unref (source1);

    */
 
    sink = gst_element_factory_make_or_warn ("alsasink", "sink");
    fail_if (sink == NULL);

    gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);

    /* Shared data */
    collect = g_new0 (CollectStructure, 1);
    collect->comp = comp;
    collect->sink = sink;
    
    /* Expected segments */
    collect->expected_segments = g_list_append (collect->expected_segments,
                                segment_new (1.0, GST_FORMAT_TIME,
                                 0, 2 * GST_SECOND, 0));

    collect->expected_segments = g_list_append (collect->expected_segments,
                                segment_new (1.0, GST_FORMAT_TIME,
                                 2 * GST_SECOND, 3 * GST_SECOND,
                                 2 * GST_SECOND));

    g_signal_connect (G_OBJECT (comp), "pad-added",
                G_CALLBACK (composition_pad_added_cb), collect);

    sinkpad = gst_element_get_pad (sink, "sink");
    gst_pad_add_event_probe (sinkpad, G_CALLBACK (sinkpad_event_probe), collect);
    gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (sinkpad_buffer_probe), collect);

    bus = gst_element_get_bus (GST_ELEMENT (pipeline));

    gst_xml_write_file(pipeline,stdout);
    
    GstStateChangeReturn buggy=gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);

    while (carry_on) {
        message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2);
    
    if (message) {
        switch (GST_MESSAGE_TYPE (message)) {
        case GST_MESSAGE_EOS:
            /* we should check if we really finished here */
            carry_on = FALSE;
        break;
        case GST_MESSAGE_SEGMENT_START:
        case GST_MESSAGE_SEGMENT_DONE:
            /* check if the segment is the correct one (0s-4s) */
            carry_on = FALSE;
        break;
        case GST_MESSAGE_ERROR:
            fail_if (TRUE);
        default:
        break;
            }
            gst_message_unref (message);
        }
    }

    fail_if (collect->expected_segments != NULL);

    gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);

    gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2);
    gst_object_unref (GST_OBJECT (sinkpad));
    ASSERT_OBJECT_REFCOUNT_BETWEEN(pipeline, "main pipeline", 1, 2);
    gst_object_unref (pipeline);
    ASSERT_OBJECT_REFCOUNT_BETWEEN(bus, "main bus", 1, 2);
    gst_object_unref (bus);
}

int
main (int argc, char **argv)
{
    gst_init (&argc, &argv);

    start();

    return 1;
}
