/* This file is part of GEGL
 *
 * GEGL is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * GEGL 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
 *
 * Copyright 2011 Michael Muré <batolettre@gmail.com>
 * Copyright 2007 Øyvind Kolås <pippin@gimp.org>
 */

/***
 * GeglPath:
 *
 * GeglPath is GEGLs means of storing the nodes and other knots and the
 * instructions for rendering 2d paths like poly lines, bezier curves and other
 * curve representations.
 */

#ifndef __GEGL_PATH_H__
#define __GEGL_PATH_H__

#include <glib-object.h>
#include <gegl-matrix.h>

G_BEGIN_DECLS

#define GEGL_TYPE_PATH            (gegl_path_get_type ())
#define GEGL_PATH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_PATH, GeglPath))
#define GEGL_PATH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GEGL_TYPE_PATH, GeglPathClass))
#define GEGL_IS_PATH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_PATH))
#define GEGL_IS_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GEGL_TYPE_PATH))
#define GEGL_PATH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GEGL_TYPE_PATH, GeglPathClass))

typedef struct _GeglPathClass  GeglPathClass;

struct _GeglPath
{
  GObject parent_instance;
};

GType                gegl_path_get_type       (void) G_GNUC_CONST;

/* Internally the following structures are used, parts
 * of the internal implementation are exposed through
 * the path access API. The linked list api is currently
 * only used for adding new path interpolators/flatteners
 * with new knot interpretations.
 */

/***
 * GeglPathItem:
 *
 * A #GeglPathItem contains the type of instruction to perform as
 * well as it's arguments. In the public API the PathItem always has
 * 4 points internally only the needed amount of memory is stored
 * for a GeglPathItem.
 *
 *  </p><pre>typedef struct GeglPathPoint
 * {
 *   gfloat x;
 *   gfloat y;
 * } GeglPathPoint;</pre></p>
 * </p><pre>typedef struct GeglPathItem
 * {
 *   gchar  type;
 *   GeglPathPoint  point[4];
 * } GeglPathItem;</pre></p>
 *
 */


typedef struct GeglPathPoint
{
  gfloat x;
  gfloat y;
} GeglPathPoint;

typedef struct GeglPathItem
{
  gchar  type;     /* should perhaps be padded out? */
  GeglPathPoint  point[4]; /* Note: internally GeglPath operates with paths that
                    * have the exact number of pairs allocated.
                    */
} GeglPathItem;


/**
 * gegl_path_new:
 *
 * Creates a new #GeglPath with no nodes.
 *
 * Returns the newly created #GeglPath
 */
GeglPath           * gegl_path_new            (void);

/**
 * gegl_path_new_from_string:
 * @instructions: a string describing the path.
 *
 * Creates a new #GeglPath with the nodes described in the string
 * @instructions. See gegl_path_parse_string() for details of the
 * format of the string.
 *
 * Returns the newly created #GeglPath
 */
GeglPath           * gegl_path_new_from_string(const gchar *instructions);

/**
 * gegl_path_is_empty:
 * @path: a #GeglPath
 *
 * Check if the path contains any nodes.
 *
 * Returns TRUE if the path has no nodes.
 */
gboolean             gegl_path_is_empty       (GeglPath    *path);

/**
 * gegl_path_get_n_nodes:
 * @path: a #GeglPath
 *
 * Retrieves the number of nodes in the path.
 *
 * Return value: the number of nodes in the path.
 */
gint                 gegl_path_get_n_nodes    (GeglPath    *path);

/**
 * gegl_path_get_length:
 * @path: a #GeglPath
 *
 * Returns the total length of the path.
 *
 * Return value: the length of the path.
 */
gdouble              gegl_path_get_length     (GeglPath     *path);

/**
 * gegl_path_get_node:
 * @path: a #GeglPath
 * @index: the node number to retrieve
 * @node: (out): a pointer to a #GeglPathItem record to be written.
 *
 * Retrieve the node of the path at position @pos.
 *
 * Returns TRUE if the node was successfully retrieved.
 */
gboolean             gegl_path_get_node       (GeglPath     *path,
                                               gint          index,
                                               GeglPathItem *node);

/**
 * gegl_path_to_string:
 * @path: a #GeglPath
 *
 * Serialize the paths nodes to a string.
 *
 * Return value: return a string with instructions describing the string you
 * need to free this with g_free().
 */
gchar              * gegl_path_to_string      (GeglPath    *path);


/**
 * gegl_path_set_matrix:
 * @path: a #GeglPath
 * @matrix: (in) (transfer none): a #GeglMatrix3 to copy the matrix from
 *
 * Set the transformation matrix of the path.
 *
 * The path is transformed through this matrix when being evaluated,
 * causing the calculated positions and length to be changed by the transform.
 */
void                 gegl_path_set_matrix     (GeglPath    *path,
                                               GeglMatrix3 *matrix);

/**
 * gegl_path_get_matrix:
 * @path: a #GeglPath
 * @matrix: (out caller-allocates): a #GeglMatrix3 to copy the matrix into
 *
 * Get the transformation matrix of the path.
 */
void                 gegl_path_get_matrix     (GeglPath    *path,
                                               GeglMatrix3 *matrix);

/**
 * gegl_path_closest_point:
 * @path: a #GeglPath
 * @x: x coordinate.
 * @y: y coordinate
 * @on_path_x: (out): return location for x coordinate on the path that was closest
 * @on_path_y: (out): return location for y coordinate on the path that was closest
 * @node_pos_before: (out): the node position interpreted before this position
 * was deemed the closest coordinate.
 *
 * Figure out what and where on a path is closest to arbitrary coordinates.
 *
 * Returns the length along the path where the closest point was encountered.
 */
gdouble              gegl_path_closest_point  (GeglPath     *path,
                                               gdouble       x,
                                               gdouble       y,
                                               gdouble      *on_path_x,
                                               gdouble      *on_path_y,
                                               gint         *node_pos_before);


/**
 * gegl_path_calc_y_for_x:
 * @path: a #GeglPath
 * @x: x coordinate to compute for
 * @y: (out): return location for y coordinate
 *
 * Compute a corresponding y coordinate for a given x input coordinate,
 * returns 0 if computed correctly and -1 if the path doesn't exist for the
 * specified x coordinate.
 */
gint gegl_path_calc_y_for_x (GeglPath *path,
                             gdouble   x,
                             gdouble  *y);

/**
 * gegl_path_calc:
 * @path: a #GeglPath
 * @pos: how far along the path.
 * @x: (out): return location for x coordinate.
 * @y: (out): return location for y coordinate
 *
 * Compute the coordinates of the path at the @position (length measured from
 * start of path, not including discontinuities).
 */
gboolean             gegl_path_calc           (GeglPath     *path,
                                               gdouble       pos,
                                               gdouble      *x,
                                               gdouble      *y);

/**
 * gegl_path_calc_values: (skip)
 * @path: a #GeglPath
 * @num_samples: number of samples to compute
 * @xs: (out caller-allocates) (array length=num_samples): return location for x coordinates
 * @ys: (out caller-allocates) (array length=num_samples): return location for y coordinates
 *
 * Compute @num_samples for a path into the provided arrays @xs and @ys
 * the returned values include the start and end positions of the path.
 */
void                 gegl_path_calc_values    (GeglPath    *path,
                                               guint        num_samples,
                                               gdouble     *xs,
                                               gdouble     *ys);

/**
 * gegl_path_get_bounds:
 * @self: a #GeglPath.
 * @min_x: (out): return location for minimum x coordinate
 * @max_x: (out): return location for maximum x coordinate
 * @min_y: (out): return location for minimum y coordinate
 * @max_y: (out): return location for maximum y coordinate
 *
 * Compute the bounding box of a path.
 */
void                 gegl_path_get_bounds     (GeglPath     *self,
                                               gdouble      *min_x,
                                               gdouble      *max_x,
                                               gdouble      *min_y,
                                               gdouble      *max_y);

/* XXX: LP and RP are nasty hacks because the GEGL docs code scanner gets
 * confused, need to be fixed there
 */
#define LP (
#define RP )
typedef void LP *GeglNodeFunction RP LP const GeglPathItem *node,
                                      gpointer            user_data RP;
#undef LP
#undef RP

/**
 * gegl_path_foreach:
 * @path: a #GeglPath
 * @each_item: (closure user_data) (scope call): a function to call for each node in the path.
 * @user_data: user data to pass to the function (in addition to the GeglPathItem).
 *
 * Execute a provided function for every node in the path (useful for
 * drawing and otherwise traversing a path.)
 */
void                 gegl_path_foreach        (GeglPath        *path,
                                               GeglNodeFunction each_item,
                                               gpointer         user_data);

/**
 * gegl_path_foreach_flat:
 * @path: a #GeglPath
 * @each_item: (closure user_data) (scope call): a function to call for each node in the path.
 * @user_data: user data to pass to a node.
 *
 * Execute a provided function for the segments of a poly line approximating
 * the path.
 */
void                 gegl_path_foreach_flat   (GeglPath        *path,
                                               GeglNodeFunction each_item,
                                               gpointer         user_data);


/**
 * gegl_path_clear:
 * @path: a #GeglPath
 *
 * Remove all nods from a @path.
 */
void                 gegl_path_clear          (GeglPath    *path);

/**
 * gegl_path_insert_node:
 * @path: a #GeglPath
 * @pos: the position we want the new node to have.
 * @node: pointer to a structure describing the GeglPathItem we want to store
 *
 * Insert the new node @node at position @pos in @path.
 * if @pos = -1, the node is added in the last position.
 */
void                 gegl_path_insert_node    (GeglPath           *path,
                                               gint                pos,
                                               const GeglPathItem *node);
/**
 * gegl_path_replace_node:
 * @path: a #GeglPath
 * @pos: the position we want the new node to have.
 * @node: pointer to a structure describing the GeglPathItem we want to store.
 *
 * Replaces the exiting node at position @pos in @path.
 */
void                 gegl_path_replace_node   (GeglPath           *path,
                                               gint                pos,
                                               const GeglPathItem *node);
/**
 * gegl_path_remove_node:
 * @path: a #GeglPath
 * @pos: a node in the path.
 *
 * Removes the node number @pos in @path.
 */
void                 gegl_path_remove_node    (GeglPath    *path,
                                               gint         pos);

/**
 * gegl_path_parse_string:
 * @path: a #GeglPath
 * @instructions: a string describing a path.
 *
 * Parses @instructions and appends corresponding nodes to path (call
 * gegl_path_clean() first if you want to replace the existing path.
 */
void                 gegl_path_parse_string   (GeglPath    *path,
                                               const gchar *instructions);

/**
 * gegl_path_append:
 * @path: a #GeglPath
 * @...: first instruction.
 *
 * Use as follows: gegl_path_append (path, 'M', 0.0, 0.0);
 * and gegl_path_append (path, 'C', 10.0, 10.0, 50.0, 10.0, 60.0, 0.0) the
 * number of arguments are determined from the instruction provided.
 */
void                 gegl_path_append         (GeglPath    *path,
                                                            ...);

/**
 * gegl_path_freeze:
 * @path: a @GeglPath
 *
 * Make the @GeglPath stop firing signals as it changes must be paired with a
 * gegl_path_thaw() for the signals to start again.
 */
void                  gegl_path_freeze        (GeglPath *path);

/**
 * gegl_path_thaw:
 * @path: a @GeglPath
 *
 * Restart firing signals (unless the path has been frozen multiple times).
 */
void                  gegl_path_thaw          (GeglPath *path);

/**
 * gegl_param_spec_path:
 * @name: canonical name of the property specified
 * @nick: nick name for the property specified
 * @blurb: description of the property specified
 * @default_path: the default value for the property specified
 * @flags: flags for the property specified
 *
 * Creates a new #GParamSpec instance specifying a #GeglPath property.
 *
 * Returns: (transfer full): a newly created parameter specification
 */
GParamSpec         *  gegl_param_spec_path    (const gchar *name,
                                               const gchar *nick,
                                               const gchar *blurb,
                                               GeglPath    *default_path,
                                               GParamFlags  flags);

#define GEGL_TYPE_PARAM_PATH    (gegl_param_path_get_type ())
#define GEGL_IS_PARAM_PATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_PARAM_PATH))
GType                 gegl_param_path_get_type (void) G_GNUC_CONST;

/**
 * gegl_path_add_type:
 * @type: a gchar to recognize in path descriptions.
 * @items: the number of floating point data items the instruction takes
 * @description: a human readable description of this entry
 *
 * Adds a new type to the path system, FIXME this should probably
 * return something on registration conflicts, for now it expects
 * all registered paths to be aware of each other.
 */
void                  gegl_path_add_type      (gchar        type,
                                               gint         items,
                                               const gchar *description);

/***
 * GeglPathList: (skip)
 *  
 * Linked list used internally, and for the plug-in API for new path
 * interpolators.
 */
typedef struct GeglPathList
{
  struct GeglPathList *next;
  GeglPathItem         d;
} GeglPathList;


/**
 * gegl_path_list_append: (skip)
 * @head: a #GeglPathList
 * @...: additional #GeglPathList items to append
 *
 * Appends to path list, if head is NULL a new list is created
 */ 
GeglPathList *     gegl_path_list_append   (GeglPathList *head, ...);

/**
 * gegl_path_list_destroy: (skip)
 * @path: A #GeglPathList
 *
 * Frees up a path list
 */
GeglPathList *     gegl_path_list_destroy  (GeglPathList *path);


/***
 * GeglFlattenerFunc: (skip)
 *
 * prototype of function passed to gegl_path_add_flattener()
 */
typedef GeglPathList *(*GeglFlattenerFunc) (GeglPathList *original);

/** 
 * gegl_path_add_flattener: (skip)
 * @func: a #GeglFlattenerFunc
 *
 * Add a new flattener, the flattener should produce a type of path that
 * GeglPath already understands, if the flattener is unable to flatten
 * the incoming path (doesn't understand the instructions), the original
 * path should be returned.
 */
void           gegl_path_add_flattener (GeglFlattenerFunc func);


/**
 * gegl_path_get_path: (skip)
 * @path: a #GeglPath
 *
 * Return the internal untouched #GeglPathList
 */
GeglPathList * gegl_path_get_path (GeglPath *path);

/**
 * gegl_path_get_flat_path: (skip)
 * @path: a #GeglPath
 *
 * Return a polyline version of @path
 */
GeglPathList * gegl_path_get_flat_path (GeglPath *path);

/***
 * GeglPathPoint: (skip)
 */

/**
 * gegl_path_point_lerp: (skip)
 * @dest: return location for the result
 * @a: origin GeglPathPoint
 * @b: destination GeglPathPoint
 * @t: ratio between @a and @b
 *
 * linear interpolation between two #GeglPathPoint
 */
void           gegl_path_point_lerp    (GeglPathPoint *dest,
                                        GeglPathPoint *a,
                                        GeglPathPoint *b,
                                        gfloat         t);

/**
 * gegl_path_point_dist: (skip)
 * @a: an arbitrary GeglPathPoint
 * @b: an arbitrary GeglPathPoint
 *
 * Compute the distance between #GeglPathPoint @a and @b
 */
gdouble        gegl_path_point_dist    (GeglPathPoint *a,
                                        GeglPathPoint *b);


/**
 * gegl_path_dirty:
 * @path: a #GeglPath
 *
 * Marks the path as dirty and issues an invalidation for the path rendering,
 * use this if modifying the values of a GeglPathPoint inline.
 */
void
gegl_path_dirty (GeglPath *path);


G_END_DECLS

#endif /* __GEGL_PATH_H__ */
