/* 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
 *
 * pikadrawabletreeview.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 "libpikawidgets/pikawidgets.h"

#include "widgets-types.h"

#include "core/pika.h"
#include "core/pikacontext.h"
#include "core/pikadrawable.h"
#include "core/pikadrawable-edit.h"
#include "core/pikafilloptions.h"
#include "core/pikaimage.h"
#include "core/pikaimage-undo.h"
#include "core/pikapattern.h"

#include "pikacontainerview.h"
#include "pikadnd.h"
#include "pikadrawabletreeview.h"

#include "pika-intl.h"


static void   pika_drawable_tree_view_view_iface_init (PikaContainerViewInterface *iface);

static void     pika_drawable_tree_view_constructed   (GObject           *object);

static gboolean pika_drawable_tree_view_select_items  (PikaContainerView *view,
                                                       GList             *items,
                                                       GList             *paths);

static gboolean pika_drawable_tree_view_drop_possible(PikaContainerTreeView *view,
                                                      PikaDndType          src_type,
                                                      GList               *src_viewables,
                                                      PikaViewable        *dest_viewable,
                                                      GtkTreePath         *drop_path,
                                                      GtkTreeViewDropPosition  drop_pos,
                                                      GtkTreeViewDropPosition *return_drop_pos,
                                                      GdkDragAction       *return_drag_action);
static void   pika_drawable_tree_view_drop_viewables (PikaContainerTreeView   *view,
                                                      GList                   *src_viewables,
                                                      PikaViewable            *dest_viewable,
                                                      GtkTreeViewDropPosition  drop_pos);
static void   pika_drawable_tree_view_drop_color (PikaContainerTreeView *view,
                                                  const PikaRGB       *color,
                                                  PikaViewable        *dest_viewable,
                                                  GtkTreeViewDropPosition  drop_pos);

static void   pika_drawable_tree_view_set_image  (PikaItemTreeView     *view,
                                                  PikaImage            *image);

static void   pika_drawable_tree_view_floating_selection_changed
                                                 (PikaImage            *image,
                                                  PikaDrawableTreeView *view);

static void   pika_drawable_tree_view_new_pattern_dropped
                                                 (GtkWidget            *widget,
                                                  gint                  x,
                                                  gint                  y,
                                                  PikaViewable         *viewable,
                                                  gpointer              data);
static void   pika_drawable_tree_view_new_color_dropped
                                                 (GtkWidget            *widget,
                                                  gint                  x,
                                                  gint                  y,
                                                  const PikaRGB        *color,
                                                  gpointer              data);


G_DEFINE_TYPE_WITH_CODE (PikaDrawableTreeView, pika_drawable_tree_view,
                         PIKA_TYPE_ITEM_TREE_VIEW,
                         G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
                                                pika_drawable_tree_view_view_iface_init))

#define parent_class pika_drawable_tree_view_parent_class

static PikaContainerViewInterface *parent_view_iface = NULL;


static void
pika_drawable_tree_view_class_init (PikaDrawableTreeViewClass *klass)
{
  GObjectClass               *object_class;
  PikaContainerTreeViewClass *tree_view_class;
  PikaItemTreeViewClass      *item_view_class;

  object_class    = G_OBJECT_CLASS (klass);
  tree_view_class = PIKA_CONTAINER_TREE_VIEW_CLASS (klass);
  item_view_class = PIKA_ITEM_TREE_VIEW_CLASS (klass);

  object_class->constructed      = pika_drawable_tree_view_constructed;

  tree_view_class->drop_possible  = pika_drawable_tree_view_drop_possible;
  tree_view_class->drop_viewables = pika_drawable_tree_view_drop_viewables;
  tree_view_class->drop_color     = pika_drawable_tree_view_drop_color;

  item_view_class->set_image     = pika_drawable_tree_view_set_image;

  item_view_class->lock_content_icon_name    = PIKA_ICON_LOCK_CONTENT;
  item_view_class->lock_content_tooltip      = _("Lock pixels");
  item_view_class->lock_position_icon_name   = PIKA_ICON_LOCK_POSITION;
  item_view_class->lock_position_tooltip     = _("Lock position and size");
  item_view_class->lock_visibility_icon_name = PIKA_ICON_LOCK_VISIBILITY;
  item_view_class->lock_visibility_tooltip   = _("Lock visibility");
}

static void
pika_drawable_tree_view_view_iface_init (PikaContainerViewInterface *iface)
{
  parent_view_iface = g_type_interface_peek_parent (iface);

  iface->select_items = pika_drawable_tree_view_select_items;
}

static void
pika_drawable_tree_view_init (PikaDrawableTreeView *view)
{
}

static void
pika_drawable_tree_view_constructed (GObject *object)
{
  PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (object);
  PikaItemTreeView      *item_view = PIKA_ITEM_TREE_VIEW (object);

  G_OBJECT_CLASS (parent_class)->constructed (object);

  pika_dnd_viewable_dest_add (pika_item_tree_view_get_new_button (item_view),
                              PIKA_TYPE_PATTERN,
                              pika_drawable_tree_view_new_pattern_dropped,
                              item_view);
  pika_dnd_color_dest_add (pika_item_tree_view_get_new_button (item_view),
                           pika_drawable_tree_view_new_color_dropped,
                           item_view);

  pika_dnd_color_dest_add    (GTK_WIDGET (tree_view->view),
                              NULL, tree_view);
  pika_dnd_viewable_dest_add (GTK_WIDGET (tree_view->view), PIKA_TYPE_PATTERN,
                              NULL, tree_view);
}


/*  PikaContainerView methods  */

static gboolean
pika_drawable_tree_view_select_items (PikaContainerView *view,
                                      GList             *items,
                                      GList             *paths)
{
  PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (view);
  PikaImage        *image     = pika_item_tree_view_get_image (item_view);
  gboolean          success   = TRUE;

  if (image)
    {
      PikaLayer *floating_sel = pika_image_get_floating_selection (image);

      success = (items        == NULL ||
                 floating_sel == NULL ||
                 (g_list_length (items) == 1 &&
                  items->data           == PIKA_VIEWABLE (floating_sel)));

      if (! success)
        {
          Pika        *pika    = image->pika;
          PikaContext *context = pika_get_user_context (pika);
          PikaDisplay *display = pika_context_get_display (context);

          pika_message_literal (pika, G_OBJECT (display), PIKA_MESSAGE_WARNING,
                                _("Cannot select items while a floating "
                                  "selection is active."));
        }
    }

  if (success)
    success = parent_view_iface->select_items (view, items, paths);

  return success;
}

/*  PikaContainerTreeView methods  */

static gboolean
pika_drawable_tree_view_drop_possible (PikaContainerTreeView   *tree_view,
                                       PikaDndType              src_type,
                                       GList                   *src_viewables,
                                       PikaViewable            *dest_viewable,
                                       GtkTreePath             *drop_path,
                                       GtkTreeViewDropPosition  drop_pos,
                                       GtkTreeViewDropPosition *return_drop_pos,
                                       GdkDragAction           *return_drag_action)
{
  if (PIKA_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_possible (tree_view,
                                                                    src_type,
                                                                    src_viewables,
                                                                    dest_viewable,
                                                                    drop_path,
                                                                    drop_pos,
                                                                    return_drop_pos,
                                                                    return_drag_action))
    {
      if (src_type == PIKA_DND_TYPE_COLOR ||
          src_type == PIKA_DND_TYPE_PATTERN)
        {
          if (! dest_viewable ||
              pika_item_is_content_locked (PIKA_ITEM (dest_viewable), NULL) ||
              pika_viewable_get_children (PIKA_VIEWABLE (dest_viewable)))
            return FALSE;

          if (return_drop_pos)
            {
              *return_drop_pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
            }
        }

      return TRUE;
    }

  return FALSE;
}

static void
pika_drawable_tree_view_drop_viewables (PikaContainerTreeView   *view,
                                        GList                   *src_viewables,
                                        PikaViewable            *dest_viewable,
                                        GtkTreeViewDropPosition  drop_pos)
{
  GList *iter;

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

      if (dest_viewable && PIKA_IS_PATTERN (src_viewable))
        {
          PikaImage       *image   = pika_item_get_image (PIKA_ITEM (dest_viewable));
          PikaFillOptions *options = pika_fill_options_new (image->pika, NULL, FALSE);

          pika_fill_options_set_style (options, PIKA_FILL_STYLE_PATTERN);
          pika_context_set_pattern (PIKA_CONTEXT (options),
                                    PIKA_PATTERN (src_viewable));

          pika_drawable_edit_fill (PIKA_DRAWABLE (dest_viewable),
                                   options,
                                   C_("undo-type", "Drop pattern to layer"));

          g_object_unref (options);

          pika_image_flush (image);
          return;
        }
    }

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

static void
pika_drawable_tree_view_drop_color (PikaContainerTreeView   *view,
                                    const PikaRGB           *color,
                                    PikaViewable            *dest_viewable,
                                    GtkTreeViewDropPosition  drop_pos)
{
  if (dest_viewable)
    {
      PikaImage       *image   = pika_item_get_image (PIKA_ITEM (dest_viewable));
      PikaFillOptions *options = pika_fill_options_new (image->pika, NULL, FALSE);

      pika_fill_options_set_style (options, PIKA_FILL_STYLE_FG_COLOR);
      pika_context_set_foreground (PIKA_CONTEXT (options), color);

      pika_drawable_edit_fill (PIKA_DRAWABLE (dest_viewable),
                               options,
                               C_("undo-type", "Drop color to layer"));

      g_object_unref (options);

      pika_image_flush (image);
    }
}


/*  PikaItemTreeView methods  */

static void
pika_drawable_tree_view_set_image (PikaItemTreeView *view,
                                   PikaImage        *image)
{
  if (pika_item_tree_view_get_image (view))
    g_signal_handlers_disconnect_by_func (pika_item_tree_view_get_image (view),
                                          pika_drawable_tree_view_floating_selection_changed,
                                          view);

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

  if (pika_item_tree_view_get_image (view))
    g_signal_connect (pika_item_tree_view_get_image (view),
                      "floating-selection-changed",
                      G_CALLBACK (pika_drawable_tree_view_floating_selection_changed),
                      view);
}


/*  callbacks  */

static void
pika_drawable_tree_view_floating_selection_changed (PikaImage            *image,
                                                    PikaDrawableTreeView *view)
{
  GList *items;

  items = PIKA_ITEM_TREE_VIEW_GET_CLASS (view)->get_selected_items (image);
  items = g_list_copy (items);

  /*  update button states  */
  pika_container_view_select_items (PIKA_CONTAINER_VIEW (view), items);

  g_list_free (items);
}

static void
pika_drawable_tree_view_new_dropped (PikaItemTreeView *view,
                                     PikaFillOptions  *options,
                                     const gchar      *undo_desc)
{
  PikaImage *image = pika_item_tree_view_get_image (view);
  PikaItem  *item;

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

  item = PIKA_ITEM_TREE_VIEW_GET_CLASS (view)->new_item (image);

  if (item)
    pika_drawable_edit_fill (PIKA_DRAWABLE (item), options, undo_desc);

  pika_image_undo_group_end (image);

  pika_image_flush (image);
}

static void
pika_drawable_tree_view_new_pattern_dropped (GtkWidget    *widget,
                                             gint          x,
                                             gint          y,
                                             PikaViewable *viewable,
                                             gpointer      data)
{
  PikaItemTreeView *view    = PIKA_ITEM_TREE_VIEW (data);
  PikaImage        *image   = pika_item_tree_view_get_image (view);
  PikaFillOptions  *options = pika_fill_options_new (image->pika, NULL, FALSE);

  pika_fill_options_set_style (options, PIKA_FILL_STYLE_PATTERN);
  pika_context_set_pattern (PIKA_CONTEXT (options), PIKA_PATTERN (viewable));

  pika_drawable_tree_view_new_dropped (view, options,
                                       C_("undo-type", "Drop pattern to layer"));

  g_object_unref (options);
}

static void
pika_drawable_tree_view_new_color_dropped (GtkWidget     *widget,
                                           gint           x,
                                           gint           y,
                                           const PikaRGB *color,
                                           gpointer       data)
{
  PikaItemTreeView *view    = PIKA_ITEM_TREE_VIEW (data);
  PikaImage        *image   = pika_item_tree_view_get_image (view);
  PikaFillOptions  *options = pika_fill_options_new (image->pika, NULL, FALSE);

  pika_fill_options_set_style (options, PIKA_FILL_STYLE_FG_COLOR);
  pika_context_set_foreground (PIKA_CONTEXT (options), color);

  pika_drawable_tree_view_new_dropped (view, options,
                                       C_("undo-type", "Drop color to layer"));

  g_object_unref (options);
}
