/* 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
 *
 * 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 <glib-object.h>

#include "core-types.h"

#include "core/pikasubprogress.h"
#include "core/pikaprogress.h"


enum
{
  PROP_0,
  PROP_PROGRESS
};


static void           pika_sub_progress_iface_init    (PikaProgressInterface *iface);

static void           pika_sub_progress_finalize      (GObject             *object);
static void           pika_sub_progress_set_property  (GObject             *object,
                                                       guint                property_id,
                                                       const GValue        *value,
                                                       GParamSpec          *pspec);
static void           pika_sub_progress_get_property  (GObject             *object,
                                                       guint                property_id,
                                                       GValue              *value,
                                                       GParamSpec          *pspec);

static PikaProgress * pika_sub_progress_start         (PikaProgress        *progress,
                                                       gboolean             cancellable,
                                                       const gchar         *message);
static void           pika_sub_progress_end           (PikaProgress        *progress);
static gboolean       pika_sub_progress_is_active     (PikaProgress        *progress);
static void           pika_sub_progress_set_text      (PikaProgress        *progress,
                                                       const gchar         *message);
static void           pika_sub_progress_set_value     (PikaProgress        *progress,
                                                       gdouble              percentage);
static gdouble        pika_sub_progress_get_value     (PikaProgress        *progress);
static void           pika_sub_progress_pulse         (PikaProgress        *progress);
static GBytes       * pika_sub_progress_get_window_id (PikaProgress        *progress);
static gboolean       pika_sub_progress_message       (PikaProgress        *progress,
                                                       Pika                *pika,
                                                       PikaMessageSeverity  severity,
                                                       const gchar         *domain,
                                                       const gchar         *message);


G_DEFINE_TYPE_WITH_CODE (PikaSubProgress, pika_sub_progress, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (PIKA_TYPE_PROGRESS,
                                                pika_sub_progress_iface_init))

#define parent_class pika_sub_progress_parent_class


static void
pika_sub_progress_class_init (PikaSubProgressClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize     = pika_sub_progress_finalize;
  object_class->set_property = pika_sub_progress_set_property;
  object_class->get_property = pika_sub_progress_get_property;

  g_object_class_install_property (object_class, PROP_PROGRESS,
                                   g_param_spec_object ("progress",
                                                        NULL, NULL,
                                                        PIKA_TYPE_PROGRESS,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY));
}

static void
pika_sub_progress_init (PikaSubProgress *sub)
{
  sub->progress = NULL;
  sub->start    = 0.0;
  sub->end      = 1.0;
}

static void
pika_sub_progress_finalize (GObject *object)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (object);

  g_clear_object (&sub->progress);

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

static void
pika_sub_progress_iface_init (PikaProgressInterface *iface)
{
  iface->start         = pika_sub_progress_start;
  iface->end           = pika_sub_progress_end;
  iface->is_active     = pika_sub_progress_is_active;
  iface->set_text      = pika_sub_progress_set_text;
  iface->set_value     = pika_sub_progress_set_value;
  iface->get_value     = pika_sub_progress_get_value;
  iface->pulse         = pika_sub_progress_pulse;
  iface->get_window_id = pika_sub_progress_get_window_id;
  iface->message       = pika_sub_progress_message;
}

static void
pika_sub_progress_set_property (GObject      *object,
                                guint         property_id,
                                const GValue *value,
                                GParamSpec   *pspec)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (object);

  switch (property_id)
    {
    case PROP_PROGRESS:
      g_return_if_fail (sub->progress == NULL);
      sub->progress = g_value_dup_object (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
pika_sub_progress_get_property (GObject    *object,
                                guint       property_id,
                                GValue     *value,
                                GParamSpec *pspec)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (object);

  switch (property_id)
    {
    case PROP_PROGRESS:
      g_value_set_object (value, sub->progress);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static PikaProgress *
pika_sub_progress_start (PikaProgress *progress,
                         gboolean      cancellable,
                         const gchar  *message)
{
  /* does nothing */
  return NULL;
}

static void
pika_sub_progress_end (PikaProgress *progress)
{
  /* does nothing */
}

static gboolean
pika_sub_progress_is_active (PikaProgress *progress)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (progress);

  if (sub->progress)
    return pika_progress_is_active (sub->progress);

  return FALSE;
}

static void
pika_sub_progress_set_text (PikaProgress *progress,
                            const gchar  *message)
{
  /* does nothing */
}

static void
pika_sub_progress_set_value (PikaProgress *progress,
                             gdouble       percentage)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (progress);

  if (sub->progress)
    pika_progress_set_value (sub->progress,
                             sub->start + percentage * (sub->end - sub->start));
}

static gdouble
pika_sub_progress_get_value (PikaProgress *progress)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (progress);

  if (sub->progress)
    return pika_progress_get_value (sub->progress);

  return 0.0;
}

static void
pika_sub_progress_pulse (PikaProgress *progress)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (progress);

  if (sub->progress)
    pika_progress_pulse (sub->progress);
}

static GBytes *
pika_sub_progress_get_window_id (PikaProgress *progress)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (progress);

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

  return NULL;
}

static gboolean
pika_sub_progress_message (PikaProgress        *progress,
                           Pika                *pika,
                           PikaMessageSeverity  severity,
                           const gchar         *domain,
                           const gchar         *message)
{
  PikaSubProgress *sub = PIKA_SUB_PROGRESS (progress);

  if (sub->progress)
    return pika_progress_message (sub->progress,
                                  pika, severity, domain, message);

  return FALSE;
}

/**
 * pika_sub_progress_new:
 * @progress: parent progress or %NULL
 *
 * PikaSubProgress implements the PikaProgress interface and can be
 * used wherever a PikaProgress is needed. It maps progress
 * information to a sub-range of its parent @progress. This is useful
 * when an action breaks down into multiple sub-actions that itself
 * need a #PikaProgress pointer. See pika_image_scale() for an example.
 *
 * Returns: a new #PikaProgress object
 */
PikaProgress *
pika_sub_progress_new (PikaProgress *progress)
{
  PikaSubProgress *sub;

  g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), NULL);

  sub = g_object_new (PIKA_TYPE_SUB_PROGRESS,
                      "progress", progress,
                      NULL);

  return PIKA_PROGRESS (sub);
}

/**
 * pika_sub_progress_set_range:
 * @start: start value of range on the parent process
 * @end:   end value of range on the parent process
 *
 * Sets a range on the parent progress that this @progress should be
 * mapped to.
 */
void
pika_sub_progress_set_range (PikaSubProgress *progress,
                             gdouble          start,
                             gdouble          end)
{
  g_return_if_fail (PIKA_IS_SUB_PROGRESS (progress));
  g_return_if_fail (start < end);

  progress->start = start;
  progress->end   = end;
}

/**
 * pika_sub_progress_set_step:
 * @index:     step index
 * @num_steps: number of steps
 *
 * A more convenient form of pika_sub_progress_set_range().
 */
void
pika_sub_progress_set_step (PikaSubProgress *progress,
                            gint             index,
                            gint             num_steps)
{
  g_return_if_fail (PIKA_IS_SUB_PROGRESS (progress));
  g_return_if_fail (index < num_steps && num_steps > 0);

  progress->start = (gdouble) index / num_steps;
  progress->end   = (gdouble) (index + 1) / num_steps;
}
