#include <QDebug>

#include <QtCore/QCoreApplication>

#include <gst/gst.h>
#include <gst/interfaces/tuner.h>
#include <gst/interfaces/propertyprobe.h>

GstElement *_pipeline, *_v4l2radio;

GstTunerChannel *dch;

GstBus* bus;

void print_channels(void* channel, gpointer)
{
	GstTunerChannel* ch=static_cast<GstTunerChannel*>(channel);
	qDebug()<<"***channel***";
	qDebug()<<"label:"<<ch->label;
	if (GST_TUNER_CHANNEL_HAS_FLAG(ch, GST_TUNER_CHANNEL_INPUT))
		qDebug()<<"flags: input";
	if (GST_TUNER_CHANNEL_HAS_FLAG(ch, GST_TUNER_CHANNEL_OUTPUT))
		qDebug()<<"flags: output";
	if (GST_TUNER_CHANNEL_HAS_FLAG(ch, GST_TUNER_CHANNEL_FREQUENCY))
		qDebug()<<"flags: frequency";
	if (GST_TUNER_CHANNEL_HAS_FLAG(ch, GST_TUNER_CHANNEL_AUDIO))
		qDebug()<<"flags: audio";
	qDebug()<<"freq multiplicator:"<<ch->freq_multiplicator;
	qDebug()<<"min frequency:"<<ch->min_frequency;
	qDebug()<<"max frequency:"<<ch->max_frequency;
	qDebug()<<"min signal:"<<ch->min_signal;
	qDebug()<<"max signal:"<<ch->max_signal;
}

void print_norms(void* norm, gpointer)
{
	GstTunerNorm* nm=static_cast<GstTunerNorm*>(norm);
	qDebug()<<nm->label;
}

void print_properties(void* property, gpointer)
{
	GParamSpec* prop = (GParamSpec*)property;
	qDebug()<<"***property***";
	qDebug()<<"name:"<<prop->name;
	qDebug()<<"flags:"<<prop->flags;
	qDebug()<<"value type:"<<g_type_name(prop->value_type);
	qDebug()<<"owner type:"<<g_type_name(prop->owner_type);
}

void cb_channel_changed(GstTuner *tuner, GstTunerChannel *channel, gpointer)
{
	qDebug()<<"channel changed";
	qDebug()<<"tuner:"<<tuner<<"v4l2radio:"<<_v4l2radio;
	print_channels(channel, NULL);
}

void cb_frequency_changed(GstTuner *tuner, GstTunerChannel *channel, gulong frequency, gpointer)
{
	qDebug()<<"frequency changed:"<<frequency;
	qDebug()<<"tuner:"<<tuner<<"v4l2radio:"<<_v4l2radio;
	print_channels(channel, NULL);
}

void cb_norm_changed(GstTuner *tuner, GstTunerNorm *norm, gpointer)
{
	qDebug()<<"norm changed";
	qDebug()<<"tuner:"<<tuner<<"v4l2radio:"<<_v4l2radio;
	qDebug()<<"norm:"<<norm;
}

void cb_signal_changed(GstTuner *tuner, GstTunerChannel *channel, gint signal, gpointer)
{
	qDebug()<<"signal changed:"<<signal;
	qDebug()<<"tuner:"<<tuner<<"v4l2radio:"<<_v4l2radio;
	print_channels(channel, NULL);
}

gboolean bus_call (GstBus *, GstMessage *msg, gpointer)
{
	qDebug()<<"timestamp:"<<GST_MESSAGE_TIMESTAMP(msg);
	switch (GST_MESSAGE_TYPE (msg))
	{

		case GST_MESSAGE_SEGMENT_START:
		{
				qDebug()<<"SEGMENT_START";
				break;
		}

		case GST_MESSAGE_ERROR:
		{
				gchar  *debug;
				GError *error;

				gst_message_parse_error (msg, &error, &debug);

				qDebug()<<"ERROR! "<<QString::fromLocal8Bit(error->message);
				qDebug()<<"DEBUG  "<<QString::fromLocal8Bit(debug);

				g_free (debug);
				g_error_free (error);
		}
		default:
			break;
	}
	return TRUE;
}

void create_pipeline_bin()
{
	_pipeline		= gst_pipeline_new("radiotest");

	_v4l2radio		= gst_element_factory_make("v4l2radio",  NULL);

	g_object_set(G_OBJECT (_v4l2radio), "device", "/dev/radio0", NULL);
	g_object_set(G_OBJECT (_v4l2radio), "frequency", 104200000, NULL);

	gst_bin_add(GST_BIN(_pipeline), _v4l2radio);

	bus = gst_pipeline_get_bus (GST_PIPELINE (_pipeline));
	gst_bus_add_watch (bus, bus_call, NULL);
	gst_object_unref (bus);

	gst_element_set_state (_pipeline, GST_STATE_PLAYING);
}

void property_test()
{
	qDebug()<<"-- property test --";
	gint freq = 0;
	gchar* device = NULL;
	g_object_get(G_OBJECT(_v4l2radio), "device", &device, "frequency", &freq, NULL);
	qDebug()<<"device:"<<device;
	qDebug()<<"frequency:"<<freq;
	g_free(device);
}

bool tuner_is_tuner_test()
{
	qDebug()<<"-- is tuner test --";
	bool b = GST_IS_TUNER(_v4l2radio);
	qDebug()<<"is tuner:"<<b;
	return b;
}

void tuner_channels_test()
{
	qDebug()<<"-- channels test --";
	qDebug()<<"***CHANNELS***";
	GList* channels = (GList*)gst_tuner_list_channels(GST_TUNER(_v4l2radio));
	g_list_foreach(channels, print_channels, 0);
	gchar* name = g_strdup("FM");
	GstTunerChannel* channel = gst_tuner_find_channel_by_name(GST_TUNER(_v4l2radio), name);
	qDebug()<<"found channel by name 'FM':"<<channel;
	if (channel)
	{
		qDebug()<<"will set new channel";
		gst_tuner_set_channel(GST_TUNER(_v4l2radio), channel);
		GstTunerChannel* tch = gst_tuner_get_channel(GST_TUNER(_v4l2radio));
		qDebug()<<"set/get channel success:"<<(channel == tch);
	}
}

void tuner_norms_test()
{
	qDebug()<<"-- norms test --";
	GList* norms = (GList*)gst_tuner_list_norms(GST_TUNER(_v4l2radio));
	qDebug()<<"norms found:"<<g_list_length(norms);
	g_list_foreach(norms, print_norms, 0);
	gchar* name = g_strdup("SECAM-D");
	GstTunerNorm* norm = gst_tuner_find_norm_by_name(GST_TUNER(_v4l2radio), name);
	qDebug()<<"norm found:"<<norm;
	bool b1 = (norm == NULL);
	norm = gst_tuner_get_norm(GST_TUNER(_v4l2radio));
	qDebug()<<"get norm:"<<norm;
	bool b2 = (norm == NULL);
	GstTunerChannel* ch = gst_tuner_get_channel(GST_TUNER(_v4l2radio));
	gst_tuner_set_norm(GST_TUNER(_v4l2radio), (GstTunerNorm*)ch);
	norm = gst_tuner_get_norm(GST_TUNER(_v4l2radio));
	qDebug()<<"get after set norm:"<<norm;
	bool b3 = (norm == NULL);
	qDebug()<<"norms ok:"<<(g_list_length(norms) == 0 && b1 && b2 && b3);
}

void tuner_frequency_test()
{
	qDebug()<<"-- frequency test --";
	GstTunerChannel* ch = gst_tuner_get_channel(GST_TUNER(_v4l2radio));
	gulong freq = gst_tuner_get_frequency(GST_TUNER(_v4l2radio), ch);
	qDebug()<<"initial frequency by GstTuner (should be 104200000):"<<freq;
	bool b1 = (freq == 104200000);
	freq = 101200000;
	gst_tuner_set_frequency(GST_TUNER(_v4l2radio), ch, freq);
	freq = gst_tuner_get_frequency(GST_TUNER(_v4l2radio), ch);
	qDebug()<<"new frequency by GstTuner (should be 101200000):"<<freq;
	bool b2 = (freq == 101200000);
	qDebug()<<"frequency success:"<<(b1 && b2);
}

void tuner_strength_test()
{
	qDebug()<<"-- signals test --";
	GstTunerChannel* ch = gst_tuner_get_channel(GST_TUNER(_v4l2radio));
	gint strength = gst_tuner_signal_strength(GST_TUNER(_v4l2radio), ch);
	qDebug()<<"signal strength:"<<strength;
}

void tuner_signals_test()
{
	g_signal_connect(G_OBJECT(_v4l2radio), "channel-changed", G_CALLBACK(cb_channel_changed), NULL);
	g_signal_connect(G_OBJECT(_v4l2radio), "frequency-changed", G_CALLBACK(cb_frequency_changed), NULL);
	g_signal_connect(G_OBJECT(_v4l2radio), "norm-changed", G_CALLBACK(cb_norm_changed), NULL);
	g_signal_connect(G_OBJECT(_v4l2radio), "signal-changed", G_CALLBACK(cb_signal_changed), NULL);
}

void tuner_test()
{
	qDebug()<<"-- tuner test --";
	if (tuner_is_tuner_test())
	{
		tuner_signals_test();
		tuner_channels_test();
		tuner_norms_test();
		tuner_frequency_test();
		tuner_strength_test();
	}
}

void property_probe_test()
{
	qDebug()<<"*** property probe test ***";
	GList* properties = (GList*)gst_property_probe_get_properties(GST_PROPERTY_PROBE(_v4l2radio));
	g_list_foreach(properties, print_properties, 0);
	qDebug()<<"list length:"<<g_list_length(properties);
	gchar* name = g_strdup("device");
	GParamSpec* property = (GParamSpec*)gst_property_probe_get_property(GST_PROPERTY_PROBE(_v4l2radio), name);
	qDebug()<<"property found:"<<property;
	print_properties(property, NULL);

	gboolean b = gst_property_probe_needs_probe(GST_PROPERTY_PROBE(_v4l2radio), property);
	qDebug()<<"property needs probe:"<<b;
	gst_property_probe_probe_property(GST_PROPERTY_PROBE(_v4l2radio), property);
	qDebug()<<"probed";
	gst_property_probe_probe_property_name(GST_PROPERTY_PROBE(_v4l2radio), name);
	qDebug()<<"probed";
	GValueArray* values = gst_property_probe_get_values(GST_PROPERTY_PROBE(_v4l2radio), property);
	if (values)
	{
		qDebug()<<"number of values:"<<values->n_values;
		for (size_t i = 0; i < values->n_values; ++i)
			qDebug()<<"value"<<i<<":"<<g_strdup_value_contents(g_value_array_get_nth(values, i));
	}
	else
		qDebug()<<"number of values:"<<0;
	values = gst_property_probe_get_values_name(GST_PROPERTY_PROBE(_v4l2radio), name);
	if (values)
	{
		qDebug()<<"number of values by name:"<<values->n_values;
		for (size_t i = 0; i < values->n_values; ++i)
			qDebug()<<"value"<<i<<":"<<g_strdup_value_contents(g_value_array_get_nth(values, i));
	}
	else
		qDebug()<<"number of values by name:"<<0;

}

void uri_test()
{
	qDebug()<<"*** URI test ***";
	GstURIHandler* handler = GST_URI_HANDLER(_v4l2radio);
	GstURIType type = (GstURIType)gst_uri_handler_get_uri_type(handler);
	if (type == GST_URI_UNKNOWN)
		qDebug()<<"type: UNKNOWN";
	if (type == GST_URI_SINK)
		qDebug()<<"type: SINK";
	if (type == GST_URI_SRC)
		qDebug()<<"type: SRC";
	gchar** protocols = gst_uri_handler_get_protocols(handler);
	qDebug()<<"protocols:";

	for (size_t i = 0; i < g_strv_length(protocols); ++i)
	{
		qDebug()<<"\t"<<protocols[i];
		protocols += strlen(protocols[i]);
	}
	gchar* uri = (gchar*)gst_uri_handler_get_uri(handler);
	qDebug()<<"uri:"<<uri;
	uri = g_strdup("radio://104.2");
	qDebug()<<"setting uri:"<<uri;
	gboolean b = gst_uri_handler_set_uri(handler, uri);
	qDebug()<<"set uri successfully:"<<b;
	uri = NULL;
	uri = (gchar*)gst_uri_handler_get_uri(handler);
	qDebug()<<"new uri:"<<uri;
	uri = g_strdup("v4l2:///dev/video0");
	qDebug()<<"setting uri:"<<uri;
	b = gst_uri_handler_set_uri(handler, uri);
	qDebug()<<"set uri successfully:"<<b;
	uri = NULL;
	uri = (gchar*)gst_uri_handler_get_uri(handler);
	qDebug()<<"new uri:"<<uri;
}

gboolean test_callback(gpointer)
{
	qDebug()<<"callback called";
	property_test();
	tuner_test();
	property_probe_test();
	sleep(1);
	uri_test();
	return FALSE;
}

gboolean quit_callback(gpointer)
{
	qDebug()<<"quit callback called";
//	connect_remote_client();
	gst_element_set_state(_pipeline, GST_STATE_NULL);
	return FALSE;
}

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

	gst_debug_set_active(true);
	gst_debug_set_default_threshold(GST_LEVEL_WARNING);

	create_pipeline_bin();

	g_timeout_add(7000, test_callback, NULL);
	g_timeout_add(15000, quit_callback, NULL);

	return a.exec();
}
