/* gcc -o test -Wall -Werror `pkg-config --libs --cflags gstreamer-0.10` main.c */
/* gcc -o test -Wall -Werror `pkg-config --libs --cflags gstreamer-1.0` main.c -DGSTREAMER_1_0 */
/* ./test <uri> */

#include <gst/gst.h>

static gboolean async_msg_handler(GstBus *bus, GstMessage *msg, gpointer data);
static gboolean start_playback(gpointer data);
static gint64 get_duration(void);
static gboolean is_seekable(void);
static gint64 get_position(void);
static void seek(gint64 position);

static GstElement *pipeline;
static GstState gst_state = GST_STATE_NULL;
static gint64 duration = -1;

#undef  G_LOG_DOMAIN
#define G_LOG_DOMAIN "main-test"

#ifndef GSTREAMER_1_0
#define PIPELINE "playbin2"
#else
#define PIPELINE "playbin"
#endif

int
main(int argc, char *argv [])
{
  if (argc != 2)
    {
      g_printerr("Usage: test <uri>\n");
      return -1;
    }

  g_debug("initializing");

  gst_init(&argc, &argv);

  pipeline = gst_element_factory_make(PIPELINE, "playbin");
  if (!pipeline)
    {
      g_critical("could not create " PIPELINE " pipeline");
      return -1;
    }

  g_object_set(pipeline, "uri", argv[1], NULL);

  GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
  gst_bus_add_watch_full(bus, G_PRIORITY_HIGH, (GstBusFunc) async_msg_handler,
                         NULL, NULL);

  g_idle_add(start_playback, NULL);

  g_main_loop_run(g_main_loop_new (NULL, FALSE));

  return 0;
}

static gint64
get_duration(void)
{
  gboolean right;
  GstFormat format = GST_FORMAT_TIME;

  duration = -1;

#ifndef GSTREAMER_1_0
  right = gst_element_query_duration(pipeline, &format, &duration);
#else
  right = gst_element_query_duration(pipeline, format, &duration);
#endif

  if (right && duration > 0)
    {
      g_debug("duration: %" G_GINT64_FORMAT, duration);
    }

  return duration;
}

static gboolean
is_seekable(void)
{
  GstFormat format = GST_FORMAT_TIME;
  gboolean seekable;
  GstQuery *seek_query;

  if (duration < 0)
    {
      return FALSE;
    }

  seek_query = gst_query_new_seeking(format);
  if (gst_element_query(pipeline, seek_query))
    {
      gst_query_parse_seeking(seek_query, NULL, &seekable, NULL, NULL);
    }

  gst_query_unref(seek_query);

  return seekable;
}

static void
seek(gint64 position)
{
  gint64 duration;
  gboolean seekable;

  g_return_if_fail(position >= 0);

  duration = get_duration();
  seekable = is_seekable();

  if (duration > 0 && seekable)
    {
      g_debug("seeking to %" G_GINT64_FORMAT, position);
      if (!gst_element_seek(pipeline, 1, GST_FORMAT_TIME,
                            GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
                            GST_SEEK_TYPE_SET, position,
                            GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
        {
          g_warning("could not seek");
        }
    }
  else
    {
      g_warning("media is not seekable");
    }
}

static gint64
get_position(void)
{
  GstFormat format = GST_FORMAT_TIME;
  gint64 position_query, position = -1;

#ifndef GSTREAMER_1_0
  if (gst_element_query_position(pipeline, &format, &position_query))
#else
  if (gst_element_query_position(pipeline, format, &position_query))
#endif
    {
      position = position_query;
    }

  return position;
}

static gboolean
async_msg_handler(GstBus *bus, GstMessage *message, gpointer data)
{
  GstState old_state, new_state, pending_state;
  GstStateChange state_transition;
  static gboolean playing = FALSE;

  switch (GST_MESSAGE_TYPE (message))
    {
    case GST_MESSAGE_STATE_CHANGED:

      gst_message_parse_state_changed(message, &old_state, &new_state,
                                      &pending_state);
      if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != pipeline)
        {
          return TRUE;
        }
      gst_state = new_state;
      g_debug("%s: %s->%s=>%s",
              gst_element_get_name(GST_ELEMENT(GST_MESSAGE_SRC(message))),
              gst_element_state_get_name (old_state),
              gst_element_state_get_name (new_state),
              gst_element_state_get_name (pending_state));

    if (old_state == GST_STATE_PAUSED && new_state == GST_STATE_PAUSED && pending_state == GST_STATE_VOID_PENDING)
      {
        g_debug("seeking finished, position: %" G_GINT64_FORMAT, get_position());
        return TRUE;
      }

      state_transition = GST_STATE_TRANSITION(old_state, gst_state);
      switch (state_transition)
        {
        case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
          g_debug("paused, seeking");
          seek(get_duration() + GST_USECOND);
          break;
        case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
          g_debug("playing, position: %" G_GINT64_FORMAT, get_position());
          if (!playing)
            {
              playing = TRUE;
              g_debug("pausing");
              gst_element_set_state(pipeline, GST_STATE_PAUSED);
            }
          break;
        default:
          break;
        }

      break;

    case GST_MESSAGE_ERROR:
      g_error("GST_MESSAGE_ERROR");
      break;

    default:
      break;
    }

  return TRUE;
}

static gboolean
start_playback(gpointer data)
{
  gst_element_set_state(pipeline, GST_STATE_PLAYING);

  return FALSE;
}
