/*
 * udpstateloop.c
 *
 *  Created on: 17.10.2008
 *      Author: Dejan Sakelsak
 */

#include <gst/gst.h>
#include <pthread.h>
#include <unistd.h>

GstPipeline * pipel;
GstElement * src;
GstElement * dec;
GstElement * ff1;
GstElement * vid;
GstElement * ff2;
GstElement * ff3;
GstElement * venc;
GstElement * vpay;
GstElement * ac1;
GstElement * aid;
GstElement * ac2;
GstElement * ac3;
GstElement * aenc;
GstElement * apay;
GstElement * asink;
GstElement * asinksrc;
GstElement * vsink;
GstElement * vsinksrc;
GMainLoop * loop;

static void print_states() {
  g_print("Element: %s has state %d\n", gst_element_get_name(src), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(dec), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(ff1), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(vid), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(ff2), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(ff3), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(venc), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(vpay), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(ac1), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(aid), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(ac2), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(ac3), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(aenc), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(apay), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(asink), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(asinksrc), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(vsink), gst_element_get_state(src, NULL, NULL, 2));
  g_print("Element: %s has state %d\n", gst_element_get_name(vsinksrc), gst_element_get_state(asink, NULL, NULL, 2));

}

static void link_sockets(GstElement * udpsink, GstElement * udpsrc, gint lport, gint rport, gchar * rhost) {

  gint fd;

  g_print("Linking sockects\n");

  g_print("Setting local port\n");
  gst_element_set_state(udpsrc, GST_STATE_NULL);
  g_object_set(G_OBJECT(udpsrc), "port", lport, NULL);

  g_print("Getting socket\n");
  gst_element_set_state(udpsrc, GST_STATE_PAUSED);
  g_object_get(G_OBJECT(udpsrc),"sock", &fd, NULL);

  g_print("Setting socket to: %d\n", fd);
  gst_element_set_state(udpsink, GST_STATE_NULL);
  g_object_set(G_OBJECT(udpsink), "sockfd", fd, NULL);

  g_print("Setting sink to paused and setting rport/rhost\n");
  gst_element_set_state(udpsink, GST_STATE_PAUSED);
  g_object_set(G_OBJECT(udpsink), "port", rport, "host", rhost, NULL);

}

void * exec_gst_thread(void * data) {

  g_print("Sleeping 2. for 10\n");
  sleep(10);
  g_print("Setting pipeline to READY\n");
  gst_element_set_state(GST_ELEMENT(pipel), GST_STATE_READY);
  print_states();

  g_print("Sleeping 3. for 10\n");
  sleep(10);
  g_print("Setting pipeline to PLAYING\n");
  link_sockets(vsink, vsinksrc, 5000, 5000, "10.99.8.102");
  link_sockets(asink, asinksrc, 5002, 5002, "10.99.8.102");
  gst_element_set_state(GST_ELEMENT(pipel), GST_STATE_PLAYING);
  print_states();

  g_print("Sleeping 4. for 10\n");
  sleep(10);
  g_print("Setting pipeline to READY\n");
  gst_element_set_state(GST_ELEMENT(pipel), GST_STATE_READY);
  print_states();

  g_print("Sleeping 5. for 10\n");
  sleep(10);
  g_print("Setting pipeline to PLAYING\n");
  link_sockets(vsink, vsinksrc, 5000, 5000, "10.99.8.102");
  link_sockets(asink, asinksrc, 5002, 5002, "10.99.8.102");
  gst_element_set_state(GST_ELEMENT(pipel), GST_STATE_PLAYING);
  print_states();

  g_print("Sleeping 6. for 10\n");
  sleep(10);
  g_main_loop_quit(loop);

  return NULL;
}

static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
  GMainLoop *loop = (GMainLoop *) data;

  switch (GST_MESSAGE_TYPE (msg)) {

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

      case GST_MESSAGE_ERROR: {
        gchar *debug;
        GError *error;

        gst_message_parse_error(msg, &error, &debug);
        g_free(debug);

        g_printerr("Error: %s on %s\n", error->message, GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)));
        g_error_free(error);

        break;
      }
      default:
        break;
    }

    return TRUE;
  }

  static void link_video_dynamic(GstElement * element, GstPad * pad, gpointer data) {
    GstPad *sinkpad;
    GstElement *to;
    GstPadLinkReturn ret;
    gchar * mtype;

    mtype = (gchar *) gst_structure_get_name(gst_caps_get_structure(gst_pad_get_caps(pad), 0));

    /* Check the media type present on the pad */
    //if (!g_str_has_prefix(mtype, "video")) {
    if (!g_strrstr(mtype, "video")) {
      return;
    }

    to = (GstElement *) data;
    if (!GST_IS_ELEMENT(to)) {
      return;
    }

    /* Get the pad to link to */
    sinkpad = gst_element_get_pad(to, "sink");
    /* Link the pads*/
    ret = gst_pad_link(pad, sinkpad);

    gst_object_unref(sinkpad);
  }

  static void link_audio_dynamic(GstElement * element, GstPad * pad, gpointer data) {
    GstPad *sinkpad;
    GstElement *to;
    GstPadLinkReturn ret;
    gchar * mtype;

    mtype = (gchar *) gst_structure_get_name(gst_caps_get_structure(gst_pad_get_caps(pad), 0));

    /* Check the media type present on the pad */
    if (!g_strrstr(mtype, "audio")) {
      return;
    }

    to = (GstElement *) data;
    if (!GST_IS_ELEMENT(to)) {
      return;
    }

    /* Get the pad to link to */
    sinkpad = gst_element_get_pad(to, "sink");

    /* Link the pads*/
    ret = gst_pad_link(pad, sinkpad);

    gst_object_unref(sinkpad);
  }

  int main(int argc, char ** argv) {
    GstBus * bus;
    pthread_t thr;

    //construct pipeline

    gst_init(&argc, &argv);

    loop = g_main_loop_new(NULL, FALSE);

    pipel = GST_PIPELINE(gst_pipeline_new("testpipe"));

    src = gst_element_factory_make("gnomevfssrc","src");
    dec = gst_element_factory_make("decodebin2","dec");
    ff1 = gst_element_factory_make("ffmpegcolorspace","ff1");
    vid = gst_element_factory_make("identity","vid");
    ff2 = gst_element_factory_make("ffmpegcolorspace","ff2");
    ff3 = gst_element_factory_make("ffmpegcolorspace","ff3");
    venc = gst_element_factory_make("ffenc_h263p","venc");
    vpay = gst_element_factory_make("rtph263ppay","vpay");
    vsink = gst_element_factory_make("udpsink","vsink");
    vsinksrc = gst_element_factory_make("udpsrc","vsinksrc");

    ac1 = gst_element_factory_make("audioconvert","ac1");
    aid = gst_element_factory_make("identity","aid");
    ac2 = gst_element_factory_make("audioconvert","ac2");
    ac3 = gst_element_factory_make("audioconvert","ac3");
    aenc = gst_element_factory_make("mulawenc","aenc");
    apay = gst_element_factory_make("rtppcmupay","apay");
    asink = gst_element_factory_make("udpsink","asink");
    asinksrc = gst_element_factory_make("udpsrc","asinksrc");

    gst_bin_add_many(GST_BIN(pipel), src, dec, ff1, vid, ff2, ff3, venc, vpay, vsink, vsinksrc, ac1, aid, ac2, ac3, aenc, apay, asink,asinksrc, NULL);

  gst_element_link(src, dec);
  gst_element_link_many(ff1, vid, ff2, ff3, venc, vpay, vsink, NULL);
  gst_element_link_many(ac1, aid, ac2, ac3, aenc, apay, asink, NULL);

  g_signal_connect(G_OBJECT(dec),"pad-added", G_CALLBACK(link_audio_dynamic), (gpointer) ac1);
  g_signal_connect(G_OBJECT(dec),"pad-added", G_CALLBACK(link_video_dynamic), (gpointer) ff1);

  bus = gst_pipeline_get_bus(pipel);
  gst_bus_add_watch(bus, bus_call, loop);
  gst_object_unref(bus);

  link_sockets(vsink, vsinksrc, 5000, 5000, "10.99.8.102");
  link_sockets(asink, asinksrc, 5002, 5002, "10.99.8.102");

  g_object_set(G_OBJECT(src), "location", "http://video.kiberpipa.org/media/POT_Mojca_Miklavec-ConTeXt-Latex_v_novi_preobleki/video.ogg", NULL);

  g_print("Setting pipeline to PLAYING\n");
  gst_element_set_state(GST_ELEMENT(pipel), GST_STATE_PLAYING);

  print_states();

  g_print("Sleeping 1. for 10\n");
  sleep(10);
  pthread_create(&thr, NULL, exec_gst_thread, NULL);

  g_main_loop_run(loop);

  g_print("Quitting main loop\n");
  pthread_join(thr, NULL);

  gst_element_set_state(GST_ELEMENT(pipel), GST_STATE_NULL);
  print_states();

  gst_object_unref(pipel);

  return 0;
}
