/* 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
 *
 * pikaplugin-progress.c
 *
 * 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>

#include "libpikabase/pikabase.h"

#include "plug-in-types.h"

#include "core/pika.h"
#include "core/pikadisplay.h"
#include "core/pikaparamspecs.h"
#include "core/pikapdbprogress.h"
#include "core/pikaprogress.h"

#include "pdb/pikapdb.h"
#include "pdb/pikapdberror.h"

#include "pikaplugin.h"
#include "pikaplugin-progress.h"
#include "pikapluginmanager.h"
#include "pikatemporaryprocedure.h"

#include "pika-intl.h"


/*  local function prototypes  */

static void   pika_plug_in_progress_cancel_callback (PikaProgress *progress,
                                                     PikaPlugIn   *plug_in);


/*  public functions  */

gint
pika_plug_in_progress_attach (PikaProgress *progress)
{
  gint attach_count;

  g_return_val_if_fail (PIKA_IS_PROGRESS (progress), 0);

  attach_count =
    GPOINTER_TO_INT (g_object_get_data (G_OBJECT (progress),
                                        "plug-in-progress-attach-count"));

  attach_count++;

  g_object_set_data (G_OBJECT (progress), "plug-in-progress-attach-count",
                     GINT_TO_POINTER (attach_count));

  return attach_count;
}

gint
pika_plug_in_progress_detach (PikaProgress *progress)
{
  gint attach_count;

  g_return_val_if_fail (PIKA_IS_PROGRESS (progress), 0);

  attach_count =
    GPOINTER_TO_INT (g_object_get_data (G_OBJECT (progress),
                                        "plug-in-progress-attach-count"));

  attach_count--;

  g_object_set_data (G_OBJECT (progress), "plug-in-progress-attach-count",
                     GINT_TO_POINTER (attach_count));

  return attach_count;
}

void
pika_plug_in_progress_start (PikaPlugIn  *plug_in,
                             const gchar *message,
                             PikaDisplay *display)
{
  PikaPlugInProcFrame *proc_frame;

  g_return_if_fail (PIKA_IS_PLUG_IN (plug_in));
  g_return_if_fail (display == NULL || PIKA_IS_DISPLAY (display));

  proc_frame = pika_plug_in_get_proc_frame (plug_in);

  if (! proc_frame->progress)
    {
      proc_frame->progress = pika_new_progress (plug_in->manager->pika,
                                                display);

      if (proc_frame->progress)
        {
          proc_frame->progress_created = TRUE;

          g_object_ref (proc_frame->progress);

          pika_plug_in_progress_attach (proc_frame->progress);
        }
    }

  if (proc_frame->progress)
    {
      if (! proc_frame->progress_cancel_id)
        {
          g_object_add_weak_pointer (G_OBJECT (proc_frame->progress),
                                     (gpointer) &proc_frame->progress);

          proc_frame->progress_cancel_id =
            g_signal_connect (proc_frame->progress, "cancel",
                              G_CALLBACK (pika_plug_in_progress_cancel_callback),
                              plug_in);
        }

      if (pika_progress_is_active (proc_frame->progress))
        {
          if (message)
            pika_progress_set_text_literal (proc_frame->progress, message);

          if (pika_progress_get_value (proc_frame->progress) > 0.0)
            pika_progress_set_value (proc_frame->progress, 0.0);
        }
      else
        {
          pika_progress_start (proc_frame->progress, TRUE,
                               "%s", message ? message : "");
        }
    }
}

void
pika_plug_in_progress_end (PikaPlugIn          *plug_in,
                           PikaPlugInProcFrame *proc_frame)
{
  g_return_if_fail (PIKA_IS_PLUG_IN (plug_in));
  g_return_if_fail (proc_frame != NULL);

  if (proc_frame->progress)
    {
      if (proc_frame->progress_cancel_id)
        {
          g_signal_handler_disconnect (proc_frame->progress,
                                       proc_frame->progress_cancel_id);
          proc_frame->progress_cancel_id = 0;

          g_object_remove_weak_pointer (G_OBJECT (proc_frame->progress),
                                        (gpointer) &proc_frame->progress);
        }

      if (pika_plug_in_progress_detach (proc_frame->progress) < 1 &&
          pika_progress_is_active (proc_frame->progress))
        {
          pika_progress_end (proc_frame->progress);
        }

      if (proc_frame->progress_created)
        {
          pika_free_progress (plug_in->manager->pika, proc_frame->progress);
          g_clear_object (&proc_frame->progress);
        }
    }
}

void
pika_plug_in_progress_set_text (PikaPlugIn  *plug_in,
                                const gchar *message)
{
  PikaPlugInProcFrame *proc_frame;

  g_return_if_fail (PIKA_IS_PLUG_IN (plug_in));

  proc_frame = pika_plug_in_get_proc_frame (plug_in);

  if (proc_frame->progress)
    pika_progress_set_text_literal (proc_frame->progress, message);
}

void
pika_plug_in_progress_set_value (PikaPlugIn *plug_in,
                                 gdouble     percentage)
{
  PikaPlugInProcFrame *proc_frame;

  g_return_if_fail (PIKA_IS_PLUG_IN (plug_in));

  proc_frame = pika_plug_in_get_proc_frame (plug_in);

  if (! proc_frame->progress                           ||
      ! pika_progress_is_active (proc_frame->progress) ||
      ! proc_frame->progress_cancel_id)
    {
      pika_plug_in_progress_start (plug_in, NULL, NULL);
    }

  if (proc_frame->progress && pika_progress_is_active (proc_frame->progress))
    pika_progress_set_value (proc_frame->progress, percentage);
}

void
pika_plug_in_progress_pulse (PikaPlugIn *plug_in)
{
  PikaPlugInProcFrame *proc_frame;

  g_return_if_fail (PIKA_IS_PLUG_IN (plug_in));

  proc_frame = pika_plug_in_get_proc_frame (plug_in);

  if (! proc_frame->progress                           ||
      ! pika_progress_is_active (proc_frame->progress) ||
      ! proc_frame->progress_cancel_id)
    {
      pika_plug_in_progress_start (plug_in, NULL, NULL);
    }

  if (proc_frame->progress && pika_progress_is_active (proc_frame->progress))
    pika_progress_pulse (proc_frame->progress);
}

GBytes *
pika_plug_in_progress_get_window_id (PikaPlugIn *plug_in)
{
  PikaPlugInProcFrame *proc_frame;

  g_return_val_if_fail (PIKA_IS_PLUG_IN (plug_in), 0);

  proc_frame = pika_plug_in_get_proc_frame (plug_in);

  if (proc_frame->progress)
    return pika_progress_get_window_id (proc_frame->progress);

  return 0;
}

gboolean
pika_plug_in_progress_install (PikaPlugIn  *plug_in,
                               const gchar *progress_callback)
{
  PikaPlugInProcFrame *proc_frame;
  PikaProcedure       *procedure;

  g_return_val_if_fail (PIKA_IS_PLUG_IN (plug_in), FALSE);
  g_return_val_if_fail (progress_callback != NULL, FALSE);

  procedure = pika_pdb_lookup_procedure (plug_in->manager->pika->pdb,
                                         progress_callback);

  if (! PIKA_IS_TEMPORARY_PROCEDURE (procedure)                ||
      PIKA_TEMPORARY_PROCEDURE (procedure)->plug_in != plug_in ||
      procedure->num_args                           != 3       ||
      ! G_IS_PARAM_SPEC_INT    (procedure->args[0])            ||
      ! G_IS_PARAM_SPEC_STRING (procedure->args[1])            ||
      ! G_IS_PARAM_SPEC_DOUBLE (procedure->args[2]))
    {
      return FALSE;
    }

  proc_frame = pika_plug_in_get_proc_frame (plug_in);

  if (proc_frame->progress)
    {
      pika_plug_in_progress_end (plug_in, proc_frame);

      g_clear_object (&proc_frame->progress);
    }

  proc_frame->progress = g_object_new (PIKA_TYPE_PDB_PROGRESS,
                                       "pdb",           plug_in->manager->pika->pdb,
                                       "context",       proc_frame->main_context,
                                       "callback-name", progress_callback,
                                       NULL);

  pika_plug_in_progress_attach (proc_frame->progress);

  return TRUE;
}

gboolean
pika_plug_in_progress_uninstall (PikaPlugIn  *plug_in,
                                 const gchar *progress_callback)
{
  PikaPlugInProcFrame *proc_frame;

  g_return_val_if_fail (PIKA_IS_PLUG_IN (plug_in), FALSE);
  g_return_val_if_fail (progress_callback != NULL, FALSE);

  proc_frame = pika_plug_in_get_proc_frame (plug_in);

  if (PIKA_IS_PDB_PROGRESS (proc_frame->progress))
    {
      pika_plug_in_progress_end (plug_in, proc_frame);

      g_clear_object (&proc_frame->progress);

      return TRUE;
    }

  return FALSE;
}

gboolean
pika_plug_in_progress_cancel (PikaPlugIn  *plug_in,
                              const gchar *progress_callback)
{
  g_return_val_if_fail (PIKA_IS_PLUG_IN (plug_in), FALSE);
  g_return_val_if_fail (progress_callback != NULL, FALSE);

  return FALSE;
}


/*  private functions  */

static PikaValueArray *
get_cancel_return_values (PikaProcedure *procedure)
{
  PikaValueArray *return_vals;
  GError         *error;

  error = g_error_new_literal (PIKA_PDB_ERROR, PIKA_PDB_ERROR_CANCELLED,
                               _("Cancelled"));
  return_vals = pika_procedure_get_return_values (procedure, FALSE, error);
  g_error_free (error);

  return return_vals;
}

static void
pika_plug_in_progress_cancel_callback (PikaProgress *progress,
                                       PikaPlugIn   *plug_in)
{
  PikaPlugInProcFrame *proc_frame = &plug_in->main_proc_frame;
  GList               *list;

  if (proc_frame->main_loop)
    {
      proc_frame->return_vals =
        get_cancel_return_values (proc_frame->procedure);
    }

  for (list = plug_in->temp_proc_frames; list; list = g_list_next (list))
    {
      proc_frame = list->data;

      if (proc_frame->main_loop)
        {
          proc_frame->return_vals =
            get_cancel_return_values (proc_frame->procedure);
        }
    }

  pika_plug_in_close (plug_in, TRUE);
}
