/*
 * test manual colorkey handling
 * including reserting the video
 *
 * gcc `pkg-config --cflags --libs gtk+-2.0 gstreamer-0.10 gstreamer-interfaces-0.10` xvoverlay.c -o xvoverlay
 */

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

#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#include <gst/interfaces/xoverlay.h>
#include <gst/interfaces/propertyprobe.h>

static GtkWidget *video_window = NULL;
static GstElement *sink = NULL;
static guint embed_xid = 0;
static GdkGC *trans_gc = NULL;

static GstBusSyncReply
bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
  if ((GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) &&
      gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
    GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (message));

    g_print ("got prepare-xwindow-id\n");
    if (!embed_xid) {
      embed_xid = GDK_WINDOW_XID (GDK_WINDOW (video_window->window));
    }

    if (g_object_class_find_property (G_OBJECT_GET_CLASS(element), "force-aspect-ratio")) {
      g_object_set (element, "force-aspect-ratio", TRUE, NULL);
    }

    gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (GST_MESSAGE_SRC (message)),
                                  embed_xid);
    //gst_x_overlay_expose (GST_X_OVERLAY (sink));
  }
  return GST_BUS_PASS;
}

static void
redraw_overlay (GtkWidget *widget)
{
  gdk_draw_rectangle(widget->window, widget->style->white_gc, TRUE,
     0, 0, widget->allocation.width, widget->allocation.height);

  if (trans_gc) {
    guint x,y;
    guint h=widget->allocation.height*0.75;

    gdk_draw_rectangle(widget->window, trans_gc, TRUE,
            0, 0, widget->allocation.width,h);
    
    for (y=h; y<widget->allocation.height; y++) {
      for (x=0; x<widget->allocation.width; x++) {
        if(((x&1) || (y&1)) && (x&1) != (y&1)) {
          gdk_draw_point(widget->window, trans_gc, x, y);
        }
      }
    }
  }
}

static gboolean
handle_resize_cb (GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{
  g_print ("handle resize\n");
  redraw_overlay (widget);
  return FALSE;
}

static gboolean
handle_expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
  g_print ("handle expose\n");
  redraw_overlay (widget);
  return FALSE;
}

static void
msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
  const GstStructure *s;

  s = gst_message_get_structure (message);

  /* We only care about state changed on the pipeline */
  if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
    GstState old, new, pending;
    gint color;

    gst_message_parse_state_changed (message, &old, &new, &pending);

    /* When state of the pipeline changes to paused or playing we start updating scale */
    switch(GST_STATE_TRANSITION(old,new)) {
      case GST_STATE_CHANGE_READY_TO_PAUSED:
        g_object_get (G_OBJECT (sink), "colorkey", &color, NULL);
        if( color != -1) {
          GdkColor trans_color={ 0,
                (color&0xff0000)>>8,
                (color&0xff00),
                (color&0xff)<<8
          };

          trans_gc = gdk_gc_new (video_window->window);
          gdk_gc_set_rgb_fg_color(trans_gc,&trans_color);
        }
        handle_resize_cb (video_window, NULL, NULL);
        break;
      default:
        break;
    }
  }
}

static void
window_closed (GtkWidget *widget, GdkEvent  *event, gpointer user_data)
{
  GstElement *pipeline=user_data;

  g_print ("stopping\n");
  gtk_widget_hide_all (widget);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gtk_main_quit();
}

static void
restart_pipeline(GtkWidget *widget, gpointer user_data)
{
  GstElement *pipeline=user_data;

  g_print ("restarting\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_element_set_state (pipeline, GST_STATE_PLAYING);
}

static void
toggle_resolution(GtkWidget *widget, gpointer user_data)
{
  GstElement *capsfilter=user_data;
  static guint state=0;
  static GstCaps *caps[2] = {NULL,NULL};
  
  if (caps[0] == NULL) {
      caps[0] = gst_caps_new_simple ("video/x-raw-yuv",
        "width", G_TYPE_INT, 80,
        "height", G_TYPE_INT, 60,
        NULL);
      caps[1] = gst_caps_new_simple ("video/x-raw-yuv",
        "width", G_TYPE_INT, 320,
        "height", G_TYPE_INT, 240,
        NULL);
  }

  g_print ("switching\n");
  g_object_set (capsfilter, "caps", caps[state], NULL);
  state = 1 - state;
}

static gboolean
run_pipeline(GstElement *pipeline)
{
  GstStateChangeReturn sret;

   /* prepare playing */
  sret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
  if (sret == GST_STATE_CHANGE_FAILURE) {
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gtk_main_quit();
  }
  return FALSE;
}

int
main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *vbox,*button;
  GstElement *pipeline, *src, *capsfilter;
  GstBus *bus;
  GstStateChangeReturn sret;
  GstPropertyProbe *probe;
  GValueArray *arr;

  if (!g_thread_supported ())
    g_thread_init (NULL);

  if (!XInitThreads ()) {
    g_print ("XInitThreads failed\n");
    exit (-1);
  }
  
  gst_init (&argc,&argv);
  gtk_init (&argc,&argv);
  
  /* prepare the pipeline */
  
  pipeline = gst_pipeline_new ("xvoverlay");
  src = gst_element_factory_make ("videotestsrc", NULL);
  capsfilter = gst_element_factory_make ("capsfilter", NULL);
  sink = gst_element_factory_make ("xvimagesink", NULL);
  gst_bin_add_many (GST_BIN (pipeline), src, capsfilter, sink, NULL);
  gst_element_link_many (src, capsfilter, sink, NULL);
  
  g_object_set (G_OBJECT (sink), 
    "autopaint-colorkey", FALSE, 
    "force-aspect-ratio", TRUE,
    "draw-borders",FALSE,
    NULL);

  /* check xvimagesink capabilities */
  sret = gst_element_set_state (pipeline, GST_STATE_READY);
  if (sret == GST_STATE_CHANGE_FAILURE) {
    g_printerr("Can't set pipeline to READY\n");
    gst_object_unref (pipeline);
    return -1;
  }
  
  probe = GST_PROPERTY_PROBE (sink);
  if(!probe) {
    g_printerr("Can't probe sink\n");
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (pipeline);
    return -1;
  }
  arr = gst_property_probe_probe_and_get_values_name (probe, "autopaint-colorkey");
  if(!arr || !arr->n_values) {
    g_printerr("Can't disable autopaint-colorkey property\n");
    /*
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (pipeline);
    return -1;
    */
  }
  if(arr) g_value_array_free (arr);

  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, pipeline);
  gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
  g_signal_connect (bus, "message::state-changed", G_CALLBACK (msg_state_changed), pipeline);
  gst_object_unref (bus);
  
  /* prepare the ui */

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (window_closed), (gpointer)pipeline);
  gtk_window_set_default_size (GTK_WINDOW (window), 320, 240);

  vbox=gtk_vbox_new(FALSE,3);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  video_window = gtk_drawing_area_new ();
  g_signal_connect (G_OBJECT (video_window), "configure-event",  G_CALLBACK (handle_resize_cb), NULL);
  g_signal_connect (G_OBJECT (video_window), "expose-event",  G_CALLBACK (handle_expose_cb), NULL);
  gtk_widget_set_double_buffered(video_window,FALSE);
  gtk_box_pack_start (GTK_BOX (vbox), video_window, TRUE, TRUE, 0);

  button=gtk_button_new_with_label("restart");
  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (restart_pipeline), (gpointer)pipeline);
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

  button=gtk_button_new_with_label("toggle res");
  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (toggle_resolution), (gpointer)capsfilter);
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

  /* show the gui and play */
  
  gtk_widget_show_all (window);
  g_idle_add((GSourceFunc)run_pipeline,(gpointer)pipeline);
  gtk_main ();

  gst_object_unref (pipeline);
  return 0;
}
