#include <stdio.h>
#include <stdlib.h>
#include <gst/gst.h>

GstElement 	*pipeline;
GstElement 	*mqueue;
GstElement 	*muxer;
GstElement 	*filesink;
GstElement 	*acodec;
GstElement 	*vcodec;

GMainLoop 	*loop;
GThread		*m_loop_thread;
GCond 		*running_cond;
GMutex		*running_mutex;

static void pad_added (GstPadTemplate * templ G_GNUC_UNUSED, GstPad * newpad, gpointer data)
{GstPad *q_sinkpad,*q_srcpad,*mpad;
 GstPadLinkReturn r;
 GstIterator *it;
 gchar *caps;

 	 q_sinkpad=gst_element_get_request_pad(mqueue,"sink%d");

 	 it=gst_pad_iterate_internal_links(q_sinkpad);

 	 if (!it ||
 		 (gst_iterator_next (it, (gpointer) & q_srcpad)) != GST_ITERATOR_OK
 	      || q_srcpad == NULL) {
 	    g_print("Couldn't get srcpad from multiqueue for sinkpad %" GST_PTR_FORMAT, q_sinkpad);
 	    return;
 	 }

 	 gst_iterator_free(it);

 	 caps=gst_caps_to_string(gst_pad_get_caps(newpad));

 	 if(g_strrstr(caps,"video")){
 		 mpad=gst_element_get_compatible_pad(vcodec,newpad,gst_pad_get_caps(newpad));
 	 }else{
 		 mpad=gst_element_get_compatible_pad(acodec,newpad,gst_pad_get_caps(newpad));
 	 }

 	 g_free(caps);
 	 //mpad=gst_element_get_compatible_pad(muxer,newpad,gst_pad_get_caps(newpad));

 	 r=gst_pad_link(newpad,q_sinkpad);
 	 g_print("pad_added, linked %s:%s to %s:%s result=%d\n",GST_DEBUG_PAD_NAME(newpad),GST_DEBUG_PAD_NAME(q_sinkpad),r);

 	 r=gst_pad_link(q_srcpad,mpad);
 	 g_print("pad_added, linked %s:%s to %s:%s result=%d\n",GST_DEBUG_PAD_NAME(q_srcpad),GST_DEBUG_PAD_NAME(mpad),r);

 	 gst_object_unref(q_srcpad);
 	 gst_object_unref(q_sinkpad);
 	 gst_object_unref(mpad);

 	 if(data)
		GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(data),GST_DEBUG_GRAPH_SHOW_ALL,"gnlcomp");
}

gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data)
{
	switch(GST_MESSAGE_TYPE(msg)){
		case GST_MESSAGE_EOS:
			g_print("eos\n");
			g_main_loop_quit(loop);
			return FALSE;
		case GST_MESSAGE_ERROR:{
			GError *err;
			gchar *debug;

			gst_message_parse_error (msg, &err, &debug);
			g_print ("Error: %s\n", err->message);
			g_error_free (err);

			if (debug) {
				g_print ("Debug deails: %s\n", debug);
				g_free (debug);
			}
			g_main_loop_quit(loop);
			return FALSE;
		}
		case GST_MESSAGE_STATE_CHANGED:{
			GstState old,new,pend;
			gst_message_parse_state_changed(msg,&old,&new,&pend);
			g_print("message state changed from %s: old:%d, new:%d, pending:%d\n",GST_MESSAGE_SRC_NAME(msg),
					old,new,pend);
			return TRUE;
		}
		default:
			g_print("message %s(%d) from %s\n",GST_MESSAGE_TYPE_NAME(msg),GST_MESSAGE_TYPE(msg),GST_MESSAGE_SRC_NAME(msg));
			return TRUE;
	}
}

void iterator_fold_function(GstElement *element,GValue *ret,gpointer user_data)
{
	if(g_strcmp0(GST_OBJECT_NAME(element),"internal-uridecodebin")==0){
		g_object_set(G_OBJECT(element),"caps",GST_CAPS(user_data),NULL);
		g_value_set_boolean(ret,FALSE);
	}else{
		g_value_set_boolean(ret,TRUE);
	}
	gst_object_unref(element);
}

void set_caps_on_internal_bin(GstElement *gnlelement,GstCaps *caps)
{GstIterator *it;
 GValue ret={0,};

 	g_value_init(&ret,G_TYPE_BOOLEAN);

	it=gst_bin_iterate_elements(GST_BIN(gnlelement));
	gst_iterator_fold(it,(GstIteratorFoldFunction)iterator_fold_function,&ret,(gpointer)caps);
	gst_iterator_free(it);
}

static void main_loop_run(gpointer data)
{
	g_main_loop_run(loop);
	g_mutex_lock(running_mutex);
	g_cond_signal(running_cond);
	g_mutex_unlock(running_mutex);
}


int main(int argc,char *argv[])
{
	GstBus *bus;
	GstElement *comp_a, *comp_v;
	GstElement *filesource1_video,*filesource1_audio;
	GstElement *filesource2_video,*filesource2_audio;
	GstCaps *video_caps, *audio_caps;
	GTimeVal timeval;
	gboolean wait_result;
	GstFormat format;
	gint64 position, duration;

	g_thread_init(NULL);
	gst_init(&argc,&argv);

	loop=g_main_loop_new(NULL,FALSE);
	running_cond=g_cond_new();
	running_mutex=g_mutex_new();

	pipeline = gst_pipeline_new("pipeline");
	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
	gst_bus_enable_sync_message_emission(bus);
	gst_bus_add_watch(bus,bus_call,NULL);

	comp_a = gst_element_factory_make("gnlcomposition","comp_a");
	g_signal_connect(G_OBJECT(comp_a),"pad-added",G_CALLBACK(pad_added),pipeline);

	comp_v = gst_element_factory_make("gnlcomposition","comp_v");
	g_signal_connect(G_OBJECT(comp_v),"pad-added",G_CALLBACK(pad_added),pipeline);

	mqueue = gst_element_factory_make("multiqueue","mqueue");

	acodec = gst_element_factory_make("twolame","acodec");

	vcodec = gst_element_factory_make("mpeg2enc","vcodec");

	muxer = gst_element_factory_make("mpegpsmux"
									 //"ffmux_dvd"
								,"muxer");
	filesink = gst_element_factory_make("filesink","fsink");
	g_object_set(filesink,"location","/home/wl/AVIK/video/gnlsaved.mpg",NULL);

	gst_bin_add_many(GST_BIN(pipeline),comp_a,comp_v,mqueue
			,acodec
			,vcodec
			,muxer,filesink,NULL);

	gst_element_link(acodec,muxer);
	gst_element_link(vcodec,muxer);
	gst_element_link(muxer,filesink);

	//video_caps=gst_caps_from_string("video/mpeg,parsed=true,systemstream=false")
	video_caps=gst_caps_from_string("video/x-raw-yuv");

	//audio_caps=gst_caps_from_string("audio/mpeg,mpegversion=1");
	audio_caps=gst_caps_from_string("audio/x-raw-int");

	filesource1_video=gst_element_factory_make("gnlfilesource","fr1v");
	g_object_set(G_OBJECT(filesource1_video),"location","file:///home/wl/AVIK/video/10.mpg",
									   "caps",video_caps,
									   "start",0*GST_SECOND,
									   "duration",5*GST_SECOND,
									   "media-start",5*GST_SECOND,
									   "media-duration",5*GST_SECOND,
									   NULL);
	gst_bin_add(GST_BIN(comp_v),filesource1_video);

	filesource1_audio=gst_element_factory_make("gnlfilesource","fr1a");
	g_object_set(G_OBJECT(filesource1_audio),"location","file:///home/wl/AVIK/video/10.mpg",
									   "caps",audio_caps,
									   "start",0*GST_SECOND,
									   "duration",5*GST_SECOND,
									   "media-start",5*GST_SECOND,
									   "media-duration",5*GST_SECOND,
									   NULL);
	gst_bin_add(GST_BIN(comp_a),filesource1_audio);


	filesource2_video=gst_element_factory_make("gnlfilesource","fr2v");
	g_object_set(G_OBJECT(filesource2_video),"location","file:///home/wl/AVIK/video/10.mpg",
									   "caps",video_caps,
									   "start",5*GST_SECOND,
									   "duration",5*GST_SECOND,
									   "media-start",125*GST_SECOND,
									   "media-duration",5*GST_SECOND,
									   NULL);
	gst_bin_add(GST_BIN(comp_v),filesource2_video);


	filesource2_audio=gst_element_factory_make("gnlfilesource","fr2a");
	g_object_set(G_OBJECT(filesource2_audio),"location","file:///home/wl/AVIK/video/10.mpg",
									   "caps",audio_caps,
									   "start",5*GST_SECOND,
									   "duration",5*GST_SECOND,
									   "media-start",125*GST_SECOND,
									   "media-duration",5*GST_SECOND,
									   NULL);
	gst_bin_add(GST_BIN(comp_a),filesource2_audio);

	gst_element_set_state(pipeline,GST_STATE_PLAYING);

	if((m_loop_thread=g_thread_create((GThreadFunc)main_loop_run,NULL,FALSE,NULL))==NULL){
		return EXIT_FAILURE;
	}

#define time_val_add 1000000

	g_mutex_lock(running_mutex);
	g_get_current_time(&timeval); g_time_val_add(&timeval,time_val_add);
	wait_result=g_cond_timed_wait(running_cond,running_mutex,&timeval);
	g_mutex_unlock(running_mutex);

	duration=position=GST_CLOCK_TIME_NONE;

	while(!wait_result){
		g_mutex_lock(running_mutex);
		g_get_current_time(&timeval); g_time_val_add(&timeval,time_val_add);
		wait_result=g_cond_timed_wait(running_cond,running_mutex,&timeval);
		g_mutex_unlock(running_mutex);
		format=GST_FORMAT_TIME;
		gst_element_query_duration(pipeline,&format,&duration);
		format=GST_FORMAT_TIME;
		gst_element_query_position(pipeline,&format,&position);
		g_print("position: [%"GST_TIME_FORMAT"/%"GST_TIME_FORMAT"]\n",GST_TIME_ARGS(position),GST_TIME_ARGS(duration));
	}

	g_print("Return from main app");
	return EXIT_SUCCESS;
}
