/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 *   gimpvectorlayer.c
 *
 *   Copyright 2006 Hendrik Boom
 *
 *   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 <stdio.h>

#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>

#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "libgimpmath/gimpmath.h"

#include "path-types.h"

#include "core/gimp.h"
#include "core/gimpdrawable-fill.h"
#include "core/gimpdrawable-stroke.h"
#include "core/gimpimage.h"
#include "core/gimpselection.h"
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimpstrokeoptions.h"
#include "core/gimpparasitelist.h"
#include "core/gimprasterizable.h"

#include "gimpvectorlayer.h"
#include "gimpvectorlayeroptions.h"
#include "gimppath.h"

#include "gimp-intl.h"


enum
{
  PROP_0,
  PROP_VECTOR_LAYER_OPTIONS,
};


/* local function declarations */

static void       gimp_vector_layer_rasterizable_iface_init
                                                    (GimpRasterizableInterface *iface);


static void       gimp_vector_layer_finalize        (GObject                *object);
static void       gimp_vector_layer_get_property    (GObject                *object,
                                                     guint                   property_id,
                                                     GValue                 *value,
                                                     GParamSpec             *pspec);
static void       gimp_vector_layer_set_property    (GObject                *object,
                                                     guint                   property_id,
                                                     const GValue           *value,
                                                     GParamSpec             *pspec);

static void       gimp_vector_layer_set_rasterized  (GimpRasterizable *rasterizable,
                                                     gboolean          rasterized);

static void       gimp_vector_layer_set_vector_options
                                                    (GimpVectorLayer        *layer,
                                                     GimpVectorLayerOptions *options);

static void       gimp_vector_layer_set_buffer      (GimpDrawable           *drawable,
                                                     gboolean                push_undo,
                                                     const gchar            *undo_desc,
                                                     GeglBuffer             *buffer,
                                                     const GeglRectangle    *bounds);
static void       gimp_vector_layer_push_undo       (GimpDrawable           *drawable,
                                                     const gchar            *undo_desc,
                                                     GeglBuffer             *buffer,
                                                     gint                    x,
                                                     gint                    y,
                                                     gint                    width,
                                                     gint                    height);

static gint64     gimp_vector_layer_get_memsize     (GimpObject             *object,
                                                     gint64                 *gui_size);

static void       gimp_vector_layer_convert         (GimpItem               *item,
                                                     GimpImage              *dest_image,
                                                     GType                   old_type);
static GimpItem * gimp_vector_layer_duplicate       (GimpItem               *item,
                                                     GType                   new_type);

static void       gimp_vector_layer_translate       (GimpLayer              *layer,
                                                     gint                    offset_x,
                                                     gint                    offset_y);
static void       gimp_vector_layer_scale           (GimpItem               *item,
                                                     gint                    new_width,
                                                     gint                    new_height,
                                                     gint                    new_offset_x,
                                                     gint                    new_offset_y,
                                                     GimpInterpolationType   interp_type,
                                                     GimpProgress           *progress);
static void       gimp_vector_layer_resize          (GimpItem               *item,
                                                     GimpContext            *context,
                                                     GimpFillType            fill_type,
                                                     gint                    new_width,
                                                     gint                    new_height,
                                                     gint                    offset_x,
                                                     gint                    offset_y);
static void       gimp_vector_layer_flip            (GimpItem               *item,
                                                     GimpContext            *context,
                                                     GimpOrientationType     flip_type,
                                                     gdouble                 axis,
                                                     gboolean                clip_result);
static void       gimp_vector_layer_rotate          (GimpItem               *item,
                                                     GimpContext            *context,
                                                     GimpRotationType        rotate_type,
                                                     gdouble                 center_x,
                                                     gdouble                 center_y,
                                                     gboolean                clip_result);
static void       gimp_vector_layer_transform       (GimpItem               *item,
                                                     GimpContext            *context,
                                                     const GimpMatrix3      *matrix,
                                                     GimpTransformDirection  direction,
                                                     GimpInterpolationType   interp_type,
                                                     GimpTransformResize     clip_result,
                                                     GimpProgress           *progress,
                                                     gboolean                push_undo);

static gboolean   gimp_vector_layer_render          (GimpVectorLayer        *layer);
static void       gimp_vector_layer_render_path     (GimpVectorLayer        *layer);
static void       gimp_vector_layer_changed_options (GimpVectorLayer        *layer);

static void       gimp_vector_layer_removed         (GimpItem               *item);

static void       gimp_vector_layer_removed_options_path
                                                    (GimpVectorLayer        *layer);


G_DEFINE_TYPE_WITH_CODE (GimpVectorLayer, gimp_vector_layer, GIMP_TYPE_LAYER,
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_RASTERIZABLE,
                                                gimp_vector_layer_rasterizable_iface_init))

#define parent_class gimp_vector_layer_parent_class


static void
gimp_vector_layer_class_init (GimpVectorLayerClass *klass)
{
  GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
  GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
  GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
  GimpItemClass     *item_class        = GIMP_ITEM_CLASS (klass);
  GimpDrawableClass *drawable_class    = GIMP_DRAWABLE_CLASS (klass);
  GimpLayerClass    *layer_class       = GIMP_LAYER_CLASS (klass);

  drawable_class->set_buffer        = gimp_vector_layer_set_buffer;
  drawable_class->push_undo         = gimp_vector_layer_push_undo;

  object_class->finalize            = gimp_vector_layer_finalize;
  object_class->set_property        = gimp_vector_layer_set_property;
  object_class->get_property        = gimp_vector_layer_get_property;

  gimp_object_class->get_memsize    = gimp_vector_layer_get_memsize;

  /* TODO: make a custom icon "gimp-vector-layer". */
  viewable_class->default_icon_name = "gimp-tool-path";
  viewable_class->default_name      = _("Vector Layer");

  layer_class->translate            = gimp_vector_layer_translate;

  item_class->removed               = gimp_vector_layer_removed;
  item_class->convert               = gimp_vector_layer_convert;
  item_class->duplicate             = gimp_vector_layer_duplicate;
  item_class->rename                = gimp_rasterizable_rename;
  item_class->scale                 = gimp_vector_layer_scale;
  item_class->resize                = gimp_vector_layer_resize;
  item_class->flip                  = gimp_vector_layer_flip;
  item_class->rotate                = gimp_vector_layer_rotate;
  item_class->transform             = gimp_vector_layer_transform;
  item_class->rename_desc           = _("Rename Vector Layer");
  item_class->translate_desc        = _("Move Vector Layer");
  item_class->scale_desc            = _("Scale Vector Layer");
  item_class->resize_desc           = _("Resize Vector Layer");
  item_class->flip_desc             = _("Flip Vector Layer");
  item_class->rotate_desc           = _("Rotate Vector Layer");
  item_class->transform_desc        = _("Transform Vector Layer");

  GIMP_CONFIG_PROP_OBJECT (object_class, PROP_VECTOR_LAYER_OPTIONS,
                           "vector-layer-options", NULL, NULL,
                           GIMP_TYPE_VECTOR_LAYER_OPTIONS,
                           G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
}

static void
gimp_vector_layer_init (GimpVectorLayer *layer)
{
  layer->options  = NULL;
}

static void
gimp_vector_layer_rasterizable_iface_init (GimpRasterizableInterface *iface)
{
  iface->set_rasterized = gimp_vector_layer_set_rasterized;
}

static void
gimp_vector_layer_finalize (GObject *object)
{
  GimpVectorLayer *layer = GIMP_VECTOR_LAYER (object);

  if (layer->options)
    {
      g_object_unref (layer->options);
      layer->options = NULL;
    }

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gimp_vector_layer_get_property (GObject    *object,
                                guint       property_id,
                                GValue     *value,
                                GParamSpec *pspec)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (object);

  switch (property_id)
    {
    case PROP_VECTOR_LAYER_OPTIONS:
      g_value_set_object (value, vector_layer->options);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_vector_layer_set_property (GObject      *object,
                                guint         property_id,
                                const GValue *value,
                                GParamSpec   *pspec)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (object);

  switch (property_id)
    {
    case PROP_VECTOR_LAYER_OPTIONS:
      gimp_vector_layer_set_vector_options (vector_layer, g_value_get_object (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_vector_layer_set_rasterized (GimpRasterizable *rasterizable,
                                  gboolean          rasterized)
{
  if (! rasterized)
    gimp_vector_layer_render (GIMP_VECTOR_LAYER (rasterizable));
}

static void
gimp_vector_layer_set_vector_options (GimpVectorLayer        *layer,
                                      GimpVectorLayerOptions *options)
{
  if (layer->options)
    {
      g_signal_handlers_disconnect_by_func (layer->options,
                                            G_CALLBACK (gimp_vector_layer_changed_options),
                                            layer);

      if (layer->options->path)
        g_signal_handlers_disconnect_by_func (layer->options->path,
                                              G_CALLBACK (gimp_vector_layer_removed_options_path),
                                              layer);

      g_object_unref (layer->options);
      layer->options = NULL;
    }

  if (options)
    g_set_object (&layer->options, options);

  gimp_vector_layer_changed_options (layer);

  if (layer->options)
    {
      if (layer->options->path)
        g_signal_connect_object (layer->options->path, "removed",
                                 G_CALLBACK (gimp_vector_layer_removed_options_path),
                                 layer, G_CONNECT_SWAPPED);

      g_signal_connect_object (layer->options, "notify",
                               G_CALLBACK (gimp_vector_layer_changed_options),
                               layer, G_CONNECT_SWAPPED);
    }

  g_object_notify (G_OBJECT (layer), "vector-layer-options");
}

static void
gimp_vector_layer_set_buffer (GimpDrawable        *drawable,
                              gboolean             push_undo,
                              const gchar         *undo_desc,
                              GeglBuffer          *buffer,
                              const GeglRectangle *bounds)
{
  GimpVectorLayer *layer = GIMP_VECTOR_LAYER (drawable);
  GimpImage       *image = gimp_item_get_image (GIMP_ITEM (layer));
  gboolean         is_rasterized;

  is_rasterized = gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer));

  if (push_undo && ! is_rasterized)
    gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD,
                                 undo_desc);

  GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
                                                  push_undo, undo_desc,
                                                  buffer, bounds);

  if (push_undo && ! is_rasterized)
    {
      gimp_rasterizable_rasterize (GIMP_RASTERIZABLE (layer), push_undo);
      gimp_image_undo_group_end (image);
    }
}

static void
gimp_vector_layer_push_undo (GimpDrawable *drawable,
                             const gchar  *undo_desc,
                             GeglBuffer   *buffer,
                             gint          x,
                             gint          y,
                             gint          width,
                             gint          height)
{
  GimpVectorLayer *layer = GIMP_VECTOR_LAYER (drawable);
  GimpImage       *image = gimp_item_get_image (GIMP_ITEM (layer));
  gboolean         is_rasterized;

  is_rasterized = gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer));

  if (! is_rasterized)
    gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE, undo_desc);

  GIMP_DRAWABLE_CLASS (parent_class)->push_undo (drawable, undo_desc,
                                                 buffer,
                                                 x, y, width, height);

  if (! is_rasterized)
    {
      gimp_rasterizable_rasterize (GIMP_RASTERIZABLE (layer), TRUE);
      gimp_image_undo_group_end (image);
    }
}

static gint64
gimp_vector_layer_get_memsize (GimpObject *object,
                               gint64     *gui_size)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (object);
  gint64           memsize      = 0;

  memsize += gimp_object_get_memsize (GIMP_OBJECT (vector_layer->options),
                                      gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}

static void
gimp_vector_layer_convert (GimpItem  *item,
                           GimpImage *dest_image,
                           GType      old_type)
{
  GimpPath *path = gimp_vector_layer_get_path (GIMP_VECTOR_LAYER (item));

  if (path)
    {
      GIMP_ITEM_GET_CLASS (path)->convert (GIMP_ITEM (path), dest_image,
                                           G_TYPE_FROM_INSTANCE (path));

      if (! gimp_item_is_attached (GIMP_ITEM (path)))
        gimp_image_add_path (dest_image, path, NULL, -1, FALSE);
    }

  GIMP_ITEM_CLASS (parent_class)->convert (item, dest_image, old_type);
}

static GimpItem *
gimp_vector_layer_duplicate (GimpItem *item,
                             GType     new_type)
{
  GimpItem *new_item;

  g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);

  new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type);

  if (GIMP_IS_VECTOR_LAYER (new_item))
    {
      GimpVectorLayer *vector_layer     = GIMP_VECTOR_LAYER (item);
      GimpVectorLayer *new_vector_layer = GIMP_VECTOR_LAYER (new_item);

      if (vector_layer->options)
        {
          GimpPath               *path;
          GimpVectorLayerOptions *new_options =
            gimp_config_duplicate (GIMP_CONFIG (vector_layer->options));

          path = gimp_vector_layer_get_path (vector_layer);
          if (path)
            {
              GimpPath *new_path;

              new_path = GIMP_PATH (gimp_item_duplicate (GIMP_ITEM (path),
                                                         G_TYPE_FROM_INSTANCE (GIMP_ITEM (path))));

              g_object_set (new_options, "path", new_path, NULL);
            }

          g_object_set (new_vector_layer,
                        "vector-layer-options", new_options,
                        NULL);
          g_object_unref (new_options);
        }

      if (gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (vector_layer)))
        gimp_rasterizable_set_undo_rasterized (GIMP_RASTERIZABLE (new_vector_layer), TRUE);
    }

  return new_item;
}

static void
gimp_vector_layer_translate (GimpLayer *layer,
                             gint       offset_x,
                             gint       offset_y)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (layer);

  if (gimp_item_is_vector_layer (GIMP_ITEM (layer)))
    {
      gimp_item_translate (GIMP_ITEM (vector_layer->options->path),
                           offset_x, offset_y, FALSE);
    }
  else
    {
      GIMP_LAYER_CLASS (parent_class)->translate (layer, offset_x, offset_y);
    }
}

static void
gimp_vector_layer_scale (GimpItem              *item,
                         gint                   new_width,
                         gint                   new_height,
                         gint                   new_offset_x,
                         gint                   new_offset_y,
                         GimpInterpolationType  interp_type,
                         GimpProgress          *progress)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (item);

  if (gimp_item_is_vector_layer (item))
    {
      gdouble scale_x = new_width  / (gdouble) gimp_item_get_width  (item);
      gdouble scale_y = new_height / (gdouble) gimp_item_get_height (item);

      gimp_item_scale_by_factors (GIMP_ITEM (vector_layer->options->path),
                                  scale_x, scale_y,
                                  interp_type, progress);
    }
  else
    {
      GIMP_ITEM_CLASS (parent_class)->scale (item, new_width, new_height,
                                             new_offset_x, new_offset_y,
                                             interp_type, progress);
    }
}

static void
gimp_vector_layer_resize (GimpItem     *item,
                          GimpContext  *context,
                          GimpFillType  fill_type,
                          gint          new_width,
                          gint          new_height,
                          gint          offset_x,
                          gint          offset_y)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (item);

  if (gimp_item_is_vector_layer (item))
    {
      gimp_item_resize (GIMP_ITEM (vector_layer->options->path),
                        context, fill_type, new_width, new_height,
                        offset_x, offset_y);
    }
  else
    {
      GIMP_ITEM_CLASS (parent_class)->resize (item, context, fill_type,
                                              new_width, new_height, offset_x,
                                              offset_y);
    }
}

static void
gimp_vector_layer_flip (GimpItem            *item,
                        GimpContext         *context,
                        GimpOrientationType  flip_type,
                        gdouble              axis,
                        gboolean             clip_result)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (item);

  if (gimp_item_is_vector_layer (item))
    {
      gimp_item_flip (GIMP_ITEM (vector_layer->options->path),
                      context, flip_type, axis, clip_result);
    }
  else
    {
      GIMP_ITEM_CLASS (parent_class)->flip (item, context, flip_type,
                                            axis, clip_result);
    }
}

static void
gimp_vector_layer_rotate (GimpItem         *item,
                          GimpContext      *context,
                          GimpRotationType  rotate_type,
                          gdouble           center_x,
                          gdouble           center_y,
                          gboolean          clip_result)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (item);

  if (gimp_item_is_vector_layer (item))
    {
      gimp_item_rotate (GIMP_ITEM (vector_layer->options->path),
                        context, rotate_type, center_x, center_y, clip_result);
    }
  else
    {
      GIMP_ITEM_CLASS (parent_class)->rotate (item, context, rotate_type,
                                              center_x, center_y, clip_result);
    }
}

static void
gimp_vector_layer_transform (GimpItem               *item,
                             GimpContext            *context,
                             const GimpMatrix3      *matrix,
                             GimpTransformDirection  direction,
                             GimpInterpolationType   interp_type,
                             GimpTransformResize     clip_result,
                             GimpProgress           *progress,
                             gboolean                push_undo)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (item);

  if (gimp_item_is_vector_layer (item))
    {
      gimp_item_transform (GIMP_ITEM (vector_layer->options->path),
                           context, matrix, direction, interp_type,
                           clip_result, progress);
    }
  else
    {
      GIMP_ITEM_CLASS (parent_class)->transform (item, context, matrix,
                                                 direction, interp_type,
                                                 clip_result, progress, push_undo);
    }
}

static void
gimp_vector_layer_removed_options_path (GimpVectorLayer *layer)
{
  if (layer->options)
    {
      gimp_image_undo_push_vector_layer (gimp_item_get_image (GIMP_ITEM (layer)),
                                         _("Discard Vector Information"),
                                         layer, NULL);

      g_object_set (layer->options, "path", NULL, NULL);
    }
}

static void
gimp_vector_layer_removed (GimpItem *item)
{
  GimpVectorLayer *vector_layer = GIMP_VECTOR_LAYER (item);

  if (vector_layer->options && vector_layer->options->path)
    g_signal_handlers_disconnect_by_func (vector_layer->options->path,
                                          G_CALLBACK (gimp_vector_layer_removed_options_path),
                                          vector_layer);

  GIMP_ITEM_CLASS (parent_class)->removed (item);
}


/*  public functions  */

/**
 * gimp_vector_layer_new:
 * @image:   the #GimpImage the layer should belong to
 * @path:    the #GimpPath object the layer should render
 * @context: the #GimpContext from which to pull context properties
 *
 * Creates a new vector layer.
 *
 * Return value: a new #GimpVectorLayer or %NULL in case of a problem
 **/
GimpVectorLayer *
gimp_vector_layer_new (GimpImage   *image,
                       GimpPath    *path,
                       GimpContext *context)
{
  GimpVectorLayer        *layer;
  GimpVectorLayerOptions *options;
  gint                    x      = 0;
  gint                    y      = 0;
  gint                    width  = gimp_image_get_width (image);
  gint                    height = gimp_image_get_height (image);

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_PATH (path), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);

  options = gimp_vector_layer_options_new (image, path, context);

  gimp_item_bounds (GIMP_ITEM (path), &x, &y, &width, &height);

  /* Set boundaries to image size if it's a blank path */
  if (width == 0 || height == 0)
    {
      width  = gimp_image_get_width (image);
      height = gimp_image_get_height (image);
    }

  layer =
    GIMP_VECTOR_LAYER (gimp_drawable_new (GIMP_TYPE_VECTOR_LAYER,
                                          image, NULL,
                                          x, y, width, height,
                                          gimp_image_get_layer_format (image,
                                                                       TRUE)));

  gimp_object_set_name (GIMP_OBJECT (layer),
                        gimp_object_get_name (GIMP_OBJECT (path)));

  gimp_layer_set_mode (GIMP_LAYER (layer),
                       gimp_image_get_default_new_layer_mode (image),
                       FALSE);

  gimp_vector_layer_set_vector_options (layer, options);
  g_object_unref (options);

  return layer;
}

void
gimp_vector_layer_set_path (GimpVectorLayer *layer,
                            GimpPath        *path,
                            gboolean         push_undo)
{
  GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));

  if (push_undo)
    {
      GParamSpec *pspec;

      pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (layer->options), "path");
      gimp_image_undo_push_vector_layer (image,
                                         _("Change Vector Layer Path"),
                                         layer, pspec);
    }

  g_object_set (layer->options,
                "path", path,
                NULL);

  gimp_image_flush (image);
}

/**
 * gimp_vector_layer_get_path:
 * @layer: a #GimpVectorLayer
 *
 * Gets the path from @layer if one is associated with it.
 *
  * Return value: a #GimpPath or %NULL if no path is set.
 */
GimpPath *
gimp_vector_layer_get_path (GimpVectorLayer *layer)
{
  g_return_val_if_fail (GIMP_IS_VECTOR_LAYER (layer), NULL);

  return layer->options->path;
}

/**
 * gimp_vector_layer_get_options:
 * @layer: a #GimpVectorLayer
 *
 * Gets the vector layer options from @layer if one is associated with it.
 *
  * Return value: a #GimpVectorLayerOptions or %NULL if no options are set.
 */
GimpVectorLayerOptions *
gimp_vector_layer_get_options (GimpVectorLayer *layer)
{
  g_return_val_if_fail (GIMP_IS_VECTOR_LAYER (layer), NULL);

  if (gimp_item_is_vector_layer (GIMP_ITEM (layer)))
    return layer->options;

  return NULL;
}

void
gimp_vector_layer_set (GimpVectorLayer *layer,
                       const gchar     *undo_desc,
                       const gchar     *first_property_name,
                       ...)
{
  GimpImage              *image;
  GimpVectorLayerOptions *options;
  va_list                 var_args;

  g_return_if_fail (gimp_item_is_vector_layer (GIMP_ITEM (layer)));
  g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));

  options = layer->options;

  if (! options)
    return;

  image = gimp_item_get_image (GIMP_ITEM (layer));

  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_VECTOR, undo_desc);

  g_object_freeze_notify (G_OBJECT (layer));
  g_object_freeze_notify (G_OBJECT (options));

  if (gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer)))
    gimp_image_undo_push_drawable_mod (image, NULL,
                                       GIMP_DRAWABLE (layer), TRUE);

  gimp_image_undo_push_vector_layer (image, undo_desc, layer, NULL);

  va_start (var_args, first_property_name);
  g_object_set_valist (G_OBJECT (options), first_property_name,
                       var_args);
  va_end (var_args);

  if (gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer)))
    gimp_rasterizable_restore (GIMP_RASTERIZABLE (layer));

  g_object_thaw_notify (G_OBJECT (layer));
  g_object_thaw_notify (G_OBJECT (options));

  gimp_image_undo_group_end (image);
}

void
gimp_vector_layer_refresh (GimpVectorLayer *layer)
{
  if (layer->options)
    gimp_vector_layer_render (layer);
}

gboolean
gimp_item_is_vector_layer (GimpItem *item)
{
  return (GIMP_IS_VECTOR_LAYER (item) &&
          ! gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (item)));
}


/* private functions  */

static gboolean
gimp_vector_layer_render (GimpVectorLayer *layer)
{
  GimpDrawable *drawable = GIMP_DRAWABLE (layer);
  GeglBuffer   *buffer   = NULL;
  GimpItem     *item     = GIMP_ITEM (layer);
  GimpImage    *image    = gimp_item_get_image (item);
  gint          layer_x  = 0;
  gint          layer_y  = 0;
  gint          x        = 0;
  gint          y        = 0;
  gint          width    = gimp_image_get_width (image);
  gint          height   = gimp_image_get_height (image);
  gdouble       stroke   = 0;

  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);

  g_object_freeze_notify (G_OBJECT (drawable));

  if (layer->options->enable_stroke)
    stroke = gimp_stroke_options_get_width (layer->options->stroke_options);

  /* Resize layer according to path size */
  gimp_item_get_offset (GIMP_ITEM (layer), &layer_x, &layer_y);
  gimp_item_bounds (GIMP_ITEM (layer->options->path), &x, &y, &width, &height);

  buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                            (gint) ceil (width + stroke),
                                            (gint) ceil (height + stroke)),
                            gimp_drawable_get_format (drawable));
  gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer);
  g_object_unref (buffer);

  gimp_item_set_offset (GIMP_ITEM (layer), x - (stroke / 2), y - (stroke / 2));

  /* make the layer background transparent */
  gimp_drawable_fill (GIMP_DRAWABLE (layer),
                      gimp_get_user_context (image->gimp),
                      GIMP_FILL_TRANSPARENT);

  /* render path to the layer */
  gimp_vector_layer_render_path (layer);

  g_object_thaw_notify (G_OBJECT (drawable));

  return TRUE;
}

static void
gimp_vector_layer_render_path (GimpVectorLayer *layer)
{
  GimpImage              *image     = gimp_item_get_image (GIMP_ITEM (layer));
  GimpVectorLayerOptions *options   = layer->options;
  GimpPath               *path      = NULL;
  GimpChannel            *selection = gimp_image_get_mask (image);
  GList                  *drawables;
  GimpCustomStyle         style;

  if (options)
    path = options->path;

  /* Don't mask these fill/stroke operations  */
  gimp_selection_suspend (GIMP_SELECTION (selection));

  /* Convert from custom to standard styles */
  style = gimp_fill_options_get_custom_style (options->fill_options);
  if (style == GIMP_CUSTOM_STYLE_SOLID_COLOR ||
      ! gimp_context_get_pattern (GIMP_CONTEXT (options->fill_options)))
    gimp_fill_options_set_style (options->fill_options,
                                 GIMP_FILL_STYLE_FG_COLOR);
  else
    gimp_fill_options_set_style (options->fill_options,
                                 GIMP_FILL_STYLE_PATTERN);

  style =
    gimp_fill_options_get_custom_style (GIMP_FILL_OPTIONS (options->stroke_options));
  if (style == GIMP_CUSTOM_STYLE_SOLID_COLOR ||
      ! gimp_context_get_pattern (GIMP_CONTEXT (options->stroke_options)))
    gimp_fill_options_set_style (GIMP_FILL_OPTIONS (options->stroke_options),
                                 GIMP_FILL_STYLE_FG_COLOR);
  else
    gimp_fill_options_set_style (GIMP_FILL_OPTIONS (options->stroke_options),
                                 GIMP_FILL_STYLE_PATTERN);

  /* Fill the path object onto the layer */
  if (options->enable_fill)
    gimp_drawable_fill_path (GIMP_DRAWABLE (layer),
                             options->fill_options,
                             path, FALSE, NULL);

  drawables = g_list_prepend (NULL, GIMP_DRAWABLE (layer));
  /* stroke the path object onto the layer */
  if (options->enable_stroke && gimp_item_is_attached (GIMP_ITEM (path)))
    gimp_item_stroke (GIMP_ITEM (path), drawables,
                      gimp_get_user_context (image->gimp),
                      options->stroke_options,
                      FALSE, FALSE,
                      NULL, NULL);

  g_list_free (drawables);

  gimp_rasterizable_auto_rename (GIMP_RASTERIZABLE (layer),
                                 GIMP_OBJECT (layer->options->path),
                                 NULL);

  gimp_selection_resume (GIMP_SELECTION (selection));
}

static void
gimp_vector_layer_changed_options (GimpVectorLayer *layer)
{
  GimpItem *item = GIMP_ITEM (layer);

  if (layer->options && ! layer->options->path)
    gimp_rasterizable_rasterize (GIMP_RASTERIZABLE (layer), TRUE);
  else if (gimp_item_is_attached (item))
    gimp_vector_layer_refresh (layer);
}
