#include <gst/gst.h>
#include <glib.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
#include <stdbool.h>
#include <time.h>
#include <unistd.h>

#define MAX_THREAD 100
typedef struct
{
	int             id;
	int             nproc;
}parm;

GstElement* dynelement;
GstBus *bus;

void* setProperty(void* arg){

	parm  *p = (parm *) arg;
	int   id = p->id;
	char property[10] = "datafile";
	char value[10];
	snprintf(value,9,"%d",id);
	int iter = 0;
	while(true){
		iter++;
		if(20000 < iter)
			break;
		fprintf(stderr,"Thread id: %d property:%s -> value:%s  iter:%d\n",id,property,value,iter);
		g_object_set (G_OBJECT(dynelement), property, value, NULL);
		//usleep(250000);
		GstMessage* msg;
		msg = gst_bus_pop(bus);
		if(msg){
		  g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (msg));
		  gst_message_unref(msg);
		}
	}

	return NULL;
}

static void on_rtspsrc_pad_added (GstElement *element, GstPad *pad, void *data)
{
	GstElement *decode = GST_ELEMENT (data);
	GstPad *sinkpad;
	sinkpad = gst_element_get_static_pad (decode, "sink");
	GstPadLinkReturn ret = gst_pad_link (pad, sinkpad);
	fprintf(stderr, "return: %d\n", ret);
	gst_object_unref (sinkpad);
}

static void on_decodebin2_pad_added (GstElement *element, GstPad *pad, gboolean last, void *data)
{
	GstElement *videos = GST_ELEMENT (data);
	GstPad *sinkpad;
	sinkpad = gst_element_get_static_pad (videos, "sink");
	GstPadLinkReturn ret = gst_pad_link (pad, sinkpad);
	fprintf(stderr, "decodebin2 return: %d\n", ret);
	gst_object_unref (sinkpad);
}

//  gst-launch v4l2src device=/dev/video0 ! videoscale ! videorate !capsfilter "caps=video/x-raw-yuv,framerate=10/1" ! ffmpegcolorspace ! motioncells ! ffmpegcolorspace ! xvimagesink
int
main (int   argc,
      char *argv[])
{
  GstElement *pipeline, *source, *videor,*videos,*capsf, *colorsp0, *colorsp1, *sink, *decode;
  GstCaps* caps;
  GstStateChangeReturn ret;
  int n,i ;
  pthread_t *threads;
  pthread_attr_t pthread_custom_attr;
  parm *p;
  n = (argc > 1)?atoi(argv[1]):MAX_THREAD;
  threads=(pthread_t *)malloc(n*sizeof(*threads));
  pthread_attr_init(&pthread_custom_attr);
  p=(parm *)malloc(sizeof(parm)*n);
//  GMainLoop *loop;
//  loop = g_main_loop_new (NULL, FALSE);

  /* Initialisation */
  gst_init (&argc, &argv);

  /* Create gstreamer elements */
  pipeline = 	gst_pipeline_new		("moitoncells-pipeline");
  //source   = 	gst_element_factory_make	("v4l2src", "vidsrc");
  source   = 	gst_element_factory_make	("rtspsrc", "vidsrc");
  decode   =	gst_element_factory_make	("decodebin2","decoder");
  videor   =	gst_element_factory_make	("videorate","videor");
  videos   =	gst_element_factory_make	("videoscale","videos");
  capsf	   =	gst_element_factory_make	("capsfilter", "capsf");
  colorsp0 = 	gst_element_factory_make	("ffmpegcolorspace", "colorspace0");
  dynelement =  gst_element_factory_make	("motioncells","mcells");
  colorsp1 = 	gst_element_factory_make	("ffmpegcolorspace", "colorspace1");
  sink     = 	gst_element_factory_make	("xvimagesink", "xv-image-sink");
//  sink     = 	gst_element_factory_make	("fakesink", "xv-image-sink");
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_object_unref (bus);

  if (!pipeline || !source || !decode || !videor || !videos || !capsf || !colorsp0 || !dynelement || !colorsp1 || !sink || !bus) {
    g_printerr ("One element could not be created. Exiting.\n");
    return -1;
  }
  //g_object_set (G_OBJECT (source), "device", argv[2], NULL);
  g_object_set (G_OBJECT (source), "location", "rtsp://admin:12345@10.30.0.106:554/h.264/ch1/main", NULL);
  g_object_set (G_OBJECT (source), "latency", 1000,NULL);

  caps = gst_caps_from_string( "video/x-raw-yuv,width=320,height=240,framerate=4/1" );
  g_object_set (G_OBJECT (capsf), "caps", caps, NULL);

  gst_bin_add_many (GST_BIN (pipeline),
                    source, decode, videos,videor,capsf, colorsp0, dynelement, colorsp1, sink, NULL);
  fprintf(stderr,"before CALL BACKS \n");
  g_signal_connect (source , "pad-added", G_CALLBACK (on_rtspsrc_pad_added),decode);
  g_signal_connect (decode , "new-decoded-pad", G_CALLBACK (on_decodebin2_pad_added), videos);
  fprintf(stderr,"after CALL BACKS \n");
 
  gst_element_link_many (videos, videor,capsf, colorsp0, dynelement, colorsp1, sink, NULL);
  
  g_print ("Now playing\n");
  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);

  if (ret == GST_STATE_CHANGE_FAILURE) {
       GstMessage *msg;

       g_print ("Failed to start up pipeline!\n");

       /* check if there is an error message with details on the bus */
       msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0);
       if (msg) {
         GError *err = NULL;

         gst_message_parse_error (msg, &err, NULL);
         g_print ("ERROR: %s\n", err->message);
         g_error_free (err);
         gst_message_unref (msg);
       }
       return -1;
     }

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

  for (i=0; i<n; i++)
  {
	p[i].id=i;
	p[i].nproc = n;
	pthread_create(&threads[i], &pthread_custom_attr, setProperty, (void *)(p+i));
  }
  /* Synchronize the completion of each thread. */
  for (i = 0; i < n; i++)
  {
	pthread_join(threads[i], NULL);
  }
  free(p);

  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref(pipeline);
  return 0;
}
