#include <stdlib.h>
#include <gst/gst.h>
#include <string.h>

static void
get_position_info (GstElement *cdparanoia)
{
  GstFormat track_format;
  const GstFormat *formats;
  GstPad *pad;

  track_format = gst_format_get_by_nick ("track");
  g_assert (track_format != 0);

  pad = gst_element_get_pad (cdparanoia, "src");
  formats = gst_pad_get_formats (pad);

  while (*formats) {
    const GstFormatDefinition *definition;
    GstFormat format;
    gint64 position;
    gboolean res;

    definition = gst_format_get_details (*formats);

    format = *formats;
    res = gst_pad_query (pad, GST_QUERY_POSITION,
		         &format, &position);

    if (format == GST_FORMAT_TIME) {
      position /= GST_SECOND;
      g_print ("%s: %lld:%02lld", definition->nick, position/60, position%60);
    }
    else {
      g_print ("%s: %lld", definition->nick, position);
    }

    formats++;
    if (*formats) {
      g_print (", ");
    }
  }
  g_print ("\r");
}

static void
get_track_info (GstElement *cdparanoia)
{
  GstFormat track_format;
  gint64 total_tracks = 0, total_time = 0;
  GstPad *pad;
  const GstFormat *formats;
  gint i;
  gint64 time_count = 0;
  
  track_format = gst_format_get_by_nick ("track");
  g_assert (track_format != 0);

  pad = gst_element_get_pad (cdparanoia, "src");
  formats = gst_pad_get_formats (pad);

  /* we loop over all supported formats and report the total
   * number of them */
  while (*formats) {
    const GstFormatDefinition *definition;
    gint64 total;
    GstFormat format;
    gboolean res;
    
    definition = gst_format_get_details (*formats);

    format = *formats;
    res = gst_pad_query (pad, GST_QUERY_TOTAL,
		         &format, &total);
    if (res) {
      if (format == GST_FORMAT_TIME) {
	total /= GST_SECOND;
        g_print ("%s total: %lld:%02lld\n", definition->nick, total/60, total%60);
      }
      else
        g_print ("%s total: %lld\n", definition->nick, total);

      if (format == track_format)
	total_tracks = total;
      else if (format == GST_FORMAT_TIME)
	total_time = total;
    }
    else
      g_print ("failed to get %s total\n", definition->nick);

    formats++;
  }

  /* then we loop over all the tracks to get more info.
   * since pad_convert always works from 0, the time from track 1 needs
   * to be substracted from track 2 */
  for (i = 0; i <= total_tracks; i++) {
    gint64 time;
    gboolean res;

    if (i < total_tracks) {
      GstFormat format;

      format = GST_FORMAT_TIME;
      res = gst_pad_convert (pad, track_format, i,
		             &format, &time);
      time /= GST_SECOND;
    }
    else {
      time = total_time;
      res = TRUE;
    }

    if (res) {
      /* for the first track (i==0) we wait until we have the
       * time of the next track */
      if (i > 0) {
	gint64 length = time - time_count;

        g_print ("track %d: %lld:%02lld -> %lld:%02lld, length: %lld:%02lld\n",
		 i-1,
		 time_count / 60, time_count % 60,
		 time / 60, time % 60,
		 length / 60, length % 60);
      }
    }
    else {
      g_print ("could not get time for track %d\n", i);
    }

    time_count = time;
  }
}

int
main (int argc, char **argv)
{
  GstElement *pipeline;
  GstElement *cdparanoia;
  GstElement *osssink;
  GstPad *pad;
  GstFormat track_format;
  GstEvent *event;
  gint count;
  gboolean res;

  gst_init (&argc, &argv);

  pipeline = gst_pipeline_new ("pipeline");

  cdparanoia = gst_element_factory_make ("cdparanoia", "cdparanoia");
  g_assert (cdparanoia);
  g_object_set (G_OBJECT (cdparanoia), "paranoia_mode", 0, NULL);

  osssink = gst_element_factory_make ("osssink", "osssink");
  g_assert (osssink);

  gst_bin_add (GST_BIN (pipeline), cdparanoia);
  gst_bin_add (GST_BIN (pipeline), osssink);

  gst_element_link_pads (cdparanoia, "src", osssink, "sink");

  g_signal_connect (G_OBJECT (pipeline), "deep_notify",
		  G_CALLBACK (gst_element_default_deep_notify), NULL);

  gst_element_set_state (pipeline, GST_STATE_PAUSED);

  /* now we go into probe mode */
  get_track_info (cdparanoia);

  track_format = gst_format_get_by_nick ("track");
  g_assert (track_format != 0);

  pad = gst_element_get_pad (cdparanoia, "src");
  g_assert (pad);

  g_print ("playing from track 3\n");
  /* seek to track3 */
  event = gst_event_new_seek (track_format |
		              GST_SEEK_METHOD_SET |
			      GST_SEEK_FLAG_FLUSH,
			      3);

  res = gst_pad_send_event (pad, event);
  if (!res)
    g_warning ("seek failed");

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  count = 0;
  while (gst_bin_iterate (GST_BIN (pipeline))) {
    get_position_info (cdparanoia);
    if (count++ > 500)
      break;
  }
  gst_element_set_state (pipeline, GST_STATE_PAUSED);

  g_print ("\nplaying from second 25 to second 29\n");
  /* seek to some seconds */
  event = gst_event_new_segment_seek (GST_FORMAT_TIME |
		                      GST_SEEK_METHOD_SET |
			              GST_SEEK_FLAG_FLUSH,
			              25 * GST_SECOND, 29 * GST_SECOND);
  res = gst_pad_send_event (pad, event);
  if (!res)
    g_warning ("seek failed");

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  while (gst_bin_iterate (GST_BIN (pipeline))) {
    get_position_info (cdparanoia);
  }
  g_print ("\n");

  /* shutdown everything again */
  gst_element_set_state (pipeline, GST_STATE_NULL);

  return 0;
}