/*
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This is a plug-in for GIMP.
 *
 * Generates images containing vector type drawings.
 *
 * Copyright (C) 1997 Andy Thomas  alt@picnic.demon.co.uk
 *
 * 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 <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "gfig.h"
#include "gfig-grid.h"
#include "gfig-dobject.h"
#include "gfig-preview.h"

#include "libgimp/stdplugins-intl.h"

#define PREVIEW_MASK  (GDK_EXPOSURE_MASK       | \
                       GDK_POINTER_MOTION_MASK | \
                       GDK_BUTTON_PRESS_MASK   | \
                       GDK_BUTTON_RELEASE_MASK | \
                       GDK_BUTTON_MOTION_MASK  | \
                       GDK_KEY_PRESS_MASK      | \
                       GDK_KEY_RELEASE_MASK)

static gint       x_pos_val;
static gint       y_pos_val;
static gint       pos_tag = -1;
GtkWidget        *status_label_dname;
GtkWidget        *status_label_fname;
static GtkWidget *pos_label;       /* XY pos marker */


static void       gfig_preview_realize  (GtkWidget *widget);
static gboolean   gfig_preview_events   (GtkWidget *widget,
                                         GdkEvent  *event);
static gboolean   gfig_preview_expose   (GtkWidget *widget,
                                         GdkEvent  *event);

static gint       gfig_invscale_x        (gint      x);
static gint       gfig_invscale_y        (gint      y);
static GtkWidget *gfig_pos_labels        (void);
static GtkWidget *make_pos_info          (void);

static void       gfig_pos_update        (gint      x,
                                          gint      y);
static void       gfig_pos_update_labels (gpointer  data);

GtkWidget *
make_preview (void)
{
  GtkWidget *frame;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *table;
  GtkWidget *ruler;

  gfig_context->preview = gtk_drawing_area_new ();
  gtk_widget_set_events (GTK_WIDGET (gfig_context->preview), PREVIEW_MASK);

  g_signal_connect (gfig_context->preview , "realize",
                    G_CALLBACK (gfig_preview_realize),
                    NULL);

  g_signal_connect (gfig_context->preview , "event",
                    G_CALLBACK (gfig_preview_events),
                    NULL);

  g_signal_connect_after (gfig_context->preview , "expose-event",
                          G_CALLBACK (gfig_preview_expose),
                          NULL);

  gtk_widget_set_size_request (gfig_context->preview,
                               preview_width, preview_height);

  frame = gtk_frame_new (NULL);

  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);

  table = gtk_table_new (3, 3, FALSE);
  gtk_table_attach (GTK_TABLE (table), gfig_context->preview, 1, 2, 1, 2,
                    GTK_FILL , GTK_FILL , 0, 0);
  gtk_container_add (GTK_CONTAINER (frame), table);

  ruler = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL);
  gimp_ruler_set_range (GIMP_RULER (ruler), 0, preview_width, PREVIEW_SIZE);
  g_signal_connect_swapped (gfig_context->preview, "motion-notify-event",
                            G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
                            ruler);
  gtk_table_attach (GTK_TABLE (table), ruler, 1, 2, 0, 1,
                    GTK_FILL, GTK_FILL, 0, 0);
  gtk_widget_show (ruler);

  ruler = gimp_ruler_new (GTK_ORIENTATION_VERTICAL);
  gimp_ruler_set_range (GIMP_RULER (ruler), 0, preview_height, PREVIEW_SIZE);
  g_signal_connect_swapped (gfig_context->preview, "motion-notify-event",
                            G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
                            ruler);
  gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 2,
                    GTK_FILL, GTK_FILL, 0, 0);
  gtk_widget_show (ruler);

  gtk_widget_show (frame);
  gtk_widget_show (table);

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  frame = make_pos_info ();
  gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);

  gtk_widget_show (vbox);
  gtk_widget_show (hbox);

  return vbox;
}

static void
gfig_preview_realize (GtkWidget *widget)
{
  GdkDisplay *display = gtk_widget_get_display (widget);

  gdk_window_set_cursor (gtk_widget_get_window (gfig_context->preview),
                         gdk_cursor_new_for_display (display, GDK_CROSSHAIR));
  gfig_grid_colors (widget);
}

static void
draw_background (cairo_t  *cr)
{
  if (! back_pixbuf)
    back_pixbuf = gimp_image_get_thumbnail (gfig_context->image_id,
                                            preview_width, preview_height,
                                            GIMP_PIXBUF_LARGE_CHECKS);

  if (back_pixbuf)
    {
      gdk_cairo_set_source_pixbuf (cr, back_pixbuf, 0, 0);
      cairo_paint (cr);
    }
}

static gboolean
gfig_preview_expose (GtkWidget *widget,
                     GdkEvent  *event)
{
  cairo_t *cr = gdk_cairo_create (event->expose.window);

  if (gfig_context->show_background)
    draw_background (cr);

  draw_grid (cr);
  draw_objects (gfig_context->current_obj->obj_list, TRUE, cr);

  if (obj_creating)
    {
      GList *single = g_list_prepend (NULL, obj_creating);
      draw_objects (single, TRUE, cr);
      g_list_free (single);
    }

  cairo_destroy (cr);
  return FALSE;
}

static gboolean
gfig_preview_events (GtkWidget *widget,
                     GdkEvent  *event)
{
  GdkEventButton *bevent;
  GdkEventMotion *mevent;
  GdkPoint        point;
  static gint     tmp_show_single = 0;

  switch (event->type)
    {
    case GDK_EXPOSE:
      break;

    case GDK_BUTTON_PRESS:
      bevent = (GdkEventButton *) event;
      point.x = bevent->x;
      point.y = bevent->y;

      g_assert (need_to_scale == 0); /* If not out of step some how */

      /* Start drawing of object */
      if (selvals.otype >= MOVE_OBJ)
        {
          if (!selvals.scaletoimage)
            {
              point.x = gfig_invscale_x (point.x);
              point.y = gfig_invscale_y (point.y);
            }
          object_operation_start (&point, bevent->state & GDK_SHIFT_MASK);

          /* If constraining save start pnt */
          if (selvals.opts.snap2grid)
            {
              /* Save point to constrained point ... if button 3 down */
              if (bevent->button == 3)
                {
                  find_grid_pos (&point, &point, FALSE);
                }
            }
        }
      else
        {
          if (selvals.opts.snap2grid)
            find_grid_pos (&point, &point, FALSE);
          object_start (&point, bevent->state & GDK_SHIFT_MASK);

          gtk_widget_queue_draw (widget);
        }

      break;

    case GDK_BUTTON_RELEASE:
      bevent = (GdkEventButton *) event;
      point.x = bevent->x;
      point.y = bevent->y;

      if (selvals.opts.snap2grid)
        find_grid_pos (&point, &point, bevent->button == 3);

      /* Still got shift down ?*/
      if (selvals.otype >= MOVE_OBJ)
        {
          if (!selvals.scaletoimage)
            {
              point.x = gfig_invscale_x (point.x);
              point.y = gfig_invscale_y (point.y);
            }
          object_operation_end (&point, bevent->state & GDK_SHIFT_MASK);
        }
      else
        {
          if (obj_creating)
            {
              object_end (&point, bevent->state & GDK_SHIFT_MASK);
            }
          else
            break;
        }

      gfig_paint_callback ();
      break;

    case GDK_MOTION_NOTIFY:
      mevent = (GdkEventMotion *) event;
      point.x = mevent->x;
      point.y = mevent->y;

      if (selvals.opts.snap2grid)
        find_grid_pos (&point, &point, mevent->state & GDK_BUTTON3_MASK);

      if (selvals.otype >= MOVE_OBJ)
        {
          /* Moving objects around */
          if (!selvals.scaletoimage)
            {
              point.x = gfig_invscale_x (point.x);
              point.y = gfig_invscale_y (point.y);
            }
          object_operation (&point, mevent->state & GDK_SHIFT_MASK);
          gfig_pos_update (point.x, point.y);
          return FALSE;
        }

      if (obj_creating)
        {
          obj_creating->class->update (&point);
          gtk_widget_queue_draw (widget);
        }
      gfig_pos_update (point.x, point.y);
      break;

    case GDK_KEY_PRESS:
      if ((tmp_show_single = obj_show_single) != -1)
        {
          obj_show_single = -1;
          draw_grid_clear ();
        }
      break;

    case GDK_KEY_RELEASE:
      if (tmp_show_single != -1)
        {
          obj_show_single = tmp_show_single;
          draw_grid_clear ();
        }
      break;

    default:
      break;
    }

  return FALSE;
}

static GtkWidget *
make_pos_info (void)
{
  GtkWidget *frame;
  GtkWidget *hbox;
  GtkWidget *label;

  frame = gimp_frame_new (_("Object Details"));

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
  gtk_container_add (GTK_CONTAINER (frame), hbox);

  /* Add labels */
  label = gfig_pos_labels ();
  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
  gfig_pos_enable (NULL, NULL);

#if 0
  label = gfig_obj_size_label ();
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
#endif /* 0 */

  gtk_widget_show (hbox);
  gtk_widget_show (frame);

  return frame;
}

static gint
gfig_invscale_x (gint x)
{
  if (!selvals.scaletoimage)
    return (gint) (x * scale_x_factor);
  else
    return x;
}

static gint
gfig_invscale_y (gint y)
{
  if (!selvals.scaletoimage)
    return (gint) (y * scale_y_factor);
  else
    return y;
}

static GtkWidget *
gfig_pos_labels (void)
{
  GtkWidget *label;
  GtkWidget *hbox;
  gchar      buf[256];

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  gtk_widget_show (hbox);

  /* Position labels */
  label = gtk_label_new (_("XY position:"));
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  pos_label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (hbox), pos_label, FALSE, FALSE, 0);
  gtk_widget_show (pos_label);

  g_snprintf (buf, sizeof (buf), "%d, %d", 0, 0);
  gtk_label_set_text (GTK_LABEL (pos_label), buf);

  return hbox;
}

void
gfig_pos_enable (GtkWidget *widget,
                 gpointer   data)
{
  gboolean enable = selvals.showpos;

  gtk_widget_set_sensitive (GTK_WIDGET (pos_label), enable);
}

static void
gfig_pos_update_labels (gpointer data)
{
  static gchar buf[256];

  pos_tag = -1;

  g_snprintf (buf, sizeof (buf), "%d, %d", x_pos_val, y_pos_val);
  gtk_label_set_text (GTK_LABEL (pos_label), buf);
}

static void
gfig_pos_update (gint x,
                 gint y)
{
  if ((x_pos_val !=x || y_pos_val != y) && pos_tag == -1 && selvals.showpos)
    {
      x_pos_val = x;
      y_pos_val = y;
      gfig_pos_update_labels (NULL);
    }
}
