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


typedef struct {
    GstElement * pipeline;
    GMainLoop * loop;
    GstTagList * tags;
    GstTagSetter * setter;
} callbackdata;


static void on_pad_added (GstElement *element, GstPad *pad, gpointer data) {

    GstElement * muxer = (GstElement *) data;
    GstElement * queue = NULL;

    GstElement * pipeline = GST_ELEMENT (GST_OBJECT_PARENT (GST_OBJECT(element)));
    
    queue = gst_element_factory_make ("queue", NULL);
    if (queue == NULL) {
        g_printf ("FAIL creating queue\n");
    } else {
        if (!gst_bin_add (GST_BIN (pipeline), queue)) {
            g_printf ("failed to add queue to pipeline\n");
            return;
        }
    }
   
    
    if (gst_element_link_many (element, queue, muxer, NULL))
        gst_element_set_state (queue, GST_STATE_PLAYING);
    else
        g_printf ("Linking elements FAILED\n");
}



static void print_tag (const GstTagList * list, const gchar * tag, 
    gpointer unused) {

    gint i, count;

    count = gst_tag_list_get_tag_size (list, tag);

    for (i = 0; i < count; i++) {
        gchar *str;

        if (gst_tag_get_type (tag) == G_TYPE_STRING) {
            if (!gst_tag_list_get_string_index (list, tag, i, &str))
                g_assert_not_reached ();
            } else {
                str =
                g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i));
            }
            if (i == 0) {
                g_print ("  %15s: %s\n", gst_tag_get_nick (tag), str);
            } else {
                g_print ("                 : %s\n", str);
        }
        g_free (str);
    }
}

static gboolean
bus_call (GstBus     *bus,
          GstMessage *msg,
          gpointer    data)
{
    callbackdata * calldata = (callbackdata*) data;
    
  GMainLoop *loop = calldata->loop;
    
  switch (GST_MESSAGE_TYPE (msg)) {

    case GST_MESSAGE_EOS:
      g_print ("End of stream\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_INFO: {
        GError * error2;
        gchar * debug2;
        gst_message_parse_info (msg, &error2, &debug2);
        g_printf ("%s\n", debug2);
        
        gchar * debug;
        GError * error;
        
        gst_message_parse_info (msg, &error, &debug);
        g_free (debug);
        
        g_printerr ("Info: %s\n", error->message);
        g_error_free (error);
        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;
    }
    case GST_MESSAGE_TAG: {
        GstElement * elem = GST_ELEMENT (GST_MESSAGE_SRC(msg));
        g_printf ("%s - ", gst_element_get_name(elem));
        g_printf("GST_MESSAGE_TAG received\n");
        gchar * title = NULL;
        GstTagList *new_tags;
        gst_message_parse_tag (msg, &new_tags);
        gst_tag_list_foreach (new_tags, print_tag, NULL); 
        gst_tag_list_free (new_tags);
 
    break;   
    }
    case GST_MESSAGE_STATE_CHANGED: {
        
        GstElement * elem = GST_ELEMENT (GST_MESSAGE_SRC(msg));
        
        g_printf ("%s - ", gst_element_get_name(elem));
        
            GstState oldstate, newstate, pending;
            
            gst_message_parse_state_changed (msg, &oldstate, &newstate, &pending);
            
            if (GST_STATE_TRANSITION (oldstate, newstate) == 
                GST_STATE_CHANGE_NULL_TO_READY) {
                    g_printf ("GST_STATE_CHANGE_NULL_TO_READY\n");
            } else if (GST_STATE_TRANSITION (oldstate, newstate) == 
                    GST_STATE_CHANGE_READY_TO_PAUSED) {
                    g_printf ("GST_STATE_CHANGE_READY_TO_PAUSED\n");
            } else if (GST_STATE_TRANSITION (oldstate, newstate) ==
                GST_STATE_CHANGE_PAUSED_TO_PLAYING) {
                    g_printf ("GST_STATE_CHANGE_PAUSED_TO_PLAYING\n");
            } else if (GST_STATE_TRANSITION (oldstate, newstate) == 
                GST_STATE_CHANGE_PLAYING_TO_PAUSED) {
                    g_printf ("GST_STATE_CHANGE_PLAYING_TO_PAUSED\n");
            } else if (GST_STATE_TRANSITION (oldstate, newstate) == 
                GST_STATE_CHANGE_PAUSED_TO_READY) {
                    g_printf ("GST_STATE_CHANGE_PAUSED_TO_READY\n");
            } else if (GST_STATE_TRANSITION (oldstate, newstate) == 
                GST_STATE_CHANGE_READY_TO_NULL) {
                    g_printf ("GST_STATE_CHANGE_READY_TO_NULL\n");
            }
        
    }
    default: {

      break;
  }
 }
  return TRUE;
}


int main (int argc, char *argv[]) {

    GMainLoop * loop = NULL;

    GstElement * pipeline = NULL;
    GstElement * source = NULL;
    GstElement * demuxer = NULL;
    GstElement * muxer = NULL;
    GstElement * sink = NULL;
    
    GstBus * bus = NULL;
    
    callbackdata calldata;
    
    g_type_init ();
    g_thread_init(NULL);
    
    calldata.tags = gst_tag_list_new();
    
    /* initialization */
    gst_init (&argc, &argv);
    
    calldata.loop = loop = g_main_loop_new (NULL, FALSE);
    
    
    /* Check input arguments */
    if (argc != 3) {
        g_printerr ("Usage: %s <inputfilename> <outputfilename>\n", argv[0]);
    return -1;
    }

    calldata.pipeline = pipeline = gst_pipeline_new ("tag-filter");
    source = gst_element_factory_make ("filesrc", "file-source");
    demuxer = gst_element_factory_make ("avidemux", "avi-demuxer");
    muxer = gst_element_factory_make ("avimux", "avi-muxer");
    sink = gst_element_factory_make ("filesink", "file-sink");
    
    
    if (!pipeline || !source || !demuxer || !muxer || !sink) {
        g_printerr ("One element could not be created. Exiting.\n");
        return -1;
    }
    
    /* Set up the pipeline */
    /* We set the input and output filenames */
    g_object_set (G_OBJECT (source), "location", argv[1], NULL);
    g_object_set (G_OBJECT (sink), "location", argv[2], NULL);    

    /* we add message handler */
    bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
    gst_bus_add_watch (bus, bus_call, &calldata);
    gst_object_unref (bus);
    
    /* add elements to pipeline */
    gst_bin_add_many (GST_BIN (pipeline),
                    source, demuxer, muxer, sink, NULL);
    /* linking */
    gst_element_link (source, demuxer);
    gst_element_link (muxer, sink);
 
    g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), muxer);

    calldata.setter = GST_TAG_SETTER (gst_bin_get_by_interface (
        GST_BIN (pipeline), GST_TYPE_TAG_SETTER));
    g_printf ("GstSetter: %s\n", gst_element_get_name (calldata.setter));
  
    gst_tag_list_add(calldata.tags, GST_TAG_MERGE_REPLACE_ALL,
        GST_TAG_TITLE, "example title", 
        GST_TAG_ARTIST, "example artist",
        GST_TAG_GENRE, "example genre",
        GST_TAG_COMMENT, "example comment",
        GST_TAG_DESCRIPTION, "example description",
        GST_TAG_COPYRIGHT, "example copyright",
  /*      GST_TAG_TITLE, "t", 
        GST_TAG_ARTIST, "a",
        GST_TAG_GENRE, "g",
        GST_TAG_COMMENT, "c",
        GST_TAG_DESCRIPTION, "d",
        GST_TAG_COPYRIGHT, "o",
    */
        NULL);
        
    gst_tag_setter_merge_tags (calldata.setter, calldata.tags,
        GST_TAG_MERGE_REPLACE_ALL);
    gst_tag_setter_set_tag_merge_mode (calldata.setter, GST_TAG_MERGE_REPLACE_ALL);
    
    /* set the pipeline to "playing" state */
    g_print("Now playing...\n");
    gst_element_set_state (pipeline, GST_STATE_PLAYING);

    
    /* Iterate */
    g_print ("Running...\n");
    g_main_loop_run (loop);

    
    /* Out of the main loop, clean up nicely */
    g_print ("Returned, stopping playback\n");
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (calldata.pipeline);
    g_print ("Deleting pipeline\n");
    gst_object_unref (GST_OBJECT (pipeline));

  return 0;

}
