#include <string.h>

#include <gst/gst.h>
#include <gst/video/videooverlay.h>
#include <ges/ges.h>

#include <gdk/gdk.h>
#if defined (GDK_WINDOWING_X11)
#include <gdk/gdkx.h>
#elif defined (GDK_WINDOWING_WIN32)
#include <gdk/gdkwin32.h>
#elif defined (GDK_WINDOWING_QUARTZ)
#include <gdk/gdkquartz.h>
#endif

#include <gtk/gtk.h>

#define URI1 "file:///home/slomo/Downloads/bunny.mov"

typedef struct
{
  GstElement *pipeline;
  GstElement *video_sink;
  GESTimeline *timeline;
  GESLayer *layer;
  GESClip *clip;

  GtkWidget *window;
  GtkWidget *play_button, *pause_button, *switch_button;
  GtkWidget *seekbar;
  GtkWidget *video_area;
  gulong seekbar_value_changed_signal_id;
} GtkPlay;

static void
delete_event_cb (GtkWidget * widget, GdkEvent * event, GtkPlay * play)
{
  gst_element_set_state (play->pipeline, GST_STATE_NULL);
  gtk_main_quit ();
}

static void
video_area_realize_cb (GtkWidget * widget, GtkPlay * play)
{
  GdkWindow *window = gtk_widget_get_window (widget);
  guintptr window_handle;

  if (!gdk_window_ensure_native (window))
    g_error ("Couldn't create native window needed for GstXOverlay!");

#if defined (GDK_WINDOWING_WIN32)
  window_handle = (guintptr) GDK_WINDOW_HWND (window);
#elif defined (GDK_WINDOWING_QUARTZ)
  window_handle = gdk_quartz_window_get_nsview (window);
#elif defined (GDK_WINDOWING_X11)
  window_handle = GDK_WINDOW_XID (window);
#endif
  gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (play->video_sink),
      window_handle);
}

static void
play_clicked_cb (GtkButton * button, GtkPlay * play)
{
  g_assert (gst_element_set_state (play->pipeline,
          GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE);
}

static void
pause_clicked_cb (GtkButton * button, GtkPlay * play)
{
  g_assert (gst_element_set_state (play->pipeline,
          GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE);
}

static void
switch_clicked_cb (GtkButton * button, GtkPlay * play)
{
  GstClockTime duration = GST_CLOCK_TIME_NONE;

  if (gst_element_query_duration (play->pipeline, GST_FORMAT_TIME, &duration) &&
      duration != GST_CLOCK_TIME_NONE) {
    g_assert (gst_element_seek (play->pipeline, -1.0, GST_FORMAT_TIME,
            GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET,
            duration / 2));
  }
}

static void
seekbar_value_changed_cb (GtkRange * range, GtkPlay * play)
{
  gdouble value = gtk_range_get_value (GTK_RANGE (play->seekbar));
  g_assert (gst_element_seek (play->pipeline, 1.0, GST_FORMAT_TIME,
          GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, gst_util_uint64_scale (value,
              GST_SECOND, 1), GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE));
}

static void
create_ui (GtkPlay * play)
{
  GtkWidget *controls, *main_hbox, *main_vbox;

  play->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (play->window), "delete-event",
      G_CALLBACK (delete_event_cb), play);

  play->video_area = gtk_drawing_area_new ();
  gtk_widget_set_double_buffered (play->video_area, FALSE);
  g_signal_connect (play->video_area, "realize",
      G_CALLBACK (video_area_realize_cb), play);

  play->play_button =
      gtk_button_new_from_icon_name ("media-playback-start",
      GTK_ICON_SIZE_BUTTON);
  g_signal_connect (G_OBJECT (play->play_button), "clicked",
      G_CALLBACK (play_clicked_cb), play);

  play->pause_button =
      gtk_button_new_from_icon_name ("media-playback-pause",
      GTK_ICON_SIZE_BUTTON);
  g_signal_connect (G_OBJECT (play->pause_button), "clicked",
      G_CALLBACK (pause_clicked_cb), play);

  play->switch_button =
      gtk_button_new_from_icon_name ("media-skip-forward",
      GTK_ICON_SIZE_BUTTON);
  g_signal_connect (G_OBJECT (play->switch_button), "clicked",
      G_CALLBACK (switch_clicked_cb), play);

  play->seekbar =
      gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 100, 1);
  gtk_scale_set_draw_value (GTK_SCALE (play->seekbar), 0);
  play->seekbar_value_changed_signal_id =
      g_signal_connect (G_OBJECT (play->seekbar), "value-changed",
      G_CALLBACK (seekbar_value_changed_cb), play);

  controls = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_pack_start (GTK_BOX (controls), play->play_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (controls), play->pause_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (controls), play->switch_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (controls), play->seekbar, TRUE, TRUE, 2);

  main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_pack_start (GTK_BOX (main_hbox), play->video_area, TRUE, TRUE, 0);

  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (main_vbox), controls, FALSE, FALSE, 0);
  gtk_container_add (GTK_CONTAINER (play->window), main_vbox);

  gtk_widget_realize (play->video_area);

  gtk_widget_show_all (play->window);

  gtk_widget_hide (play->video_area);
}

static void
play_clear (GtkPlay * play)
{
  gst_object_unref (play->pipeline);
}

static gboolean
tick_cb (GtkPlay * play)
{
  GstClockTime position = 0, duration = 0;

  gst_element_query_position (play->pipeline, GST_FORMAT_TIME, &position);
  if (position == -1)
    position = 0;

  gst_element_query_duration (play->pipeline, GST_FORMAT_TIME, &duration);
  if (duration == -1)
    duration = 0;

  g_signal_handler_block (play->seekbar, play->seekbar_value_changed_signal_id);
  gtk_range_set_range (GTK_RANGE (play->seekbar), 0,
      (gdouble) duration / GST_SECOND);
  gtk_range_set_value (GTK_RANGE (play->seekbar),
      (gdouble) position / GST_SECOND);
  g_signal_handler_unblock (play->seekbar,
      play->seekbar_value_changed_signal_id);

  return G_SOURCE_CONTINUE;
}

int
main (gint argc, gchar ** argv)
{
  GtkPlay play;
  GOptionContext *ctx;
  GOptionEntry options[] = {
    {NULL}
  };
  GError *err = NULL;

  memset (&play, 0, sizeof (play));

  g_set_prgname ("gtk-play");

  ctx = g_option_context_new ("FILE|URI");
  g_option_context_add_main_entries (ctx, options, NULL);
  g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
  g_option_context_add_group (ctx, gst_init_get_option_group ());
  g_option_context_add_group (ctx, ges_init_get_option_group ());
  if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
    g_print ("Error initializing: %s\n", GST_STR_NULL (err->message));
    return 1;
  }
  g_option_context_free (ctx);

  ges_init ();

  play.pipeline = GST_ELEMENT (ges_pipeline_new ());
  g_assert (play.pipeline);
  ges_pipeline_set_mode (GES_PIPELINE (play.pipeline),
      GES_PIPELINE_MODE_PREVIEW);
  play.video_sink = gst_element_factory_make ("glimagesink", NULL);
  ges_pipeline_preview_set_video_sink (GES_PIPELINE (play.pipeline),
      play.video_sink);

  {
    GESTrack *tracka, *trackv;
    GESAsset *asset;

    play.timeline = ges_timeline_new ();
    tracka = GES_TRACK (ges_audio_track_new ());
    trackv = GES_TRACK (ges_video_track_new ());

    ges_track_set_mixing (tracka, FALSE);
    ges_track_set_mixing (trackv, FALSE);

    play.layer = GES_LAYER (ges_layer_new ());

    ges_timeline_add_layer (play.timeline, play.layer);
    ges_timeline_add_track (play.timeline, tracka);
    ges_timeline_add_track (play.timeline, trackv);

    ges_pipeline_set_timeline (GES_PIPELINE (play.pipeline), play.timeline);

    asset = GES_ASSET (ges_uri_clip_asset_request_sync (URI1, NULL));
    g_assert (asset);

    play.clip = GES_CLIP (ges_asset_extract (asset, NULL));
    g_assert (play.clip);
    g_object_unref (asset);

    //g_object_set (clip, "start", (guint64) 0, "in-point", (guint64) 0, "duration", (guint64) 20 * GST_SECOND, NULL);

    ges_layer_add_clip (play.layer, play.clip);
    ges_timeline_commit (play.timeline);
  }

  create_ui (&play);
  gtk_widget_show (play.video_area);

  g_timeout_add (200, (GSourceFunc) tick_cb, &play);

  gst_element_set_state (play.pipeline, GST_STATE_READY);

  gtk_main ();

  play_clear (&play);

  return 0;
}
