/*
 * gsttest.c
 *
 * Compile with:
 * libtool --mode=link gcc -g3 -O0 -o gsttest -Wall `pkg-config --cflags --libs gstreamer-0.10` gsttest.c
 *
 * [run-standalone.sh] ./gsttest file:///path/to/the/wav.file iterations timeout_to_abort_an_iteration | tee /tmp/log.txt
 */

#include <glib-object.h>
#include <gst/gst.h>
/* #include <gst/interfaces/xoverlay.h> */
#include <signal.h>

static GstElement *pipeline = NULL;
static const gchar *uri;
guint bus_id;
static GMainLoop *loop = NULL;
static gboolean success = FALSE;
static GstElement *asink = NULL;
static GstElement *vsink = NULL;
GstState current_state = GST_STATE_NULL, new_state = GST_STATE_NULL;
guint failure_timeout;
guint runs = 0;
guint total_runs;
guint out_timeout_id;

static gboolean _build_pipeline(gpointer user_data);

static void _set_pipeline_state(GstState state)
{
	new_state = state;
	gst_element_set_state(pipeline, state);
}

static void _set_volume(void)
{
	g_return_if_fail(pipeline != NULL);

	g_debug("setting volume %1.1f", 1.0);
	g_object_set(pipeline, "volume", 1.0, NULL);
}

static void _set_mute(void)
{
	gboolean mute = TRUE;

	g_return_if_fail(pipeline != NULL);

	g_debug("setting mute: %s", mute ? "TRUE" : "FALSE");
	g_object_set(pipeline, "mute", mute, NULL);
}

gboolean _do_exit(gpointer user_data)
{
	g_debug("Test exitting");
	g_main_loop_quit (loop);
        return FALSE;
}

static gboolean _do_cleaning_and_run_again(gpointer user_data)
{
	if (++runs < total_runs) {
		g_debug("Cleaning up");
		_set_pipeline_state(GST_STATE_NULL);
		g_source_remove(bus_id);
		gst_object_unref(pipeline);
		pipeline = NULL;
		success = FALSE;
		_build_pipeline(NULL);
	} else {
		_do_exit(NULL);
	}

	return FALSE;
}

static gboolean _out_timeout(gpointer user_data) {
	g_critical("FAILURE: timeout (%s->%s)",
		   gst_element_state_get_name(current_state),
		   gst_element_state_get_name(new_state));
	_do_cleaning_and_run_again(NULL);
	out_timeout_id = 0;
	return FALSE;
}

static gboolean _do_play(gpointer user_data)
{
	g_return_val_if_fail (pipeline != NULL, FALSE);

	_set_volume();
	_set_mute();
	_set_pipeline_state(GST_STATE_PLAYING);
	g_debug("setting pipeline to PLAYING");

	return FALSE;
}

static void _handle_state_changed(GstMessage *msg)
{
	GstState old_state;

	gst_message_parse_state_changed(msg, &old_state, &current_state, NULL);
	g_debug("pipeline changed state from %s to %s",
		gst_element_state_get_name(old_state),
		gst_element_state_get_name(current_state));

	if (out_timeout_id != 0) {
		g_source_remove(out_timeout_id);
		out_timeout_id = 0;
	}

	switch (GST_STATE_TRANSITION(old_state, current_state)) {
	case GST_STATE_CHANGE_READY_TO_PAUSED:
		g_debug("Test changed to PAUSED");
		_do_play(NULL);
		break;
	case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
		g_debug("Test changed to PLAYING");
		success = TRUE;
		break;
	case GST_STATE_CHANGE_READY_TO_NULL:
		g_debug("Test changed to NULL");
		break;
	default:
		break;
	}

	out_timeout_id = g_timeout_add_seconds(failure_timeout, _out_timeout,
					       NULL);
}

static gboolean _async_bus_handler(GstBus *bus, GstMessage *msg,
				   gpointer user_data)
{
	switch (GST_MESSAGE_TYPE(msg)) {
	case GST_MESSAGE_ERROR:
		{
			gchar *debug;
			GError *err;

			debug = NULL;
			gst_message_parse_error(msg, &err, &debug);
			g_error("gst error: domain = %d, code = %d, "
				"message = '%s', debug = '%s'",
				err->domain, err->code, err->message, debug);
		}
		break;
	case GST_MESSAGE_STATE_CHANGED:
		if ((GstElement *)GST_MESSAGE_SRC(msg) == pipeline)
			_handle_state_changed(msg);
		break;
	case GST_MESSAGE_EOS:
		if (success) {
			g_debug("Success");
		} else {
			g_critical("FAILURE: EOS");
		}

		if (out_timeout_id != 0) {
			g_source_remove(out_timeout_id);
			out_timeout_id = 0;
		}

		g_idle_add(_do_cleaning_and_run_again, NULL);
		break;
	default:
		break;
	}
	return TRUE;
}

static gboolean _build_pipeline(gpointer user_data)
{
	GstBus *bus;

	pipeline = gst_element_factory_make("playbin2",
					    "playbin");

	g_assert(pipeline != NULL);

	if (asink == NULL) {
		asink = gst_element_factory_make("fakesink",
						 "audio-sink");
	}
	if (vsink == NULL) {
		vsink = gst_element_factory_make("fakesink",
						 "video-sink");
	}
	g_object_set(pipeline, "audio-sink", g_object_ref(asink), NULL);
	g_object_set(pipeline, "video-sink", g_object_ref(vsink), NULL);

	g_object_set(G_OBJECT(pipeline), "uri", uri, NULL);

	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
	
	bus_id = gst_bus_add_watch(bus, _async_bus_handler, NULL);

	g_debug("Pipeline built, setting to PAUSED");
	_set_pipeline_state(GST_STATE_PAUSED);

	return FALSE;
}

int main(int argc, char *argv[])
{
	g_assert(argc > 3);

	g_type_init();
	g_thread_init(NULL);
	gst_init(NULL, NULL);

	uri = argv[1];
	total_runs = atoi(argv[2]);
	failure_timeout = atoi(argv[3]);

	g_debug("INIT");

        loop = g_main_loop_new(NULL, FALSE);

	g_idle_add(_build_pipeline, NULL);

	g_main_loop_run(loop);

	g_debug("END");

	return 0;
}
