/*  Copyright 2023 Red Hat, Inc.
 *
 * GTK is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * GTK 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GTK; see the file COPYING.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * Author: Matthias Clasen
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <glib/gi18n-lib.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtk-rendernode-tool.h"

#ifdef GDK_WINDOWING_BROADWAY
#include <gsk/broadway/gskbroadwayrenderer.h>
#endif

static void
deserialize_error_func (const GskParseLocation *start,
                        const GskParseLocation *end,
                        const GError           *error,
                        gpointer                user_data)
{
  GString *string = g_string_new ("<data>");

  g_string_append_printf (string, ":%zu:%zu",
                          start->lines + 1, start->line_chars + 1);
  if (start->lines != end->lines || start->line_chars != end->line_chars)
    {
      g_string_append (string, "-");
      if (start->lines != end->lines)
        g_string_append_printf (string, "%zu:", end->lines + 1);
      g_string_append_printf (string, "%zu", end->line_chars + 1);
    }

  g_printerr (_("Error at %s: %s\n"), string->str, error->message);

  g_string_free (string, TRUE);
}

GskRenderNode *
load_node_file (const char *filename)
{
  GFile *file;
  GBytes *bytes;
  GError *error = NULL;

  file = g_file_new_for_commandline_arg (filename);
  bytes = g_file_load_bytes (file, NULL, NULL, &error);
  g_object_unref (file);

  if (bytes == NULL)
    {
      g_printerr (_("Failed to load node file: %s\n"), error->message);
      g_clear_error (&error);
      exit (1);
    }

  return gsk_render_node_deserialize (bytes, deserialize_error_func, NULL);
}

/* keep in sync with gsk/gskrenderer.c */
static GskRenderer *
get_renderer_for_name (const char *renderer_name)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  if (renderer_name == NULL)
    return NULL;
#ifdef GDK_WINDOWING_BROADWAY
  else if (g_ascii_strcasecmp (renderer_name, "broadway") == 0)
    return gsk_broadway_renderer_new ();
#endif
  else if (g_ascii_strcasecmp (renderer_name, "cairo") == 0)
    return gsk_cairo_renderer_new ();
  else if (g_ascii_strcasecmp (renderer_name, "gl") == 0)
    return gsk_gl_renderer_new ();
#ifdef GDK_RENDERING_VULKAN
  else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
    return gsk_vulkan_renderer_new ();
#endif
  else
    return NULL;
G_GNUC_END_IGNORE_DEPRECATIONS
}

static const char **
get_renderer_names (void)
{
  static const char *names[] = {
#ifdef GDK_WINDOWING_BROADWAY
    "broadway",
#endif
    "cairo",
    "gl",
#ifdef GDK_RENDERING_VULKAN
    "vulkan",
#endif
    NULL
  };

  return names;
}

GskRenderer *
create_renderer (const char *name, GError **error)
{
  GskRenderer *renderer;

  if (name == NULL)
    {
      /* awwwwwkward - there should be code to get the
       * default renderer without a surface */
      static GdkSurface *window = NULL;

      if (window == NULL)
        window = gdk_surface_new_toplevel (gdk_display_get_default ());
      return gsk_renderer_new_for_surface (window);
    }

  renderer = get_renderer_for_name (name);

  if (renderer == NULL)
    {
      char **names;
      char *suggestion;

      names = (char **) get_renderer_names ();
      suggestion = g_strjoinv ("\n  ", names);

      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                   "No renderer named \"%s\"\nPossible values:\n  %s", name, suggestion);
      return NULL;
    }

  if (!gsk_renderer_realize_for_display (renderer, gdk_display_get_default (), error))
    {
      g_object_unref (renderer);
      return NULL;
    }

  return renderer;
}
