#include <stdio.h>
#include <glibmm/main.h>
#include <gstreamermm.h>
#include <gstreamermm/appsink.h>
#include <gstreamermm/uridecodebin.h>
#include <gst/net/gstnetaddressmeta.h>
#include <gio/gio.h>
#include <gst/sdp/gstsdpmessage.h>

#include <gstreamermm.h>
#include <iostream>
#include <glibmm.h>

#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>

using namespace Gst;
using Glib::RefPtr;

class AllMediaPlayer
{
private:
  RefPtr<Glib::MainLoop> main_loop;
  RefPtr<Pipeline> pipeline;
  RefPtr<Gst::UriDecodeBin> uridecodebin;

  void init(const std::string& filename)
  {
	uridecodebin = Gst::UriDecodeBin::create();
	pipeline->add(uridecodebin);
	uridecodebin->property_uri() = filename;
	uridecodebin->signal_autoplug_continue().connect(sigc::mem_fun(*this, &AllMediaPlayer::AutoplugContinue));
	uridecodebin->signal_pad_added().connect(sigc::mem_fun(*this, &AllMediaPlayer::PadAdded));
	uridecodebin->signal_no_more_pads().connect(sigc::mem_fun(*this, &AllMediaPlayer::NoMorePads));
	uridecodebin->signal_autoplug_select().connect(sigc::mem_fun(*this, &AllMediaPlayer::AutoplugSelect));
  }

public:
	AllMediaPlayer()
	{
		main_loop = Glib::MainLoop::create();
		pipeline = Pipeline::create();
	}

	void play_until_eos(const std::string& filename)
	{
		// Set priority not as high.
		int lowest = sched_get_priority_min(SCHED_FIFO);
		
		sched_param schedParam;
		memset(&schedParam, 0, sizeof(schedParam));
		schedParam.sched_priority = lowest;
		pthread_setschedparam(pthread_self(), SCHED_FIFO, &schedParam);

		init(filename);
		pipeline->set_state(STATE_PLAYING);
		main_loop->run();
		pipeline->set_state(STATE_NULL);
	}

	bool AutoplugContinue(RefPtr<Gst::Pad> pad, RefPtr<Gst::Caps> caps)
	{
		std::string caps_string = caps->to_string();
		printf("Gstreamer::AutoplugContinue: pad %s caps \"%s\"\n", pad->get_name().c_str(), caps_string.c_str());
		return true;
	}

	Gst::AutoplugSelectResult AutoplugSelect(RefPtr<Gst::Pad> pad, RefPtr<Gst::Caps> caps, RefPtr<Gst::ElementFactory> element_factory)
	{
		// Don't really need this, but the debug can be nice.
		printf("Pad %s try %s\n", pad->get_name().c_str(), element_factory->get_name().c_str());
		return Gst::AUTOPLUG_SELECT_TRY;
	}

	void PadAdded(RefPtr<Gst::Pad> pad)
	{
		printf("Gstreamer::PadAdded: pad %s\n", pad->get_name().c_str());
	}

	void NoMorePads()
	{
		printf("NoMorePads()\n");
	}
	
	void Stop()
	{
		pipeline->set_state(Gst::STATE_NULL);
		main_loop->quit();
	}

};

int main()
{
	// Set priority high.
	int highest = sched_get_priority_max(SCHED_FIFO);

	sched_param schedParam;
	memset(&schedParam, 0, sizeof(schedParam));
	schedParam.sched_priority = highest;
	pthread_setschedparam(pthread_self(), SCHED_FIFO, &schedParam);
	
	printf("Highest priority %d\n", highest);

	Gst::init();

	// Seems to generally assert when we get to ~300ms on my hardware. Note that this is with a 100ms sleep inserted into
	// gst_base_parse_push_frame after lines which test the caps and jump to no_caps.
	for(int ms = 150; ms < 400; ++ms)
	{
		AllMediaPlayer player;
		boost::thread thread;
		printf("Spawning player...\n");
		
		// planes720p.mp4 is a 720p H.264 video with stereo AAC audio.
		thread = boost::thread(boost::bind(&AllMediaPlayer::play_until_eos, player, "http://server/~user/planes720p.mp4"));
		printf("Sleep %dms\n", ms);
		usleep(ms*1000);
		printf("Wake and stop\n");
		player.Stop();
		printf("Join\n");
		thread.join();
		printf("And repeat..\n");
	}

	return 0;
}
