#include "gstreamer.h"

using namespace TT::Modules::Utilities::Video;

//library objects
#include "gst\gst.h"

GStreamer::GStreamer()
{
	//initialization
	Initialziation();
}

GStreamer::~GStreamer()
{
}

void GStreamer::Initialziation()
{
	//init gstreamer library
	gst_init(nullptr, nullptr);

	loop = g_main_loop_new(NULL, FALSE);

	//init papeline
	_pipeline = gst_pipeline_new("VideoRecorder");
	if (!_pipeline)
	{
		throw 1;
	}

#pragma region video pipeline
	//init video source
	_videoSource = gst_element_factory_make("appsrc", "VideoFrame");//appsrc videotestsrc
	if (!_videoSource)
	{
		throw 2;
	}
	g_object_set(G_OBJECT(_videoSource), "stream-type", 0, "format", GST_FORMAT_TIME, NULL);

	//init video filter
	_videoFilter = gst_element_factory_make("capsfilter", "VideoFilter");
	if (!_videoFilter)
	{
		throw 2;
	}
	_videoFormat = gst_caps_new_simple("video/x-raw",
		"format", G_TYPE_STRING, "RGB",
		"width", G_TYPE_INT, 800,
		"height", G_TYPE_INT, 400,
		"framerate", GST_TYPE_FRACTION, 0, 1,
		NULL);
	g_object_set(G_OBJECT(_videoFilter), "caps", _videoFormat, NULL);

	//init video converter
	_videoConverter = gst_element_factory_make("jpegenc", "VideoConvertor"); //videoconvert
	if (!_videoConverter)
	{
		throw 2;
	}
#pragma endregion

#pragma region sublitles
	//init video source
	_subtitleSource = gst_element_factory_make("appsrc", "RaceSubtitles");
	if (!_subtitleSource)
	{
		throw 2;
	}
	g_object_set(G_OBJECT(_subtitleSource), "stream-type", 0, "format", GST_FORMAT_TIME, NULL);

	//init video filter
	_subtitleFilter = gst_element_factory_make("capsfilter", "SubtitlesFilter");
	if (!_subtitleFilter)
	{
		throw 2;
	}
	_subtitleFormat = gst_caps_new_simple("text/x-raw",
		"format", G_TYPE_STRING, "utf8",
		NULL);
	g_object_set(G_OBJECT(_subtitleFilter), "caps", _subtitleFormat, NULL);
#pragma endregion

#pragma region sublitles for time
	//init video source
	_subtitleTimingSource = gst_element_factory_make("appsrc", "Timing");
	if (!_subtitleTimingSource)
	{
		throw 2;
	}
	g_object_set(G_OBJECT(_subtitleTimingSource), "stream-type", 0, "format", GST_FORMAT_TIME, NULL);

	//init video filter
	_subtitleTimingFilter = gst_element_factory_make("capsfilter", "TimingFilter");
	if (!_subtitleTimingFilter)
	{
		throw 2;
	}
	_subtitleTimingFormat = gst_caps_new_simple("text/x-raw",
		"format", G_TYPE_STRING, "utf8",
		NULL);
	g_object_set(G_OBJECT(_subtitleTimingFilter), "caps", _subtitleTimingFormat, NULL);
#pragma endregion

	//init video container
	_container = gst_element_factory_make("matroskamux", "Container"); //matroskamux avimux
	if (!_container)
	{
		throw 3;
	}

	//init file recording
	_recorder = gst_element_factory_make("filesink", "Recorder");
	if (!_recorder)
	{
		throw 4;
	}

	//build the pipeline
	gst_bin_add_many(GST_BIN(_pipeline), _videoSource, _videoFilter, _videoConverter, _subtitleSource, _subtitleFilter, _subtitleTimingSource, _subtitleTimingFilter, _container, _recorder, NULL);
	//gst_bin_add_many(GST_BIN(_pipeline), _videoSource, _videoFilter, _videoConverter, _subtitleSource, _subtitleFilter, _container, _recorder, NULL);

	////get container pads
	//GstPadTemplate* containerSubtitlePadTemplate = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(_container), "subtitle_%u");
	//GstPad* containerSubtitlePad = gst_element_request_pad(_container, containerSubtitlePadTemplate, NULL, NULL);
	//GstPad* containerSubtitleTimingPad = gst_element_request_pad(_container, containerSubtitlePadTemplate, NULL, NULL);
	//GstPad* subtitlesPad = gst_element_get_static_pad(data.video_queue, "src");

	//auto t = gst_pad_link(containerSubtitlePad, queue_audio_pad) != GST_PAD_LINK_OK

	//link video elemets
	if ((bool)gst_element_link(_videoSource, _videoFilter) != true) { throw 5; }
	if ((bool)gst_element_link(_videoFilter, _videoConverter) != true) { throw 5; }
	if ((bool)gst_element_link(_videoConverter, _container) != true) { throw 5; }
	//link subtitles elemets
	if ((bool)gst_element_link(_subtitleSource, _subtitleFilter) != true) { throw 5; }
	if ((bool)gst_element_link(_subtitleFilter, _container) != true) { throw 5; }
	//link timing subtitles elemets
	if ((bool)gst_element_link(_subtitleTimingSource, _subtitleTimingFilter) != true) { throw 5; }
	if ((bool)gst_element_link(_subtitleTimingFilter, _container) != true) { throw 5; }
	//link common elemets
	if ((bool)gst_element_link(_container, _recorder) != true) { throw 5; }

	//register call back function for soft stop
	bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline));
}

int GStreamer::BusCallback(GstBus* bus, GstMessage* message, void* loop)
{
	switch (GST_MESSAGE_TYPE(message))
	{
		case GST_MESSAGE_ERROR:
		break;

		case GST_MESSAGE_EOS:

			g_main_loop_quit((GMainLoop *)loop);

			return false;

		break;

		default:
		/* unhandled message */
		break;
	}

	///* we want to be notified again the next time there is a message
	//* on the bus, so returning TRUE (FALSE means we want to stop watching
	//* for messages on the bus and our callback should not be called again)
	//*/
	return TRUE;
}

#pragma region record video
void GStreamer::StartVideoFile(QString name, int width, int height)
{
	//store parameters
	_width = width;
	_height = height;

	//build the pipeline
//	gst_bin_add_many(GST_BIN(_pipeline), _videoSource, _videoFilter, _videoConverter, _container, _recorder, NULL);
	//gst_bin_add_many(GST_BIN(_pipeline), _videoSource, _videoFilter, _container, _recorder, NULL);

	//set file name
	g_object_set(_recorder, "location", name.toStdString().data(), NULL);

	//start recording
	GstStateChangeReturn status;
	status = gst_element_set_state(_pipeline, GST_STATE_PLAYING);
	if (status == GST_STATE_CHANGE_FAILURE)
	{
		throw 1;
	}
}

GstClockTime videoTimestamp = 0;
void GStreamer::AttachVideoFrame(unsigned char* frame)
{
	//validate environment
	if (_pipeline == nullptr) return;

	GstBuffer *buffer;
	GstFlowReturn ret;

	guint size = _width * _height * 3;

	buffer = gst_buffer_new_allocate(NULL, size, NULL);

	gst_buffer_fill(buffer,0 , frame, size);

	GST_BUFFER_PTS(buffer) = videoTimestamp;
	GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, 2);

	videoTimestamp += GST_BUFFER_DURATION(buffer);

	g_signal_emit_by_name(_videoSource, "push-buffer", buffer, &ret);
	gst_buffer_unref(buffer);

	if (ret != GST_FLOW_OK) {
		/* something wrong, stop pushing */
		g_main_loop_quit(loop);
	}
}

GstClockTime subtitlesTimestamp = 0;
int counter = 40;
void GStreamer::AttachVideoFrameData(unsigned char* frame)
{
	//validate environment
	if (_pipeline == nullptr) return;

	GstBuffer *buffer;
	GstFlowReturn ret;

	guint size = 100;

	buffer = gst_buffer_new_allocate(NULL, size, NULL);

	gst_buffer_fill(buffer, 0, frame, size);

	GST_BUFFER_PTS(buffer) = subtitlesTimestamp;
	GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, 2);

	subtitlesTimestamp += GST_BUFFER_DURATION(buffer);

	g_signal_emit_by_name(_subtitleSource, "push-buffer", buffer, &ret);

	counter++;
	frame[4] = counter;
	gst_buffer_fill(buffer, 0, frame, size);
	g_signal_emit_by_name(_subtitleTimingSource, "push-buffer", buffer, &ret);

	gst_buffer_unref(buffer);

	if (ret != GST_FLOW_OK) {
		/* something wrong, stop pushing */
		g_main_loop_quit(loop);
	}
}

void GStreamer::StopVideoFile()
{
	//send soft stop signal
	gst_element_send_event(_pipeline, gst_event_new_eos());

	gst_bus_add_watch(bus, BusCallback, loop);

	//activate main llop
	g_main_loop_run(loop);
	
	//stop recording
	gst_element_set_state(_pipeline, GST_STATE_NULL);
}
#pragma endregion