#include <gst/gst.h>
#include <stdbool.h>
#include <unistd.h>
#include <vector>

gboolean bus_call(GstBus *bus, GstMessage *msg, GMainLoop *loop) {
    switch (GST_MESSAGE_TYPE(msg)) {
    case GST_MESSAGE_EOS:
        g_message("End-of-stream");
        g_main_loop_quit(loop);
        break;
    case GST_MESSAGE_ERROR: {
        GError *err;
        gst_message_parse_error(msg, &err, NULL);
        g_error("%s", err->message);
        g_error_free(err);

        g_main_loop_quit(loop);
        break;
    }
    }

    return true;
}

GstPadProbeReturn new_buffer(GstPad* pad, GstPadProbeInfo* info, gpointer user_data)
{
    g_print("New buffer!\n");
    GstBuffer* buffer = gst_pad_probe_info_get_buffer(info);
    
    GstMemory* memory = gst_buffer_get_all_memory(buffer);
    GstMapInfo mapInfo;
    if (!gst_memory_map(memory, &mapInfo, GST_MAP_READ)) {
        g_print("Unable to memory map cue data.\n");
        return GST_PAD_PROBE_OK;
    }
    float start = ((float)buffer->pts) / GST_SECOND;
    float end = start + ((float)buffer->duration) / GST_SECOND;
    
    gst_memory_unmap(memory, &mapInfo);
    gst_memory_unref(memory);
    
    g_print("Buffer is %f -> %f\n%.*s\n", start, end, (int)mapInfo.size, mapInfo.data);
    
    return GST_PAD_PROBE_OK;
}

void text_changed(GstElement* playbin, gpointer user_data) {
  static std::vector<gint> probes;
  gint num_tracks, current_track;
  g_object_get(playbin, "n-text", &num_tracks, NULL);
  g_object_get(playbin, "current-text", &current_track, NULL);
  
  for(gint i = probes.size(); i < num_tracks; ++i) {
    GstPad* pad;
    g_signal_emit_by_name(playbin, "get-text-pad", i, &pad, NULL);
    if(!pad) {
      g_print("Unable to get pad %d\n", (int)i);
      continue;
    }
    probes.push_back(gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER,
        reinterpret_cast<GstPadProbeCallback>(new_buffer), 0, 0));
  }
}

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

  const char* uri = argv[1];

  GMainLoop *loop = g_main_loop_new(NULL, FALSE);
  GstElement *playbin = gst_element_factory_make("playbin", "player");
  if(!playbin) {
    g_print("Unable to create playbin element.\n");
    return 1;
  }

  GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(playbin));
  gst_bus_add_watch(bus, (GstBusFunc)bus_call, NULL);
  gst_object_unref(bus);

  if (uri) {
    g_object_set(playbin, "uri", uri, NULL);
  }
  
  g_signal_connect(playbin, "text-changed", G_CALLBACK(text_changed), loop);

  gst_element_set_state(playbin, GST_STATE_PLAYING);

  g_main_loop_run(loop);

  gst_element_set_state(playbin, GST_STATE_NULL);
  gst_object_unref(playbin);

  return 0;
}
