#include <assert.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

#include <gst/gst.h>
#include <glib.h>



typedef struct PipelinePlayerCtx {
  GMainLoop  *loop;
  GstElement *pipeline;
  gboolean    restart;
} PipelinePlayerCtx;


static gboolean caught_intr = FALSE;
static gboolean caught_hup  = FALSE;


/* restore default interrupt handler after
   we handled the interrupt */
static void
sigint_restore (void)
{
  struct sigaction action;
  memset (&action, 0, sizeof (action));
  action.sa_handler = SIG_DFL;
  sigaction (SIGINT, &action, NULL);
}


/* signal handler: set a flag which is
 * being checked in the mainloop
 */
static void
sighandler (int signum)
{
	switch (signum) {
	case SIGINT:
		sigint_restore ();
		caught_intr = TRUE;
		break;
	case SIGHUP:
		caught_hup = TRUE;
		break;
	}
}


static void
sign_setup (void)
{
  struct sigaction action;
  memset (&action, 0, sizeof (action));
  action.sa_handler = sighandler;
  sigaction (SIGINT, &action, NULL);
  sigaction (SIGHUP, &action, NULL);
}


/* called every 50 milliseconds (20 times a second), the interrupt handler
 * will set a flag for us. We react to this by posting a message. */
static gboolean
check_signal (PipelinePlayerCtx *ctx)
{
  if (caught_intr) {
		caught_intr = FALSE;
		g_print( "caught interrupt\n");
		
		/* notify application */
		ctx->restart = FALSE;
		gst_element_post_message (ctx->pipeline,
															gst_message_new_application (GST_OBJECT (ctx->pipeline),
																													 gst_structure_empty_new ("Quit")));
		/* remove timeout handler */
		return FALSE;
	}

	if (caught_hup) {
		caught_hup = FALSE;
		ctx->restart = TRUE;
		gst_element_post_message (ctx->pipeline,
															gst_message_new_application (GST_OBJECT (ctx->pipeline),
																													 gst_structure_empty_new ("Restart")));
	}
	return TRUE;
}


static gboolean
bus_call (GstBus     *bus,
					GstMessage *msg,
					gpointer    data)
{
	PipelinePlayerCtx *ctx = (PipelinePlayerCtx*) data;
	GMainLoop *loop = ctx->loop;

	switch (GST_MESSAGE_TYPE (msg)) {
    
	case GST_MESSAGE_APPLICATION:
		{
			const GstStructure *s = gst_message_get_structure (msg);
			if (gst_structure_has_name (s, "Quit")) {
				/* this application message is posted when we caught INT signal and
				 * we need to quit. */
        g_print ("Quit\n");
        g_main_loop_quit (loop);
      } else if (gst_structure_has_name (s, "Restart")) {
				/* this application message is posted when we caught HUP signal and
				 * we need to restart the pipeline. */
        g_print ("Sending EOS\n");
				gst_element_send_event (ctx->pipeline, gst_event_new_eos ());
      } else {
        g_printerr ("unknown application message %s.\n", gst_structure_get_name (s));
			}
		}
		break;

  case GST_MESSAGE_EOS:
    g_print ("End of stream, restart: %u\n", ctx->restart);
		g_main_loop_quit (loop);
		break;
		
	case GST_MESSAGE_ERROR: {
		gchar  *debug;
		GError *error;
    
		gst_message_parse_error (msg, &error, &debug);
		g_free (debug);
    
		g_printerr ("Error: %s\n", error->message);
		g_error_free (error);
    
		g_main_loop_quit (loop);
		break;
	}
	default: 
		g_print ("ignoring %s message\n",
						 gst_message_get_structure (msg) ?
						   gst_structure_get_name (gst_message_get_structure (msg)) :
						   "anonymous");
	}
  
	return TRUE;
}

 
int
main (int   argc,
			char *argv[])
{
	guint check_signal_id = 0;
	GError *err = NULL;
	GMainLoop *loop;

	GstElement *mainpipeline;
	GstBus *bus;
	const gchar *pipeline_description = "empty pipeline";
	PipelinePlayerCtx  ctx = {NULL, NULL, FALSE};
  
	gst_init (&argc, &argv);
	switch (argc) {
	case 2:
		pipeline_description = argv[1];
		break;
	default:
		g_printerr ("Usage: %s <pipeline-description>\n", argv[0]);
		return 1;
	}

	loop = g_main_loop_new (NULL, FALSE);

	mainpipeline = gst_parse_launch_full (pipeline_description,
																				NULL /* context */,
																				GST_PARSE_FLAG_FATAL_ERRORS,
																				&err);
	if (!mainpipeline) {
		g_printerr ("Failed to parse the pipeline: %s\n", err->message);
		g_error_free(err);
		err = NULL;
		return 2;
	}

  /* add a message handler */
  bus = gst_pipeline_get_bus (GST_PIPELINE (mainpipeline));
  ctx.loop     = loop;
  ctx.pipeline = mainpipeline;
  gst_bus_add_watch (bus, bus_call, &ctx);
  gst_object_unref (bus);

	sign_setup ();
	check_signal_id = g_timeout_add (50, (GSourceFunc) check_signal, &ctx);
	do {
		/* Set the pipeline to "playing" state*/
		if (gst_element_set_state (mainpipeline, GST_STATE_PLAYING) ==
				GST_STATE_CHANGE_FAILURE) {
			g_printerr ("Cannot play. Exiting.\n");
			return 3;
		}
		g_print ("%d playing: %s\n", getpid (), pipeline_description);
      
		/* Iterate */
		ctx.restart = FALSE;
		g_main_loop_run (loop);
    
		/* Out of the main loop, clean up nicely */
		g_print ("Returned, stopping playback\n");
		gst_element_set_state (mainpipeline, GST_STATE_NULL);
	} while (ctx.restart);
	
	g_source_remove(check_signal_id);
	sigint_restore ();

	g_print ("Deleting pipeline\n");
	gst_object_unref (GST_OBJECT (mainpipeline));

	gst_deinit();
  
	return 0;
}
