/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * gimpviewrenderer-frame.c
 * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
 *
 * Contains code taken from eel, the Eazel Extensions Library.
 *
 * 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 "libgimpwidgets/gimpwidgets.h"

#include "widgets-types.h"

#include "core/gimpviewable.h"

#include "gimpviewrenderer.h"
#include "gimpviewrenderer-frame.h"
#include "gimpwidgets-utils.h"


/* utility to stretch a frame to the desired size */

static void
draw_frame_row (GdkPixbuf *frame_image,
                gint       target_width,
                gint       source_width,
                gint       source_v_position,
                gint       dest_v_position,
                GdkPixbuf *result_pixbuf,
                gint       left_offset,
                gint       height)
{
  gint remaining_width = target_width;
  gint h_offset        = 0;

  while (remaining_width > 0)
    {
      gint slab_width = (remaining_width > source_width ?
                         source_width : remaining_width);
      gdk_pixbuf_copy_area (frame_image,
                            left_offset, source_v_position,
                            slab_width, height,
                            result_pixbuf,
                            left_offset + h_offset, dest_v_position);

      remaining_width -= slab_width;
      h_offset += slab_width;
    }
}

/* utility to draw the middle section of the frame in a loop */
static void
draw_frame_column (GdkPixbuf *frame_image,
                   gint       target_height,
                   gint       source_height,
                   gint       source_h_position,
                   gint       dest_h_position,
                   GdkPixbuf *result_pixbuf,
                   gint       top_offset, int width)
{
  gint remaining_height = target_height;
  gint v_offset         = 0;

  while (remaining_height > 0)
    {
      gint slab_height = (remaining_height > source_height ?
                          source_height : remaining_height);

      gdk_pixbuf_copy_area (frame_image,
                            source_h_position, top_offset,
                            width, slab_height,
                            result_pixbuf,
                            dest_h_position, top_offset + v_offset);

      remaining_height -= slab_height;
      v_offset += slab_height;
    }
}

static GdkPixbuf *
stretch_frame_image (GdkPixbuf *frame_image,
                     gint       left_offset,
                     gint       top_offset,
                     gint       right_offset,
                     gint       bottom_offset,
                     gint       dest_width,
                     gint       dest_height)
{
  GdkPixbuf *pixbuf;
  gint       frame_width, frame_height;
  gint       target_width,  target_frame_width;
  gint       target_height, target_frame_height;

  frame_width  = gdk_pixbuf_get_width  (frame_image);
  frame_height = gdk_pixbuf_get_height (frame_image );

  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
                           dest_width, dest_height);
  gdk_pixbuf_fill (pixbuf, 0);

  target_width  = dest_width - left_offset - right_offset;
  target_height = dest_height - top_offset - bottom_offset;

  target_frame_width  = frame_width - left_offset - right_offset;
  target_frame_height = frame_height - top_offset - bottom_offset;

  left_offset   += MIN (target_width / 4, target_frame_width / 4);
  right_offset  += MIN (target_width / 4, target_frame_width / 4);
  top_offset    += MIN (target_height / 4, target_frame_height / 4);
  bottom_offset += MIN (target_height / 4, target_frame_height / 4);

  target_width  = dest_width - left_offset - right_offset;
  target_height = dest_height - top_offset - bottom_offset;

  target_frame_width  = frame_width - left_offset - right_offset;
  target_frame_height = frame_height - top_offset - bottom_offset;

  /* draw the left top corner  and top row */
  gdk_pixbuf_copy_area (frame_image,
                        0, 0, left_offset, top_offset,
                        pixbuf, 0,  0);
  draw_frame_row (frame_image, target_width, target_frame_width,
                  0, 0,
                  pixbuf,
                  left_offset, top_offset);

  /* draw the right top corner and left column */
  gdk_pixbuf_copy_area (frame_image,
                        frame_width - right_offset, 0,
                        right_offset, top_offset,

                        pixbuf,
                        dest_width - right_offset,  0);
  draw_frame_column (frame_image, target_height, target_frame_height, 0, 0,
                     pixbuf, top_offset, left_offset);

  /* draw the bottom right corner and bottom row */
  gdk_pixbuf_copy_area (frame_image,
                        frame_width - right_offset, frame_height - bottom_offset,
                        right_offset, bottom_offset,
                        pixbuf,
                        dest_width - right_offset, dest_height - bottom_offset);
  draw_frame_row (frame_image, target_width, target_frame_width,
                  frame_height - bottom_offset, dest_height - bottom_offset,
                  pixbuf, left_offset, bottom_offset);

  /* draw the bottom left corner and the right column */
  gdk_pixbuf_copy_area (frame_image,
                        0, frame_height - bottom_offset,
                        left_offset, bottom_offset,
                        pixbuf,
                        0,  dest_height - bottom_offset);
  draw_frame_column (frame_image, target_height, target_frame_height,
                     frame_width - right_offset, dest_width - right_offset,
                     pixbuf, top_offset, right_offset);

  return pixbuf;
}

static GdkPixbuf *
gimp_view_renderer_get_frame (GimpViewRenderer *renderer,
                              gint              width,
                              gint              height)
{
  GimpViewRendererClass *class = GIMP_VIEW_RENDERER_GET_CLASS (renderer);

  return stretch_frame_image (class->frame,
                              class->frame_left,
                              class->frame_top,
                              class->frame_right,
                              class->frame_bottom,
                              width, height);
}

static void
gimp_view_renderer_ensure_frame (GimpViewRenderer *renderer,
                                 GtkWidget        *widget)
{
  GimpViewRendererClass *class = GIMP_VIEW_RENDERER_GET_CLASS (renderer);

  if (! class->frame)
    {
      class->frame = gimp_widget_load_icon (widget, GIMP_ICON_FRAME, 48);

      /*  FIXME: shouldn't be hardcoded  */
      class->frame_left   = 2;
      class->frame_top    = 2;
      class->frame_right  = 4;
      class->frame_bottom = 4;
    }
}

GdkPixbuf *
gimp_view_renderer_get_frame_pixbuf (GimpViewRenderer *renderer,
                                     GtkWidget        *widget,
                                     gint              width,
                                     gint              height)
{
  GimpViewRendererClass *class;
  GdkPixbuf             *frame;
  GdkPixbuf             *pixbuf;
  gint                   w, h;
  gint                   x, y;

  g_return_val_if_fail (GIMP_IS_VIEW_RENDERER (renderer), NULL);
  g_return_val_if_fail (GIMP_IS_VIEWABLE (renderer->viewable), NULL);

  gimp_view_renderer_ensure_frame (renderer, widget);

  class = GIMP_VIEW_RENDERER_GET_CLASS (renderer);

  w = width  - class->frame_left - class->frame_right;
  h = height - class->frame_top  - class->frame_bottom;

  if (w > 12 && h > 12)
    {
      pixbuf = gimp_viewable_get_pixbuf (renderer->viewable,
                                         renderer->context,
                                         w, h);
      if (!pixbuf)
        return NULL;

      x = class->frame_left;
      y = class->frame_top;
      w = gdk_pixbuf_get_width (pixbuf);
      h = gdk_pixbuf_get_height (pixbuf);

      frame  = gimp_view_renderer_get_frame (renderer,
                                             w + x + class->frame_right,
                                             h + y + class->frame_bottom);
    }
  else
    {
      pixbuf = gimp_viewable_get_pixbuf (renderer->viewable,
                                         renderer->context,
                                         width - 2, height - 2);
      if (!pixbuf)
        return NULL;

      /*  as fallback, render the preview with a 1 pixel wide black border  */

      x = 1;
      y = 1;
      w = gdk_pixbuf_get_width (pixbuf);
      h = gdk_pixbuf_get_height (pixbuf);

      frame = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w + 2, h + 2);
      gdk_pixbuf_fill (frame, 0);
     }

  gdk_pixbuf_copy_area (pixbuf, 0, 0, w, h, frame, x, y);

  return frame;
}


/* This API is somewhat weird but GimpThumbBox needs these values so
 * it can request the GimpImageFile view in the proper size.
 */
void
gimp_view_renderer_get_frame_size (gint *horizontal,
                                   gint *vertical)
{
  GimpViewRendererClass *class;

  class = g_type_class_ref (GIMP_TYPE_VIEW_RENDERER);

  if (horizontal)
    *horizontal = class->frame_left + class->frame_right;

  if (vertical)
    *vertical = class->frame_top + class->frame_bottom;

  g_type_class_unref (class);
}
