#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <gst/gst.h>
#include <gst/gstelement.h>
#include <gst/controller/gstcontroller.h>
#include <gst/controller/gstinterpolationcontrolsource.h>

static GstElement *bin, *rtspsrc, *rtpjpegdepay, *jpegdec, *glupload, *glshader, *glimagesink;
static GstController *controller;
static int fifo;
static char buffer[2048];
static size_t bufsize=2048;

static void set_shader_variables(GstElement *glshader, gchar *variables,guint64 position,GstController **ctrl);

static void
event_loop (GstElement * pipe)
{
  GstBus *bus;
  GstMessage *message = NULL;
  ssize_t count;
  ssize_t offset=0;


  bus = gst_element_get_bus (GST_ELEMENT (pipe));

  while (TRUE) {
    message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND/5 );

    count=read(fifo,buffer+offset,bufsize-offset);
    if (count) {
	offset+=count;
	buffer[offset]=0;
	if (buffer[offset-1]==0x0a) {
		printf("%s",buffer);
		buffer[offset-1]=0;
		set_shader_variables(glshader,buffer,0,&controller);
		offset=0;
	}
	if (bufsize-offset==0) {
		offset=0;
	}
    }

    if (message == NULL) continue;

    switch (message->type) {
      case GST_MESSAGE_EOS:
        gst_message_unref (message);
        return;
      case GST_MESSAGE_WARNING:
      case GST_MESSAGE_ERROR:{
        GError *gerror;
        gchar *debug;

        gst_message_parse_error (message, &gerror, &debug);
        gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
        gst_message_unref (message);
        g_error_free (gerror);
        g_free (debug);
        return;
      }
      default:
        gst_message_unref (message);
        break;
    }
  }
}

void on_pad_added (GstElement *element, GstPad *pad)
{
        GstCaps *caps;
        GstStructure *str;

        g_debug ("Signal: pad-added");

        caps = gst_pad_get_caps (pad);
        g_assert (caps != NULL);
        str = gst_caps_get_structure (caps, 0);
        g_assert (str != NULL);

	g_debug("new pad caps name: %s\n",gst_structure_get_name (str));

        if (g_strrstr (gst_structure_get_name (str), "application/x-rtp")) {
                // Link it actually
                GstPad *targetsink = gst_element_get_static_pad (rtpjpegdepay, "sink");
                g_debug ("Linking pad to rtpjpegdepay");
                g_assert (targetsink != NULL);
                gst_pad_link (pad, targetsink);
                gst_object_unref (targetsink);
        }

        gst_caps_unref (caps);
} 

static void set_shader_variables(GstElement *glshader, gchar *variables,guint64 position,GstController **ctrl) {
  GstController *c;
  GstInterpolationControlSource *csource;
  GValue value={0,};

  if (*ctrl) {
	  g_object_unref(*ctrl);
	  *ctrl=0;
  }
  if (!*ctrl) {
	c = gst_controller_new (G_OBJECT (glshader), "vars", NULL);
	  if (!c) {
		GST_ERROR("cannot control glshader variables\n");
		exit(1);
 	 }
 	 *ctrl=c;
  }

  csource = gst_interpolation_control_source_new ();
  gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_NONE);
  gst_controller_set_control_source (c, "vars", GST_CONTROL_SOURCE (csource));
  g_object_unref (csource);

  g_value_init (&value, G_TYPE_STRING);
  g_value_set_string (&value, variables);
  gst_interpolation_control_source_set (csource, position, &value);
  g_value_unset (&value);

}

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

  if (argc < 3 || argc > 4) {
    g_print ("usage: %s <rtsp> <fragment_shader> [<fragment_shader_preset_file>]\n", argv[0]);
    exit (-1);
  }

  /* create a new bin to hold the elements */
  bin = gst_pipeline_new ("pipeline");
  g_assert (bin);

  /* rtspsrc */
  rtspsrc = gst_element_factory_make ("rtspsrc", "camera_source");
  if (!rtspsrc) {
    g_print ("could not find plugin \"rtspsrc\"");
    return -1;
  }
  g_assert (rtspsrc);
  g_object_set (G_OBJECT (rtspsrc), "location", argv[1], NULL);

  /* rtpjpegdepay */
  rtpjpegdepay = gst_element_factory_make ("rtpjpegdepay", "rtp_depay");
  if (!rtpjpegdepay) {
    g_print ("could not find plugin \"rtpjpegdepay\"");
    return -1;
  }

  /* jpegdec */
  jpegdec = gst_element_factory_make ("jpegdec", "jpegdec");
  if (!jpegdec) {
    g_print ("could not find plugin \"jpegdec\"");
    return -1;
  }

  /* glupload */
  glupload = gst_element_factory_make ("glupload", "glupload");
  if (!glupload) {
    g_print ("could not find plugin \"glupload\"");
    return -1;
  }

  /* glshader */
  glshader = gst_element_factory_make ("glshader", "fragment_shader");
  if (!glshader) {
    g_print ("could not find plugin \"glshader\"");
    return -1;
  }
  g_assert (glshader);
  g_object_set (G_OBJECT (glshader), "location", argv[2], NULL);
  if (argc==4) {
	  g_object_set (G_OBJECT (glshader), "preset", argv[3], NULL);
  }

  /* glimagesink */
  glimagesink = gst_element_factory_make ("glimagesink", "play_video");
  if (!glimagesink) {
    g_print ("could not find plugin \"glimagesink\"");
    return -1;
  }

  /* add objects to the main pipeline */
  gst_bin_add_many (GST_BIN (bin), rtspsrc, rtpjpegdepay, jpegdec, glupload, glshader, glimagesink, NULL);

  /* link the elements */
  gst_element_link_many (rtpjpegdepay, jpegdec, glupload, glshader, glimagesink, NULL);

  g_signal_connect (rtspsrc, "pad-added", G_CALLBACK (on_pad_added), NULL); 

  /* start playing */
  gst_element_set_state (bin, GST_STATE_PLAYING);

  fifo=open("fifo", O_RDONLY | O_NONBLOCK);
  if (fifo==-1) {
	  fprintf(stderr,"cant open fifo\n");
	  exit(1);
  }
  //ffifo=fdopen( fifo, "R");
  //setlinebuf(ffifo);


  /* Run event loop listening for bus messages until EOS or ERROR */
  event_loop (bin);

  /* stop the bin */
  gst_element_set_state (bin, GST_STATE_NULL);

  exit (0);
}
