/* 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 <gegl.h>
#include <gtk/gtk.h>

#include "libpikamath/pikamath.h"

#include "tools-types.h"

#include "core/pika.h"
#include "core/pikasamplepoint.h"
#include "core/pikaimage.h"
#include "core/pikaimage-sample-points.h"

#include "display/pikadisplay.h"
#include "display/pikadisplayshell.h"
#include "display/pikadisplayshell-selection.h"
#include "display/pikadisplayshell-transform.h"

#include "pikasamplepointtool.h"
#include "pikatoolcontrol.h"
#include "tool_manager.h"

#include "pika-intl.h"


/*  local function prototypes  */

static void   pika_sample_point_tool_button_release (PikaTool              *tool,
                                                     const PikaCoords      *coords,
                                                     guint32                time,
                                                     GdkModifierType        state,
                                                     PikaButtonReleaseType  release_type,
                                                     PikaDisplay           *display);
static void   pika_sample_point_tool_motion         (PikaTool              *tool,
                                                     const PikaCoords      *coords,
                                                     guint32                time,
                                                     GdkModifierType        state,
                                                     PikaDisplay           *display);

static void   pika_sample_point_tool_draw           (PikaDrawTool          *draw_tool);

static void   pika_sample_point_tool_start          (PikaTool              *parent_tool,
                                                     PikaDisplay           *display,
                                                     PikaSamplePoint       *sample_point);


G_DEFINE_TYPE (PikaSamplePointTool, pika_sample_point_tool, PIKA_TYPE_DRAW_TOOL)

#define parent_class pika_sample_point_tool_parent_class


static void
pika_sample_point_tool_class_init (PikaSamplePointToolClass *klass)
{
  PikaToolClass     *tool_class      = PIKA_TOOL_CLASS (klass);
  PikaDrawToolClass *draw_tool_class = PIKA_DRAW_TOOL_CLASS (klass);

  tool_class->button_release = pika_sample_point_tool_button_release;
  tool_class->motion         = pika_sample_point_tool_motion;

  draw_tool_class->draw      = pika_sample_point_tool_draw;
}

static void
pika_sample_point_tool_init (PikaSamplePointTool *sp_tool)
{
  PikaTool *tool = PIKA_TOOL (sp_tool);

  pika_tool_control_set_snap_to            (tool->control, FALSE);
  pika_tool_control_set_handle_empty_image (tool->control, TRUE);
  pika_tool_control_set_tool_cursor        (tool->control,
                                            PIKA_TOOL_CURSOR_MOVE);
  pika_tool_control_set_scroll_lock        (tool->control, TRUE);
  pika_tool_control_set_precision          (tool->control,
                                            PIKA_CURSOR_PRECISION_PIXEL_CENTER);

  sp_tool->sample_point   = NULL;
  sp_tool->sample_point_x = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;
  sp_tool->sample_point_y = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;
}

static void
pika_sample_point_tool_button_release (PikaTool              *tool,
                                       const PikaCoords      *coords,
                                       guint32                time,
                                       GdkModifierType        state,
                                       PikaButtonReleaseType  release_type,
                                       PikaDisplay           *display)
{
  PikaSamplePointTool *sp_tool = PIKA_SAMPLE_POINT_TOOL (tool);
  PikaDisplayShell    *shell   = pika_display_get_shell (display);
  PikaImage           *image   = pika_display_get_image (display);

  pika_tool_pop_status (tool, display);

  pika_tool_control_halt (tool->control);

  pika_draw_tool_stop (PIKA_DRAW_TOOL (tool));

  if (release_type != PIKA_BUTTON_RELEASE_CANCEL)
    {
      gint width  = pika_image_get_width  (image);
      gint height = pika_image_get_height (image);

      if (sp_tool->sample_point_x == PIKA_SAMPLE_POINT_POSITION_UNDEFINED ||
          sp_tool->sample_point_x <  0                                    ||
          sp_tool->sample_point_x >= width                                ||
          sp_tool->sample_point_y == PIKA_SAMPLE_POINT_POSITION_UNDEFINED ||
          sp_tool->sample_point_y <  0                                    ||
          sp_tool->sample_point_y >= height)
        {
          if (sp_tool->sample_point)
            {
              pika_image_remove_sample_point (image,
                                              sp_tool->sample_point, TRUE);
              sp_tool->sample_point = NULL;
            }
        }
      else
        {
          if (sp_tool->sample_point)
            {
              pika_image_move_sample_point (image,
                                            sp_tool->sample_point,
                                            sp_tool->sample_point_x,
                                            sp_tool->sample_point_y,
                                            TRUE);
            }
          else
            {
              sp_tool->sample_point =
                pika_image_add_sample_point_at_pos (image,
                                                    sp_tool->sample_point_x,
                                                    sp_tool->sample_point_y,
                                                    TRUE);
            }
        }

      pika_image_flush (image);
    }

  pika_display_shell_selection_resume (shell);

  sp_tool->sample_point_x = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;
  sp_tool->sample_point_y = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;

  tool_manager_pop_tool (display->pika);
  g_object_unref (sp_tool);

  {
    PikaTool *active_tool = tool_manager_get_active (display->pika);

    if (PIKA_IS_DRAW_TOOL (active_tool))
      pika_draw_tool_pause (PIKA_DRAW_TOOL (active_tool));

    tool_manager_oper_update_active (display->pika, coords, state,
                                     TRUE, display);
    tool_manager_cursor_update_active (display->pika, coords, state,
                                       display);

    if (PIKA_IS_DRAW_TOOL (active_tool))
      pika_draw_tool_resume (PIKA_DRAW_TOOL (active_tool));
  }
}

static void
pika_sample_point_tool_motion (PikaTool         *tool,
                               const PikaCoords *coords,
                               guint32           time,
                               GdkModifierType   state,
                               PikaDisplay      *display)

{
  PikaSamplePointTool *sp_tool      = PIKA_SAMPLE_POINT_TOOL (tool);
  PikaDisplayShell    *shell        = pika_display_get_shell (display);
  gboolean             delete_point = FALSE;
  gint                 tx, ty;

  pika_draw_tool_pause (PIKA_DRAW_TOOL (tool));

  pika_display_shell_transform_xy (shell,
                                   coords->x, coords->y,
                                   &tx, &ty);

  if (tx < 0 || tx >= shell->disp_width ||
      ty < 0 || ty >= shell->disp_height)
    {
      sp_tool->sample_point_x = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;
      sp_tool->sample_point_y = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;

      delete_point = TRUE;
    }
  else
    {
      PikaImage *image  = pika_display_get_image (display);
      gint       width  = pika_image_get_width  (image);
      gint       height = pika_image_get_height (image);

      sp_tool->sample_point_x = floor (coords->x);
      sp_tool->sample_point_y = floor (coords->y);

      if (sp_tool->sample_point_x <  0      ||
          sp_tool->sample_point_x >= height ||
          sp_tool->sample_point_y <  0      ||
          sp_tool->sample_point_y >= width)
        {
          delete_point = TRUE;
        }
    }

  pika_draw_tool_resume (PIKA_DRAW_TOOL (tool));

  pika_tool_pop_status (tool, display);

  if (delete_point)
    {
      pika_tool_push_status (tool, display,
                             sp_tool->sample_point ?
                             _("Remove Sample Point") :
                             _("Cancel Sample Point"));
    }
  else if (sp_tool->sample_point)
    {
      pika_tool_push_status_coords (tool, display,
                                    pika_tool_control_get_precision (tool->control),
                                    _("Move Sample Point: "),
                                    sp_tool->sample_point_x -
                                    sp_tool->sample_point_old_x,
                                    ", ",
                                    sp_tool->sample_point_y -
                                    sp_tool->sample_point_old_y,
                                    NULL);
    }
  else
    {
      pika_tool_push_status_coords (tool, display,
                                    pika_tool_control_get_precision (tool->control),
                                    _("Add Sample Point: "),
                                    sp_tool->sample_point_x,
                                    ", ",
                                    sp_tool->sample_point_y,
                                    NULL);
    }
}

static void
pika_sample_point_tool_draw (PikaDrawTool *draw_tool)
{
  PikaSamplePointTool *sp_tool = PIKA_SAMPLE_POINT_TOOL (draw_tool);

  if (sp_tool->sample_point_x != PIKA_SAMPLE_POINT_POSITION_UNDEFINED &&
      sp_tool->sample_point_y != PIKA_SAMPLE_POINT_POSITION_UNDEFINED)
    {
      pika_draw_tool_add_crosshair (draw_tool,
                                    sp_tool->sample_point_x,
                                    sp_tool->sample_point_y);
    }
}

static void
pika_sample_point_tool_start (PikaTool        *parent_tool,
                              PikaDisplay     *display,
                              PikaSamplePoint *sample_point)
{
  PikaSamplePointTool *sp_tool;
  PikaTool            *tool;

  sp_tool = g_object_new (PIKA_TYPE_SAMPLE_POINT_TOOL,
                          "tool-info", parent_tool->tool_info,
                          NULL);

  tool = PIKA_TOOL (sp_tool);

  pika_display_shell_selection_pause (pika_display_get_shell (display));

  if (sample_point)
    {
      sp_tool->sample_point = sample_point;

      pika_sample_point_get_position (sample_point,
                                      &sp_tool->sample_point_old_x,
                                      &sp_tool->sample_point_old_y);

      sp_tool->sample_point_x = sp_tool->sample_point_old_x;
      sp_tool->sample_point_y = sp_tool->sample_point_old_y;
    }
  else
    {
      sp_tool->sample_point       = NULL;
      sp_tool->sample_point_old_x = 0;
      sp_tool->sample_point_old_y = 0;
      sp_tool->sample_point_x     = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;
      sp_tool->sample_point_y     = PIKA_SAMPLE_POINT_POSITION_UNDEFINED;
    }

  pika_tool_set_cursor (tool, display,
                        PIKA_CURSOR_MOUSE,
                        PIKA_TOOL_CURSOR_COLOR_PICKER,
                        PIKA_CURSOR_MODIFIER_MOVE);

  tool_manager_push_tool (display->pika, tool);

  tool->display = display;
  pika_tool_control_activate (tool->control);

  pika_draw_tool_start (PIKA_DRAW_TOOL (sp_tool), display);

  if (sp_tool->sample_point)
    {
      pika_tool_push_status_coords (tool, display,
                                    pika_tool_control_get_precision (tool->control),
                                    _("Move Sample Point: "),
                                    sp_tool->sample_point_x -
                                    sp_tool->sample_point_old_x,
                                    ", ",
                                    sp_tool->sample_point_y -
                                    sp_tool->sample_point_old_y,
                                    NULL);
    }
  else
    {
      pika_tool_push_status_coords (tool, display,
                                    pika_tool_control_get_precision (tool->control),
                                    _("Add Sample Point: "),
                                    sp_tool->sample_point_x,
                                    ", ",
                                    sp_tool->sample_point_y,
                                    NULL);
    }
}


/*  public functions  */

void
pika_sample_point_tool_start_new (PikaTool    *parent_tool,
                                  PikaDisplay *display)
{
  g_return_if_fail (PIKA_IS_TOOL (parent_tool));
  g_return_if_fail (PIKA_IS_DISPLAY (display));

  pika_sample_point_tool_start (parent_tool, display, NULL);
}

void
pika_sample_point_tool_start_edit (PikaTool        *parent_tool,
                                   PikaDisplay     *display,
                                   PikaSamplePoint *sample_point)
{
  g_return_if_fail (PIKA_IS_TOOL (parent_tool));
  g_return_if_fail (PIKA_IS_DISPLAY (display));
  g_return_if_fail (PIKA_IS_SAMPLE_POINT (sample_point));

  pika_sample_point_tool_start (parent_tool, display, sample_point);
}
