/* GStreamer
 *
 * Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

/**
 * SECTION:gstplay-videooverlayvideorenderer
 * @title: GstPlayVideoOverlayVideoRenderer
 * @short_description: Play Video Overlay Video Renderer
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gstplay-video-overlay-video-renderer.h"
#include "gstplay.h"

#include <gst/video/video.h>

struct _GstPlayVideoOverlayVideoRenderer
{
  GObject parent;

  GstVideoOverlay *video_overlay;
  gpointer window_handle;
  gint x, y, width, height;

  GstElement *video_sink;       /* configured video sink, or NULL      */
};

struct _GstPlayVideoOverlayVideoRendererClass
{
  GObjectClass parent_class;
};

static void
    gst_play_video_overlay_video_renderer_interface_init
    (GstPlayVideoRendererInterface * iface);

enum
{
  VIDEO_OVERLAY_VIDEO_RENDERER_PROP_0,
  VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE,
  VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK,
  VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST
};

G_DEFINE_TYPE_WITH_CODE (GstPlayVideoOverlayVideoRenderer,
    gst_play_video_overlay_video_renderer, G_TYPE_OBJECT,
    G_IMPLEMENT_INTERFACE (GST_TYPE_PLAY_VIDEO_RENDERER,
        gst_play_video_overlay_video_renderer_interface_init));

static GParamSpec
    * video_overlay_video_renderer_param_specs
    [VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST] = { NULL, };

static void
gst_play_video_overlay_video_renderer_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
{
  GstPlayVideoOverlayVideoRenderer *self =
      GST_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (object);

  switch (prop_id) {
    case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE:
      self->window_handle = g_value_get_pointer (value);
      if (self->video_overlay)
        gst_video_overlay_set_window_handle (self->video_overlay,
            (guintptr) self->window_handle);
      break;
    case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK:
      self->video_sink = gst_object_ref_sink (g_value_get_object (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_play_video_overlay_video_renderer_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
{
  GstPlayVideoOverlayVideoRenderer *self =
      GST_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (object);

  switch (prop_id) {
    case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE:
      g_value_set_pointer (value, self->window_handle);
      break;
    case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK:
      g_value_set_object (value, self->video_sink);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_play_video_overlay_video_renderer_finalize (GObject * object)
{
  GstPlayVideoOverlayVideoRenderer *self =
      GST_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (object);

  if (self->video_overlay)
    gst_object_unref (self->video_overlay);

  if (self->video_sink)
    gst_object_unref (self->video_sink);

  G_OBJECT_CLASS
      (gst_play_video_overlay_video_renderer_parent_class)->finalize (object);
}

static void
    gst_play_video_overlay_video_renderer_class_init
    (GstPlayVideoOverlayVideoRendererClass * klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->set_property =
      gst_play_video_overlay_video_renderer_set_property;
  gobject_class->get_property =
      gst_play_video_overlay_video_renderer_get_property;
  gobject_class->finalize = gst_play_video_overlay_video_renderer_finalize;

  video_overlay_video_renderer_param_specs
      [VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE] =
      g_param_spec_pointer ("window-handle", "Window Handle",
      "Window handle to embed the video into",
      G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);

  video_overlay_video_renderer_param_specs
      [VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK] =
      g_param_spec_object ("video-sink", "Video Sink",
      "the video output element to use (NULL = default sink)",
      GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

  g_object_class_install_properties (gobject_class,
      VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST,
      video_overlay_video_renderer_param_specs);
}

static void
    gst_play_video_overlay_video_renderer_init
    (GstPlayVideoOverlayVideoRenderer * self)
{
  self->x = self->y = self->width = self->height = -1;
  self->video_sink = NULL;
}

static GstElement *gst_play_video_overlay_video_renderer_create_video_sink
    (GstPlayVideoRenderer * iface, GstPlay * play)
{
  GstElement *video_overlay;
  GstPlayVideoOverlayVideoRenderer *self =
      GST_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (iface);

  if (self->video_overlay)
    gst_object_unref (self->video_overlay);

  video_overlay = gst_play_get_pipeline (play);
  g_return_val_if_fail (GST_IS_VIDEO_OVERLAY (video_overlay), NULL);

  self->video_overlay = GST_VIDEO_OVERLAY (video_overlay);

  gst_video_overlay_set_window_handle (self->video_overlay,
      (guintptr) self->window_handle);
  if (self->width != -1 || self->height != -1)
    gst_video_overlay_set_render_rectangle (self->video_overlay, self->x,
        self->y, self->width, self->height);

  return self->video_sink;
}

static void
    gst_play_video_overlay_video_renderer_interface_init
    (GstPlayVideoRendererInterface * iface)
{
  iface->create_video_sink =
      gst_play_video_overlay_video_renderer_create_video_sink;
}

/**
 * gst_play_video_overlay_video_renderer_new:
 * @window_handle: (allow-none): Window handle to use or %NULL
 *
 * Returns: (transfer full):
 * Since: 1.20
 */
GstPlayVideoRenderer *
gst_play_video_overlay_video_renderer_new (gpointer window_handle)
{
  return g_object_new (GST_TYPE_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER,
      "window-handle", window_handle, NULL);
}

/**
 * gst_play_video_overlay_video_renderer_new_with_sink:
 * @window_handle: (allow-none): Window handle to use or %NULL
 * @video_sink: (transfer floating): the custom video_sink element to be set for the video renderer
 *
 * Returns: (transfer full):
 *
 * Since: 1.20
 */
GstPlayVideoRenderer *
gst_play_video_overlay_video_renderer_new_with_sink (gpointer window_handle,
    GstElement * video_sink)
{
  return g_object_new (GST_TYPE_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER,
      "window-handle", window_handle, "video-sink", video_sink, NULL);
}

/**
 * gst_play_video_overlay_video_renderer_set_window_handle:
 * @self: #GstPlayVideoRenderer instance
 * @window_handle: handle referencing to the platform specific window
 *
 * Sets the platform specific window handle into which the video
 * should be rendered
 * Since: 1.20
 **/
void gst_play_video_overlay_video_renderer_set_window_handle
    (GstPlayVideoOverlayVideoRenderer * self, gpointer window_handle)
{
  g_return_if_fail (GST_IS_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (self));

  g_object_set (self, "window-handle", window_handle, NULL);
}

/**
 * gst_play_video_overlay_video_renderer_get_window_handle:
 * @self: #GstPlayVideoRenderer instance
 *
 * Returns: (transfer none): The currently set, platform specific window
 * handle
 * Since: 1.20
 */
gpointer
    gst_play_video_overlay_video_renderer_get_window_handle
    (GstPlayVideoOverlayVideoRenderer * self) {
  gpointer window_handle;

  g_return_val_if_fail (GST_IS_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (self), NULL);

  g_object_get (self, "window-handle", &window_handle, NULL);

  return window_handle;
}

/**
 * gst_play_video_overlay_video_renderer_expose:
 * @self: a #GstPlayVideoOverlayVideoRenderer instance.
 *
 * Tell an overlay that it has been exposed. This will redraw the current frame
 * in the drawable even if the pipeline is PAUSED.
 * Since: 1.20
 */
void gst_play_video_overlay_video_renderer_expose
    (GstPlayVideoOverlayVideoRenderer * self)
{
  g_return_if_fail (GST_IS_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (self));

  if (self->video_overlay)
    gst_video_overlay_expose (self->video_overlay);
}

/**
 * gst_play_video_overlay_video_renderer_set_render_rectangle:
 * @self: a #GstPlayVideoOverlayVideoRenderer instance
 * @x: the horizontal offset of the render area inside the window
 * @y: the vertical offset of the render area inside the window
 * @width: the width of the render area inside the window
 * @height: the height of the render area inside the window
 *
 * Configure a subregion as a video target within the window set by
 * gst_play_video_overlay_video_renderer_set_window_handle(). If this is not
 * used or not supported the video will fill the area of the window set as the
 * overlay to 100%. By specifying the rectangle, the video can be overlaid to
 * a specific region of that window only. After setting the new rectangle one
 * should call gst_play_video_overlay_video_renderer_expose() to force a
 * redraw. To unset the region pass -1 for the @width and @height parameters.
 *
 * This method is needed for non fullscreen video overlay in UI toolkits that
 * do not support subwindows.
 *
 * Since: 1.20
 */
void gst_play_video_overlay_video_renderer_set_render_rectangle
    (GstPlayVideoOverlayVideoRenderer * self, gint x, gint y, gint width,
    gint height)
{
  g_return_if_fail (GST_IS_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (self));

  self->x = x;
  self->y = y;
  self->width = width;
  self->height = height;

  if (self->video_overlay)
    gst_video_overlay_set_render_rectangle (self->video_overlay,
        x, y, width, height);
}

/**
 * gst_play_video_overlay_video_renderer_get_render_rectangle:
 * @self: a #GstPlayVideoOverlayVideoRenderer instance
 * @x: (out) (allow-none): the horizontal offset of the render area inside the window
 * @y: (out) (allow-none): the vertical offset of the render area inside the window
 * @width: (out) (allow-none): the width of the render area inside the window
 * @height: (out) (allow-none): the height of the render area inside the window
 *
 * Return the currently configured render rectangle. See gst_play_video_overlay_video_renderer_set_render_rectangle()
 * for details.
 *
 * Since: 1.20
 */
void gst_play_video_overlay_video_renderer_get_render_rectangle
    (GstPlayVideoOverlayVideoRenderer * self, gint * x, gint * y,
    gint * width, gint * height)
{
  g_return_if_fail (GST_IS_PLAY_VIDEO_OVERLAY_VIDEO_RENDERER (self));

  if (x)
    *x = self->x;
  if (y)
    *y = self->y;
  if (width)
    *width = self->width;
  if (height)
    *height = self->height;
}