/* 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
 *
 * pikachanneltreeview.c
 * Copyright (C) 2001-2009 Michael Natterer <mitch@gimp.org>
 *
 * 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 <gegl.h>
#include <gtk/gtk.h>

#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"

#include "widgets-types.h"

#include "core/pikachannel.h"
#include "core/pikacontainer.h"
#include "core/pikaimage.h"
#include "core/pikaimage-undo.h"
#include "core/pikalayer.h"
#include "core/pikalayermask.h"

#include "pikaactiongroup.h"
#include "pikachanneltreeview.h"
#include "pikacomponenteditor.h"
#include "pikacontainerview.h"
#include "pikadnd.h"
#include "pikadocked.h"
#include "pikahelp-ids.h"
#include "pikauimanager.h"
#include "pikawidgets-utils.h"

#include "pika-intl.h"


struct _PikaChannelTreeViewPrivate
{
  GtkWidget *component_editor;

  GtkWidget *toselection_button;
};


static void  pika_channel_tree_view_view_iface_init   (PikaContainerViewInterface *iface);

static void   pika_channel_tree_view_constructed      (GObject                 *object);

static void   pika_channel_tree_view_drop_viewables   (PikaContainerTreeView   *view,
                                                       GList                   *src_viewables,
                                                       PikaViewable            *dest_viewable,
                                                       GtkTreeViewDropPosition  drop_pos);
static void   pika_channel_tree_view_drop_component   (PikaContainerTreeView   *tree_view,
                                                       PikaImage               *image,
                                                       PikaChannelType          component,
                                                       PikaViewable            *dest_viewable,
                                                       GtkTreeViewDropPosition  drop_pos);
static void   pika_channel_tree_view_set_image        (PikaItemTreeView        *item_view,
                                                       PikaImage               *image);
static PikaItem * pika_channel_tree_view_item_new     (PikaImage               *image);

static void   pika_channel_tree_view_set_context      (PikaContainerView       *view,
                                                       PikaContext             *context);
static void   pika_channel_tree_view_set_view_size    (PikaContainerView       *view);


G_DEFINE_TYPE_WITH_CODE (PikaChannelTreeView, pika_channel_tree_view,
                         PIKA_TYPE_DRAWABLE_TREE_VIEW,
                         G_ADD_PRIVATE (PikaChannelTreeView)
                         G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
                                                pika_channel_tree_view_view_iface_init))

#define parent_class pika_channel_tree_view_parent_class

static PikaContainerViewInterface *parent_view_iface = NULL;


static void
pika_channel_tree_view_class_init (PikaChannelTreeViewClass *klass)
{
  GObjectClass               *object_class = G_OBJECT_CLASS (klass);
  PikaContainerTreeViewClass *view_class   = PIKA_CONTAINER_TREE_VIEW_CLASS (klass);
  PikaItemTreeViewClass      *iv_class     = PIKA_ITEM_TREE_VIEW_CLASS (klass);

  object_class->constructed  = pika_channel_tree_view_constructed;

  view_class->drop_viewables = pika_channel_tree_view_drop_viewables;
  view_class->drop_component = pika_channel_tree_view_drop_component;

  iv_class->set_image        = pika_channel_tree_view_set_image;

  iv_class->item_type        = PIKA_TYPE_CHANNEL;
  iv_class->signal_name      = "selected-channels-changed";

  iv_class->get_container    = pika_image_get_channels;
  iv_class->get_selected_items = (PikaGetItemsFunc) pika_image_get_selected_channels;
  iv_class->set_selected_items = (PikaSetItemsFunc) pika_image_set_selected_channels;
  iv_class->add_item         = (PikaAddItemFunc) pika_image_add_channel;
  iv_class->remove_item      = (PikaRemoveItemFunc) pika_image_remove_channel;
  iv_class->new_item         = pika_channel_tree_view_item_new;

  iv_class->action_group          = "channels";
  iv_class->activate_action       = "channels-edit-attributes";
  iv_class->new_action            = "channels-new";
  iv_class->new_default_action    = "channels-new-last-values";
  iv_class->raise_action          = "channels-raise";
  iv_class->raise_top_action      = "channels-raise-to-top";
  iv_class->lower_action          = "channels-lower";
  iv_class->lower_bottom_action   = "channels-lower-to-bottom";
  iv_class->duplicate_action      = "channels-duplicate";
  iv_class->delete_action         = "channels-delete";
  iv_class->lock_content_help_id  = PIKA_HELP_CHANNEL_LOCK_PIXELS;
  iv_class->lock_position_help_id = PIKA_HELP_CHANNEL_LOCK_POSITION;
}

static void
pika_channel_tree_view_view_iface_init (PikaContainerViewInterface *view_iface)
{
  parent_view_iface = g_type_interface_peek_parent (view_iface);

  view_iface->set_context   = pika_channel_tree_view_set_context;
  view_iface->set_view_size = pika_channel_tree_view_set_view_size;
}

static void
pika_channel_tree_view_init (PikaChannelTreeView *view)
{
  view->priv = pika_channel_tree_view_get_instance_private (view);

  view->priv->component_editor   = NULL;
  view->priv->toselection_button = NULL;
}

static void
pika_channel_tree_view_constructed (GObject *object)
{
  PikaChannelTreeView   *view      = PIKA_CHANNEL_TREE_VIEW (object);
  PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (object);
  GdkModifierType        extend_mask;
  GdkModifierType        modify_mask;

  G_OBJECT_CLASS (parent_class)->constructed (object);

  extend_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object),
                                              GDK_MODIFIER_INTENT_EXTEND_SELECTION);
  modify_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object),
                                              GDK_MODIFIER_INTENT_MODIFY_SELECTION);

  pika_dnd_viewable_dest_add  (GTK_WIDGET (tree_view->view), PIKA_TYPE_LAYER,
                               NULL, tree_view);
  pika_dnd_viewable_dest_add  (GTK_WIDGET (tree_view->view), PIKA_TYPE_LAYER_MASK,
                               NULL, tree_view);
  pika_dnd_component_dest_add (GTK_WIDGET (tree_view->view),
                               NULL, tree_view);

  view->priv->toselection_button =
    pika_editor_add_action_button (PIKA_EDITOR (view), "channels",
                                   "channels-selection-replace",
                                   "channels-selection-add",
                                   extend_mask,
                                   "channels-selection-subtract",
                                   modify_mask,
                                   "channels-selection-intersect",
                                   extend_mask | modify_mask,
                                   NULL);
  pika_container_view_enable_dnd (PIKA_CONTAINER_VIEW (view),
                                  GTK_BUTTON (view->priv->toselection_button),
                                  PIKA_TYPE_CHANNEL);
  gtk_box_reorder_child (pika_editor_get_button_box (PIKA_EDITOR (view)),
                         view->priv->toselection_button, 4);
}


/*  PikaContainerTreeView methods  */

static void
pika_channel_tree_view_drop_viewables (PikaContainerTreeView   *tree_view,
                                       GList                   *src_viewables,
                                       PikaViewable            *dest_viewable,
                                       GtkTreeViewDropPosition  drop_pos)
{
  PikaItemTreeView      *item_view = PIKA_ITEM_TREE_VIEW (tree_view);
  PikaImage             *image     = pika_item_tree_view_get_image (item_view);
  PikaItemTreeViewClass *item_view_class;
  GList                 *iter;

  item_view_class = PIKA_ITEM_TREE_VIEW_GET_CLASS (item_view);

  for (iter = src_viewables; iter; iter = iter->next)
    {
      PikaViewable *src_viewable = iter->data;

      if (PIKA_IS_DRAWABLE (src_viewable) &&
          (image != pika_item_get_image (PIKA_ITEM (src_viewable)) ||
           G_TYPE_FROM_INSTANCE (src_viewable) != item_view_class->item_type))
        {
          PikaItem *new_item;
          PikaItem *parent;
          gint      index;

          index = pika_item_tree_view_get_drop_index (item_view, dest_viewable,
                                                      drop_pos,
                                                      (PikaViewable **) &parent);

          new_item = pika_item_convert (PIKA_ITEM (src_viewable),
                                        pika_item_tree_view_get_image (item_view),
                                        item_view_class->item_type);

          item_view_class->add_item (image, new_item, parent, index, TRUE);

          pika_image_flush (image);

          return;
        }
    }

  PIKA_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewables (tree_view,
                                                                 src_viewables,
                                                                 dest_viewable,
                                                                 drop_pos);
}

static void
pika_channel_tree_view_drop_component (PikaContainerTreeView   *tree_view,
                                       PikaImage               *src_image,
                                       PikaChannelType          component,
                                       PikaViewable            *dest_viewable,
                                       GtkTreeViewDropPosition  drop_pos)
{
  PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (tree_view);
  PikaImage        *image     = pika_item_tree_view_get_image (item_view);
  PikaItem         *new_item;
  PikaChannel      *parent;
  gint              index;
  const gchar      *desc;
  gchar            *name;

  index = pika_item_tree_view_get_drop_index (item_view, dest_viewable,
                                              drop_pos,
                                              (PikaViewable **) &parent);

  pika_enum_get_value (PIKA_TYPE_CHANNEL_TYPE, component,
                       NULL, NULL, &desc, NULL);
  name = g_strdup_printf (_("%s Channel Copy"), desc);

  new_item = PIKA_ITEM (pika_channel_new_from_component (src_image, component,
                                                         name, NULL));

  /*  copied components are invisible by default so subsequent copies
   *  of components don't affect each other
   */
  pika_item_set_visible (new_item, FALSE, FALSE);

  g_free (name);

  if (src_image != image)
    PIKA_ITEM_GET_CLASS (new_item)->convert (new_item, image,
                                             PIKA_TYPE_CHANNEL);

  pika_image_add_channel (image, PIKA_CHANNEL (new_item), parent, index, TRUE);

  pika_image_flush (image);
}


/*  PikaItemTreeView methods  */

static void
pika_channel_tree_view_set_image (PikaItemTreeView *item_view,
                                  PikaImage        *image)
{
  PikaChannelTreeView *channel_view = PIKA_CHANNEL_TREE_VIEW (item_view);

  if (! channel_view->priv->component_editor)
    {
      PikaContainerView *view = PIKA_CONTAINER_VIEW (item_view);
      gint               view_size;

      view_size = pika_container_view_get_view_size (view, NULL);

      channel_view->priv->component_editor =
        pika_component_editor_new (view_size,
                                   pika_editor_get_menu_factory (PIKA_EDITOR (item_view)));
      pika_docked_set_context (PIKA_DOCKED (channel_view->priv->component_editor),
                               pika_container_view_get_context (view));
      gtk_box_pack_start (GTK_BOX (item_view), channel_view->priv->component_editor,
                          FALSE, FALSE, 0);
      gtk_box_reorder_child (GTK_BOX (item_view),
                             channel_view->priv->component_editor, 0);
    }

  if (! image)
    gtk_widget_hide (channel_view->priv->component_editor);

  pika_image_editor_set_image (PIKA_IMAGE_EDITOR (channel_view->priv->component_editor),
                               image);

  PIKA_ITEM_TREE_VIEW_CLASS (parent_class)->set_image (item_view, image);

  if (pika_item_tree_view_get_image (item_view))
    gtk_widget_show (channel_view->priv->component_editor);
}

static PikaItem *
pika_channel_tree_view_item_new (PikaImage *image)
{
  PikaChannel *new_channel;
  PikaRGB      color;

  pika_rgba_set (&color, 0.0, 0.0, 0.0, 0.5);

  pika_image_undo_group_start (image, PIKA_UNDO_GROUP_EDIT_PASTE,
                               _("New Channel"));

  new_channel = pika_channel_new (image,
                                  pika_image_get_width (image),
                                  pika_image_get_height (image),
                                  _("Channel"), &color);

  pika_image_add_channel (image, new_channel,
                          PIKA_IMAGE_ACTIVE_PARENT, -1, TRUE);

  pika_image_undo_group_end (image);

  return PIKA_ITEM (new_channel);
}


/*  PikaContainerView methods  */

static void
pika_channel_tree_view_set_context (PikaContainerView *view,
                                    PikaContext       *context)
{
  PikaChannelTreeView *channel_view = PIKA_CHANNEL_TREE_VIEW (view);

  parent_view_iface->set_context (view, context);

  if (channel_view->priv->component_editor)
    pika_docked_set_context (PIKA_DOCKED (channel_view->priv->component_editor),
                             context);
}

static void
pika_channel_tree_view_set_view_size (PikaContainerView *view)
{
  PikaChannelTreeView *channel_view = PIKA_CHANNEL_TREE_VIEW (view);
  gint                 view_size;

  parent_view_iface->set_view_size (view);

  view_size = pika_container_view_get_view_size (view, NULL);

  if (channel_view->priv->component_editor)
    pika_component_editor_set_view_size (PIKA_COMPONENT_EDITOR (channel_view->priv->component_editor),
                                         view_size);
}
