/* 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
 *
 * pikatoolgroup.c
 * Copyright (C) 2020 Ell
 *
 * 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>

#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>

#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"

#include "core-types.h"

#include "pikalist.h"
#include "pikatoolgroup.h"
#include "pikatoolinfo.h"

#include "pika-intl.h"


enum
{
  ACTIVE_TOOL_CHANGED,
  LAST_SIGNAL
};

enum
{
  PROP_0,
  PROP_ACTIVE_TOOL,
  PROP_CHILDREN
};


struct _PikaToolGroupPrivate
{
  gchar         *active_tool;
  PikaContainer *children;
};


/*  local function prototypes  */


static void            pika_tool_group_finalize        (GObject        *object);
static void            pika_tool_group_get_property    (GObject        *object,
                                                        guint           property_id,
                                                        GValue         *value,
                                                        GParamSpec     *pspec);
static void            pika_tool_group_set_property    (GObject        *object,
                                                        guint           property_id,
                                                        const GValue   *value,
                                                        GParamSpec     *pspec);

static gint64          pika_tool_group_get_memsize     (PikaObject     *object,
                                                        gint64         *gui_size);

static gchar         * pika_tool_group_get_description (PikaViewable   *viewable,
                                                        gchar         **tooltip);
static PikaContainer * pika_tool_group_get_children    (PikaViewable   *viewable);
static void            pika_tool_group_set_expanded    (PikaViewable   *viewable,
                                                        gboolean        expand);
static gboolean        pika_tool_group_get_expanded    (PikaViewable   *viewable);

static void            pika_tool_group_child_add       (PikaContainer  *container,
                                                        PikaToolInfo   *tool_info,
                                                        PikaToolGroup  *tool_group);
static void            pika_tool_group_child_remove    (PikaContainer  *container,
                                                        PikaToolInfo   *tool_info,
                                                        PikaToolGroup  *tool_group);

static void            pika_tool_group_shown_changed   (PikaToolItem   *tool_item);


G_DEFINE_TYPE_WITH_PRIVATE (PikaToolGroup, pika_tool_group, PIKA_TYPE_TOOL_ITEM)

#define parent_class pika_tool_group_parent_class

static guint pika_tool_group_signals[LAST_SIGNAL] = { 0 };


/*  private functions  */

static void
pika_tool_group_class_init (PikaToolGroupClass *klass)
{
  GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
  PikaObjectClass   *pika_object_class = PIKA_OBJECT_CLASS (klass);
  PikaViewableClass *viewable_class    = PIKA_VIEWABLE_CLASS (klass);
  PikaToolItemClass *tool_item_class   = PIKA_TOOL_ITEM_CLASS (klass);

  pika_tool_group_signals[ACTIVE_TOOL_CHANGED] =
    g_signal_new ("active-tool-changed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (PikaToolGroupClass, active_tool_changed),
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);

  object_class->finalize            = pika_tool_group_finalize;
  object_class->get_property        = pika_tool_group_get_property;
  object_class->set_property        = pika_tool_group_set_property;

  pika_object_class->get_memsize    = pika_tool_group_get_memsize;

  viewable_class->default_icon_name = "folder";
  viewable_class->get_description   = pika_tool_group_get_description;
  viewable_class->get_children      = pika_tool_group_get_children;
  viewable_class->get_expanded      = pika_tool_group_get_expanded;
  viewable_class->set_expanded      = pika_tool_group_set_expanded;

  tool_item_class->shown_changed    = pika_tool_group_shown_changed;

  PIKA_CONFIG_PROP_STRING (object_class, PROP_ACTIVE_TOOL,
                           "active-tool", NULL, NULL,
                           NULL,
                           PIKA_PARAM_STATIC_STRINGS);

  PIKA_CONFIG_PROP_OBJECT (object_class, PROP_CHILDREN,
                           "children", NULL, NULL,
                           PIKA_TYPE_CONTAINER,
                           PIKA_PARAM_STATIC_STRINGS |
                           PIKA_CONFIG_PARAM_AGGREGATE);
}

static void
pika_tool_group_init (PikaToolGroup *tool_group)
{
  tool_group->priv = pika_tool_group_get_instance_private (tool_group);

  tool_group->priv->children = g_object_new (
    PIKA_TYPE_LIST,
    "children-type", PIKA_TYPE_TOOL_INFO,
    "append",        TRUE,
    NULL);

  g_signal_connect (tool_group->priv->children, "add",
                    G_CALLBACK (pika_tool_group_child_add),
                    tool_group);
  g_signal_connect (tool_group->priv->children, "remove",
                    G_CALLBACK (pika_tool_group_child_remove),
                    tool_group);
}

static void
pika_tool_group_finalize (GObject *object)
{
  PikaToolGroup *tool_group = PIKA_TOOL_GROUP (object);

  g_clear_pointer (&tool_group->priv->active_tool, g_free);

  g_clear_object (&tool_group->priv->children);

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

static void
pika_tool_group_get_property (GObject    *object,
                              guint       property_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
  PikaToolGroup *tool_group = PIKA_TOOL_GROUP (object);

  switch (property_id)
    {
    case PROP_ACTIVE_TOOL:
      g_value_set_string (value, tool_group->priv->active_tool);
      break;

    case PROP_CHILDREN:
      g_value_set_object (value, tool_group->priv->children);
      break;

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

static void
pika_tool_group_set_property_add_tool (PikaToolInfo  *tool_info,
                                       PikaToolGroup *tool_group)
{
  pika_container_add (tool_group->priv->children, PIKA_OBJECT (tool_info));
}

static void
pika_tool_group_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  PikaToolGroup *tool_group = PIKA_TOOL_GROUP (object);

  switch (property_id)
    {
    case PROP_ACTIVE_TOOL:
      g_free (tool_group->priv->active_tool);

      tool_group->priv->active_tool = g_value_dup_string (value);
      break;

    case PROP_CHILDREN:
      {
        PikaContainer *container = g_value_get_object (value);

        pika_container_clear (tool_group->priv->children);

        if (! container)
          break;

        pika_container_foreach (container,
                                (GFunc) pika_tool_group_set_property_add_tool,
                                tool_group);
      }
      break;

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

static gint64
pika_tool_group_get_memsize (PikaObject *object,
                             gint64     *gui_size)
{
  PikaToolGroup *tool_group = PIKA_TOOL_GROUP (object);
  gint64         memsize = 0;

  memsize += pika_object_get_memsize (PIKA_OBJECT (tool_group->priv->children),
                                      gui_size);

  return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}

static gchar *
pika_tool_group_get_description (PikaViewable  *viewable,
                                 gchar        **tooltip)
{
  /* Translators: this is a noun */
  return g_strdup (C_("tool-item", "Group"));
}

static PikaContainer *
pika_tool_group_get_children (PikaViewable *viewable)
{
  PikaToolGroup *tool_group = PIKA_TOOL_GROUP (viewable);

  return tool_group->priv->children;
}

static void
pika_tool_group_set_expanded (PikaViewable *viewable,
                              gboolean      expand)
{
  if (! expand)
    pika_viewable_expanded_changed (viewable);
}

static gboolean
pika_tool_group_get_expanded (PikaViewable *viewable)
{
  return TRUE;
}

static void
pika_tool_group_child_add (PikaContainer *container,
                           PikaToolInfo  *tool_info,
                           PikaToolGroup *tool_group)
{
  g_return_if_fail (
    pika_viewable_get_parent (PIKA_VIEWABLE (tool_info)) == NULL);

  pika_viewable_set_parent (PIKA_VIEWABLE (tool_info),
                            PIKA_VIEWABLE (tool_group));

  if (! tool_group->priv->active_tool)
    pika_tool_group_set_active_tool_info (tool_group, tool_info);
}

static void
pika_tool_group_child_remove (PikaContainer *container,
                              PikaToolInfo  *tool_info,
                              PikaToolGroup *tool_group)
{
  pika_viewable_set_parent (PIKA_VIEWABLE (tool_info), NULL);

  if (! g_strcmp0 (tool_group->priv->active_tool,
                   pika_object_get_name (PIKA_OBJECT (tool_info))))
    {
      PikaToolInfo *active_tool_info = NULL;

      if (! pika_container_is_empty (tool_group->priv->children))
        {
          active_tool_info = PIKA_TOOL_INFO (
            pika_container_get_first_child (tool_group->priv->children));
        }

      pika_tool_group_set_active_tool_info (tool_group, active_tool_info);
    }
}

static void
pika_tool_group_shown_changed (PikaToolItem *tool_item)
{
  PikaToolGroup *tool_group = PIKA_TOOL_GROUP (tool_item);
  GList         *iter;

  if (PIKA_TOOL_ITEM_CLASS (parent_class)->shown_changed)
    PIKA_TOOL_ITEM_CLASS (parent_class)->shown_changed (tool_item);

  for (iter = PIKA_LIST (tool_group->priv->children)->queue->head;
       iter;
       iter = g_list_next (iter))
    {
      PikaToolItem *tool_item = iter->data;

      if (pika_tool_item_get_visible (tool_item))
        pika_tool_item_shown_changed (tool_item);
    }
}


/*  public functions  */

PikaToolGroup *
pika_tool_group_new (void)
{
  PikaToolGroup *tool_group;

  tool_group = g_object_new (PIKA_TYPE_TOOL_GROUP, NULL);

  pika_object_set_static_name (PIKA_OBJECT (tool_group), "tool group");

  return tool_group;
}

void
pika_tool_group_set_active_tool (PikaToolGroup *tool_group,
                                 const gchar   *tool_name)
{
  g_return_if_fail (PIKA_IS_TOOL_GROUP (tool_group));

  if (g_strcmp0 (tool_group->priv->active_tool, tool_name))
    {
      g_return_if_fail (tool_name == NULL ||
                        pika_container_get_child_by_name (
                          tool_group->priv->children, tool_name) != NULL);

      g_free (tool_group->priv->active_tool);

      tool_group->priv->active_tool = g_strdup (tool_name);;

      g_signal_emit (tool_group,
                     pika_tool_group_signals[ACTIVE_TOOL_CHANGED], 0);

      g_object_notify (G_OBJECT (tool_group), "active-tool");
    }
}

const gchar *
pika_tool_group_get_active_tool (PikaToolGroup *tool_group)
{
  g_return_val_if_fail (PIKA_IS_TOOL_GROUP (tool_group), NULL);

  return tool_group->priv->active_tool;
}

void
pika_tool_group_set_active_tool_info (PikaToolGroup *tool_group,
                                      PikaToolInfo  *tool_info)
{
  g_return_if_fail (PIKA_IS_TOOL_GROUP (tool_group));
  g_return_if_fail (tool_info == NULL || PIKA_IS_TOOL_INFO (tool_info));

  pika_tool_group_set_active_tool (
    tool_group,
    tool_info ? pika_object_get_name (PIKA_OBJECT (tool_info)) : NULL);
}

PikaToolInfo *
pika_tool_group_get_active_tool_info (PikaToolGroup *tool_group)
{
  g_return_val_if_fail (PIKA_IS_TOOL_GROUP (tool_group), NULL);

  return PIKA_TOOL_INFO (
    pika_container_get_child_by_name (tool_group->priv->children,
                                      tool_group->priv->active_tool));
}
