/*
 * xrdesktop
 * Copyright 2019 Collabora Ltd.
 * Author: Christoph Haag <christoph.haag@collabora.com>
 * SPDX-License-Identifier: MIT
 */

#include "xrd-overlay-desktop-cursor.h"

#include <gxr.h>

#include "xrd-settings.h"
#include "xrd-math.h"
#include "graphene-ext.h"
#include "xrd-desktop-cursor.h"

struct _XrdOverlayDesktopCursor
{
  OpenVROverlay parent;

  GulkanTexture *texture;

  XrdDesktopCursorData data;
};

static void
xrd_overlay_desktop_cursor_interface_init (XrdDesktopCursorInterface *iface);

G_DEFINE_TYPE_WITH_CODE (XrdOverlayDesktopCursor, xrd_overlay_desktop_cursor, OPENVR_TYPE_OVERLAY,
                         G_IMPLEMENT_INTERFACE (XRD_TYPE_DESKTOP_CURSOR,
                                                xrd_overlay_desktop_cursor_interface_init))

static void
xrd_overlay_desktop_cursor_finalize (GObject *gobject);

static void
xrd_overlay_desktop_cursor_class_init (XrdOverlayDesktopCursorClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = xrd_overlay_desktop_cursor_finalize;
}

static void
xrd_overlay_desktop_cursor_init (XrdOverlayDesktopCursor *self)
{
  self->data.texture_width = 0;
  self->data.texture_height = 0;
  self->texture = NULL;

  openvr_overlay_create (OPENVR_OVERLAY (self),
                         "org.xrdesktop.cursor", "xrdesktop Cursor");
  if (!openvr_overlay_is_valid (OPENVR_OVERLAY (self)))
    {
      g_printerr ("Cursor overlay unavailable.\n");
      return;
    }

  xrd_desktop_cursor_init_settings (XRD_DESKTOP_CURSOR (self));

  /* pointer ray is MAX, pointer tip is MAX - 1, so cursor is MAX - 2 */
  openvr_overlay_set_sort_order (OPENVR_OVERLAY (self), UINT32_MAX - 2);

  openvr_overlay_show (OPENVR_OVERLAY (self));
}

XrdOverlayDesktopCursor *
xrd_overlay_desktop_cursor_new (void)
{
  return (XrdOverlayDesktopCursor*) g_object_new (XRD_TYPE_OVERLAY_DESKTOP_CURSOR, 0);
}

static GulkanTexture *
_get_texture (XrdDesktopCursor *cursor)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);
  return self->texture;
}

static void
_submit_texture (XrdDesktopCursor *cursor,
                 GulkanClient     *uploader)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);

  GulkanTexture *texture = _get_texture (cursor);
  openvr_overlay_submit_texture (OPENVR_OVERLAY (self), uploader, texture);
}

static void
_set_and_submit_texture (XrdDesktopCursor *cursor,
                         GulkanClient     *client,
                         GulkanTexture    *texture)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);

  openvr_overlay_submit_texture (OPENVR_OVERLAY (self), client, texture);

  GulkanTexture *to_free = self->texture;

  self->texture = texture;
  _submit_texture (cursor, client);

  self->data.texture_width = gulkan_texture_get_width (texture);
  self->data.texture_height = gulkan_texture_get_height (texture);

  if (to_free)
    g_object_unref (to_free);
}

static void
_show (XrdDesktopCursor *cursor)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);
  openvr_overlay_show (OPENVR_OVERLAY (self));
}

static void
_hide (XrdDesktopCursor *cursor)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);
  openvr_overlay_hide (OPENVR_OVERLAY (self));
}

static void
xrd_overlay_desktop_cursor_finalize (GObject *gobject)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (gobject);

  if (self->texture)
    g_clear_object (&self->texture);

  openvr_overlay_destroy (OPENVR_OVERLAY (self));
}

static XrdDesktopCursorData*
_get_data (XrdDesktopCursor *cursor)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);
  return &self->data;
}

static void
_get_transformation (XrdDesktopCursor  *cursor,
                     graphene_matrix_t *matrix)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);
  openvr_overlay_get_transform_absolute (OPENVR_OVERLAY(self), matrix);
}

static void
_set_transformation (XrdDesktopCursor  *cursor,
                     graphene_matrix_t *matrix)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);
  openvr_overlay_set_transform_absolute (OPENVR_OVERLAY (self), matrix);
}

static void
_set_width_meters (XrdDesktopCursor *cursor,
                   float             width)
{
  XrdOverlayDesktopCursor *self = XRD_OVERLAY_DESKTOP_CURSOR (cursor);
  if (!openvr_overlay_set_width_meters (OPENVR_OVERLAY (self), width))
    g_warning ("Could not set overlay desktop cursor width to %f\n",
               (double) width);
}

static void
xrd_overlay_desktop_cursor_interface_init (XrdDesktopCursorInterface *iface)
{
  iface->submit_texture = _submit_texture;
  iface->get_texture = _get_texture;
  iface->set_and_submit_texture = _set_and_submit_texture;
  iface->show = _show;
  iface->hide = _hide;
  iface->set_width_meters = _set_width_meters;
  iface->get_data = _get_data;
  iface->get_transformation = _get_transformation;
  iface->set_transformation = _set_transformation;
}
