#include <gst/gst.h>
#include <gst/controller/gstcontroller.h>
#include <gst/controller/gstinterpolationcontrolsource.h>
#include <string.h>
#include <time.h>
#include <math.h>

static void
on_message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
{
  GMainLoop *loop = user_data;

  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:
    case GST_MESSAGE_WARNING:
      g_assert_not_reached ();
      break;
    case GST_MESSAGE_EOS:
      g_main_loop_quit (loop);
      break;
    default:
      break;
  }
}

static clock_t
run_benchmark_single (void)
{
  GstElement *pipeline = gst_parse_launch ("audiotestsrc wave=sine-table samplesperbuffer=441 num-buffers=40000 ! volume name=vol ! fakesink silent=true", NULL);
  GstBus *bus;
  GMainLoop *loop;
  GstFlowReturn ret;
  GstElement *volume;
  GstController *controller;
  GstInterpolationControlSource *csource;
  clock_t start, end;
  GValue val = {0, };

  g_assert (pipeline != NULL);

  volume = gst_bin_get_by_name (GST_BIN (pipeline), "vol");
  controller = gst_object_control_properties (G_OBJECT (volume), "volume", NULL);
  csource = gst_interpolation_control_source_new ();
  gst_object_set_control_source (G_OBJECT (volume), "volume", GST_CONTROL_SOURCE (csource));

  g_value_init (&val, G_TYPE_DOUBLE);
  g_value_set_double (&val, 0.0);
  gst_interpolation_control_source_set (csource, 0 * GST_SECOND, &val);
  g_value_set_double (&val, 1.0);
  gst_interpolation_control_source_set (csource, 3 * GST_SECOND, &val);
  g_value_set_double (&val, 0.5);
  gst_interpolation_control_source_set (csource, 5 * GST_SECOND, &val);
  g_value_set_double (&val, 1.5);
  gst_interpolation_control_source_set (csource, 8 * GST_SECOND, &val);
  g_value_set_double (&val, 1.0);
  gst_interpolation_control_source_set (csource, 9 * GST_SECOND, &val);
  g_value_unset (&val);

  loop = g_main_loop_new (NULL, FALSE);

  bus = gst_element_get_bus (pipeline);
  g_assert (bus != NULL);
  gst_bus_add_signal_watch (bus);
  g_signal_connect (bus, "message", (GCallback) on_message_cb, loop);
  gst_object_unref (bus);

  ret = gst_element_set_state (pipeline, GST_STATE_READY);
  g_assert (ret == GST_STATE_CHANGE_SUCCESS || ret == GST_STATE_CHANGE_ASYNC);

  start = clock ();
  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
  g_assert (ret == GST_STATE_CHANGE_SUCCESS || ret == GST_STATE_CHANGE_ASYNC);

  g_main_loop_run (loop);
  end = clock ();

  ret = gst_element_set_state (pipeline, GST_STATE_NULL);
  g_assert (ret == GST_STATE_CHANGE_SUCCESS);

  gst_object_unref (pipeline);
  g_main_loop_unref (loop);

  return end - start;
}

static void
run_benchmark (guint nruns)
{
  guint i;
  gdouble avg, std;
  clock_t times[nruns];

  memset (times, 0, sizeof (clock_t) * nruns);

#if 0
retry:
#endif
  for (i = 0; i < nruns; i++)
    times[i] = run_benchmark_single ();

  avg = std = 0.0;
  for (i = 0; i < nruns; i++)
    avg += times[i];
  avg /= nruns;
  avg /= CLOCKS_PER_SEC;

  for (i = 0; i < nruns; i++) {
    gdouble val = avg - (((gdouble) times[i]) / ((gdouble) CLOCKS_PER_SEC));
    std += val * val;
  }
  std /= nruns;
  std = sqrt (std);

#if 0
  if (std > 1.0 && std > avg * 0.05) {
    g_printerr ("std too large: %lf\n", std);
    goto retry;
  }
#endif

  g_print ("%lf # std=%lf\n", avg, std);
}

int
main (int argc, char **argv)
{
  gst_init (&argc, &argv);

  run_benchmark (5);

  return 0;
}
