/* PIKA - Photo and Image Kooker Application
 * a rebranding of The GNU Image Manipulation Program (created with heckimp)
 * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
 *
 * Original copyright, applying to most contents (license remains unchanged): 
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * The PIKA Help Browser
 * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
 *                         Michael Natterer <mitch@gimp.org>
 *                         Henrik Brix Andersen <brix@gimp.org>
 *
 * Some code & ideas taken from the GNOME help browser.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <string.h>  /*  strlen, strcmp  */

#include <libpika/pika.h>
#include <libpika/pikaui.h>

#include "plug-ins/help/pikahelp.h"

#include "dialog.h"

#include "libpika/stdplugins-intl.h"


#define PIKA_HELP_BROWSER_EXT_PROC       "extension-pika-help-browser"
#define PIKA_HELP_BROWSER_TEMP_EXT_PROC  "extension-pika-help-browser-temp"
#define PLUG_IN_BINARY                   "help-browser"
#define PLUG_IN_ROLE                     "pika-help-browser"


#define PIKA_TYPE_HELP_BROWSER (pika_help_browser_get_type ())
G_DECLARE_FINAL_TYPE (PikaHelpBrowser, pika_help_browser,
                      PIKA, HELP_BROWSER,
                      PikaPlugIn)

static PikaValueArray * help_browser_run              (PikaProcedure        *procedure,
                                                       PikaProcedureConfig  *config,
                                                       gpointer              run_data);

static void             temp_proc_install             (PikaPlugIn           *plug_in);
static PikaValueArray * temp_proc_run                 (PikaProcedure        *procedure,
                                                       PikaProcedureConfig  *config,
                                                       gpointer              run_data);

static PikaHelpProgress * help_browser_progress_new   (void);

struct _PikaHelpBrowser
{
  PikaPlugIn parent_instance;

  PikaProcedureConfig   *config;
  GtkApplication        *app;
  PikaHelpBrowserDialog *window;
};

G_DEFINE_TYPE (PikaHelpBrowser, pika_help_browser, PIKA_TYPE_PLUG_IN)

PIKA_MAIN (PIKA_TYPE_HELP_BROWSER)
DEFINE_STD_SET_I18N

static GList *
help_browser_query_procedures (PikaPlugIn *plug_in)
{
  return g_list_append (NULL, g_strdup (PIKA_HELP_BROWSER_EXT_PROC));
}

static PikaProcedure *
help_browser_create_procedure (PikaPlugIn  *plug_in,
                               const gchar *name)
{
  PikaProcedure *procedure = NULL;

  if (! strcmp (name, PIKA_HELP_BROWSER_EXT_PROC))
    {
      procedure = pika_procedure_new (plug_in, name,
                                      PIKA_PDB_PROC_TYPE_EXTENSION,
                                      help_browser_run, plug_in, NULL);

      pika_procedure_set_documentation (procedure,
                                        "Browse the PIKA user manual",
                                        "A small and simple HTML browser "
                                        "optimized for browsing the PIKA "
                                        "user manual.",
                                        PIKA_HELP_BROWSER_EXT_PROC);
      pika_procedure_set_attribution (procedure,
                                      "Sven Neumann <sven@gimp.org>, "
                                      "Michael Natterer <mitch@gimp.org>"
                                      "Henrik Brix Andersen <brix@gimp.org>",
                                      "Sven Neumann, Michael Natterer & "
                                      "Henrik Brix Andersen",
                                      "1999-2008");

      PIKA_PROC_ARG_ENUM (procedure, "run-mode",
                          "Run mode",
                          "The run mode",
                          PIKA_TYPE_RUN_MODE,
                          PIKA_RUN_INTERACTIVE,
                          G_PARAM_READWRITE);

      PIKA_PROC_ARG_STRV (procedure, "domain-names",
                          "Domain names",
                          "Domain names",
                          G_PARAM_READWRITE);

      PIKA_PROC_ARG_STRV (procedure, "domain-uris",
                          "Domain URIs",
                          "Domain URIs",
                          G_PARAM_READWRITE);

      PIKA_PROC_AUX_ARG_BYTES (procedure, "dialog-data",
                               "Dialog data",
                               "Remembering dialog's basic features; this is never meant to be a public argument",
                               PIKA_PARAM_READWRITE);
    }

  return procedure;
}

static void
on_app_activate (GApplication *gapp,
                 gpointer      user_data)
{
  PikaHelpBrowser *browser = PIKA_HELP_BROWSER (user_data);
  GtkApplication  *app     = GTK_APPLICATION (gapp);

  browser->window = pika_help_browser_dialog_new (PLUG_IN_BINARY, gapp, browser->config);

  gtk_application_set_accels_for_action (app, "win.back", (const char*[]) { "<alt>Left", NULL });
  gtk_application_set_accels_for_action (app, "win.forward", (const char*[]) { "<alt>Right", NULL });
  gtk_application_set_accels_for_action (app, "win.reload", (const char*[]) { "<control>R", NULL });
  gtk_application_set_accels_for_action (app, "win.stop", (const char*[]) { "Escape", NULL });
  gtk_application_set_accels_for_action (app, "win.home", (const char*[]) { "<alt>Home", NULL });
  gtk_application_set_accels_for_action (app, "win.copy-selection", (const char*[]) { "<control>C", NULL });
  gtk_application_set_accels_for_action (app, "win.zoom-in", (const char*[]) { "<control>plus", NULL });
  gtk_application_set_accels_for_action (app, "win.zoom-out", (const char*[]) { "<control>minus", NULL });
  gtk_application_set_accels_for_action (app, "win.find", (const char*[]) { "<control>F", NULL });
  gtk_application_set_accels_for_action (app, "win.find-again", (const char*[]) { "<control>G", NULL });
  gtk_application_set_accels_for_action (app, "win.close", (const char*[]) { "<control>W", "<control>Q", NULL });
  gtk_application_set_accels_for_action (app, "win.show-index", (const char*[]) { "<control>I", NULL });
}

static PikaValueArray *
help_browser_run (PikaProcedure        *procedure,
                  PikaProcedureConfig  *config,
                  gpointer              user_data)
{
  PikaHelpBrowser  *browser      = PIKA_HELP_BROWSER (user_data);
  gchar           **domain_names = NULL;
  gchar           **domain_uris  = NULL;

  browser->config = config;
  g_object_get (config,
                "domain-names", &domain_names,
                "domain-uris",  &domain_uris,
                NULL);
  if (! pika_help_init ((const gchar **) domain_names,
                        (const gchar **) domain_uris))
    {
      g_strfreev (domain_names);
      g_strfreev (domain_uris);

      return pika_procedure_new_return_values (procedure,
                                               PIKA_PDB_CALLING_ERROR,
                                               NULL);
    }
  g_strfreev (domain_names);
  g_strfreev (domain_uris);

  temp_proc_install (pika_procedure_get_plug_in (procedure));

  pika_procedure_extension_ready (procedure);
  pika_plug_in_extension_enable (pika_procedure_get_plug_in (procedure));

#if GLIB_CHECK_VERSION(2,74,0)
  browser->app = gtk_application_new (NULL, G_APPLICATION_DEFAULT_FLAGS);
#else
  browser->app = gtk_application_new (NULL, G_APPLICATION_FLAGS_NONE);
#endif
  g_signal_connect (browser->app, "activate", G_CALLBACK (on_app_activate), browser);

  g_application_run (G_APPLICATION (browser->app), 0, NULL);

  g_clear_object (&browser->app);

  return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}

static void
temp_proc_install (PikaPlugIn *plug_in)
{
  PikaProcedure *procedure;

  procedure = pika_procedure_new (plug_in, PIKA_HELP_BROWSER_TEMP_EXT_PROC,
                                  PIKA_PDB_PROC_TYPE_TEMPORARY,
                                  temp_proc_run, plug_in, NULL);

  pika_procedure_set_documentation (procedure,
                                    "DON'T USE THIS ONE",
                                    "(Temporary procedure)",
                                    NULL);
  pika_procedure_set_attribution (procedure,
                                  "Sven Neumann <sven@gimp.org>, "
                                  "Michael Natterer <mitch@gimp.org>"
                                  "Henrik Brix Andersen <brix@gimp.org>",
                                  "Sven Neumann, Michael Natterer & "
                                  "Henrik Brix Andersen",
                                  "1999-2008");

  PIKA_PROC_ARG_STRING (procedure, "help-domain",
                        "Help domain",
                        "Help domain to use",
                        NULL,
                        G_PARAM_READWRITE);

  PIKA_PROC_ARG_STRING (procedure, "help-locales",
                        "Help locales",
                        "Language to use",
                        NULL,
                        G_PARAM_READWRITE);

  PIKA_PROC_ARG_STRING (procedure, "help-id",
                        "Help ID",
                        "Help ID to open",
                        NULL,
                        G_PARAM_READWRITE);

  pika_plug_in_add_temp_procedure (plug_in, procedure);
  g_object_unref (procedure);
}

typedef struct _IdleClosure
{
  PikaHelpBrowser *browser;
  char            *help_domain;
  char            *help_locales;
  char            *help_id;
} IdleClosure;

static void
idle_closure_free (gpointer data)
{
  IdleClosure *closure = data;

  g_free (closure->help_domain);
  g_free (closure->help_locales);
  g_free (closure->help_id);
  g_free (closure);
}

static gboolean
show_help_on_idle (gpointer user_data)
{
  IdleClosure      *closure = user_data;
  PikaHelpDomain   *domain;
  PikaHelpProgress *progress = NULL;
  PikaHelpLocale   *locale;
  GList            *locales;
  char             *uri;
  gboolean          fatal_error;

  /* First get the URI to load */
  domain = pika_help_lookup_domain (closure->help_domain);
  if (!domain)
    return G_SOURCE_REMOVE;

  locales = pika_help_parse_locales (closure->help_locales);

  if (! g_str_has_prefix (domain->help_uri, "file:"))
    progress = help_browser_progress_new ();

  uri = pika_help_domain_map (domain, locales, closure->help_id,
                              progress, &locale, &fatal_error);

  if (progress)
    pika_help_progress_free (progress);

  g_list_free_full (locales, (GDestroyNotify) g_free);

  /* Now actually load it */
  if (uri)
    {
      pika_help_browser_dialog_make_index (closure->browser->window, domain, locale);
      pika_help_browser_dialog_load (closure->browser->window, uri);

      g_free (uri);
    }

  return G_SOURCE_REMOVE;
}

static PikaValueArray *
temp_proc_run (PikaProcedure        *procedure,
               PikaProcedureConfig  *config,
               gpointer              user_data)
{
  PikaHelpBrowser *browser = PIKA_HELP_BROWSER (user_data);
  IdleClosure     *closure;
  gchar           *str;

  closure = g_new0 (IdleClosure, 1);
  closure->browser = browser;

  g_object_get (config, "help-domain", &str, NULL);
  closure->help_domain = g_strdup ((str && *str)? str : PIKA_HELP_DEFAULT_DOMAIN);
  g_free (str);

  g_object_get (config, "help-locales", &str, NULL);
  if (str && *str)
    closure->help_locales = g_strdup (str);
  g_free (str);

  g_object_get (config, "help-id", &str, NULL);
  closure->help_id = g_strdup ((str && *str)? str : PIKA_HELP_DEFAULT_ID);
  g_free (str);

  /* Do this on idle, to make sure everything is initialized already */
  g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
                   show_help_on_idle,
                   closure, idle_closure_free);

  return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}


static void
help_browser_progress_start (const gchar *message,
                             gboolean     cancelable,
                             gpointer     user_data)
{
  pika_progress_init (message);
}

static void
help_browser_progress_update (gdouble  value,
                              gpointer user_data)
{
  pika_progress_update (value);
}

static void
help_browser_progress_end (gpointer user_data)
{
  pika_progress_end ();
}

static PikaHelpProgress *
help_browser_progress_new (void)
{
  static const PikaHelpProgressVTable vtable =
  {
    help_browser_progress_start,
    help_browser_progress_end,
    help_browser_progress_update
  };

  return pika_help_progress_new (&vtable, NULL);
}

static void
pika_help_browser_class_init (PikaHelpBrowserClass *klass)
{
  PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);

  plug_in_class->query_procedures = help_browser_query_procedures;
  plug_in_class->create_procedure = help_browser_create_procedure;
  plug_in_class->set_i18n         = STD_SET_I18N;
}

static void
pika_help_browser_init (PikaHelpBrowser *help_browser)
{
}
