/* 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/>.
 */

#ifndef __PIKA_PAINT_CORE_H__
#define __PIKA_PAINT_CORE_H__


#include "core/pikaobject.h"


#define PIKA_TYPE_PAINT_CORE            (pika_paint_core_get_type ())
#define PIKA_PAINT_CORE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_PAINT_CORE, PikaPaintCore))
#define PIKA_PAINT_CORE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_PAINT_CORE, PikaPaintCoreClass))
#define PIKA_IS_PAINT_CORE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_PAINT_CORE))
#define PIKA_IS_PAINT_CORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_PAINT_CORE))
#define PIKA_PAINT_CORE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_PAINT_CORE, PikaPaintCoreClass))


typedef struct _PikaPaintCoreClass PikaPaintCoreClass;

struct _PikaPaintCore
{
  PikaObject      parent_instance;

  gint            ID;                /*  unique instance ID                  */

  gchar          *undo_desc;         /*  undo description                    */

  gboolean        show_all;          /*  whether working in show-all mode    */

  PikaCoords      start_coords;      /*  the last stroke's endpoint for undo */

  PikaCoords      cur_coords;        /*  current coords                      */
  PikaCoords      last_coords;       /*  last coords                         */

  PikaVector2     last_paint;        /*  last point that was painted         */

  gdouble         distance;          /*  distance traveled by brush          */
  gdouble         pixel_dist;        /*  distance in pixels                  */

  gint            x1, y1;            /*  undo extents in image coords        */
  gint            x2, y2;            /*  undo extents in image coords        */

  gboolean        use_saved_proj;    /*  keep the unmodified proj around     */

  PikaPickable   *image_pickable;    /*  the image pickable                  */

  GHashTable     *undo_buffers;      /*  pixels which have been modified     */
  GeglBuffer     *saved_proj_buffer; /*  proj tiles which have been modified */
  GeglBuffer     *canvas_buffer;     /*  the buffer to paint the mask to     */
  GeglBuffer     *paint_buffer;      /*  the buffer to paint pixels to       */
  gint            paint_buffer_x;
  gint            paint_buffer_y;

  GeglBuffer     *mask_buffer;       /*  the target drawable's mask          */

  GHashTable     *applicators;

  GArray         *stroke_buffer;
};

struct _PikaPaintCoreClass
{
  PikaObjectClass  parent_class;

  /*  virtual functions  */
  gboolean     (* start)            (PikaPaintCore    *core,
                                     GList            *drawables,
                                     PikaPaintOptions *paint_options,
                                     const PikaCoords *coords,
                                     GError          **error);

  gboolean     (* pre_paint)        (PikaPaintCore    *core,
                                     GList            *drawables,
                                     PikaPaintOptions *paint_options,
                                     PikaPaintState    paint_state,
                                     guint32           time);
  void         (* paint)            (PikaPaintCore    *core,
                                     GList            *drawables,
                                     PikaPaintOptions *paint_options,
                                     PikaSymmetry     *sym,
                                     PikaPaintState    paint_state,
                                     guint32           time);
  void         (* post_paint)       (PikaPaintCore    *core,
                                     GList            *drawables,
                                     PikaPaintOptions *paint_options,
                                     PikaPaintState    paint_state,
                                     guint32           time);

  void         (* interpolate)      (PikaPaintCore    *core,
                                     GList            *drawables,
                                     PikaPaintOptions *paint_options,
                                     guint32           time);

  GeglBuffer * (* get_paint_buffer) (PikaPaintCore    *core,
                                     PikaDrawable     *drawable,
                                     PikaPaintOptions *paint_options,
                                     PikaLayerMode     paint_mode,
                                     const PikaCoords *coords,
                                     gint             *paint_buffer_x,
                                     gint             *paint_buffer_y,
                                     gint             *paint_width,
                                     gint             *paint_height);

  PikaUndo   * (* push_undo)        (PikaPaintCore    *core,
                                     PikaImage        *image,
                                     const gchar      *undo_desc);
};


GType     pika_paint_core_get_type                  (void) G_GNUC_CONST;

void      pika_paint_core_paint                     (PikaPaintCore    *core,
                                                     GList            *drawables,
                                                     PikaPaintOptions *paint_options,
                                                     PikaPaintState    state,
                                                     guint32           time);

gboolean  pika_paint_core_start                     (PikaPaintCore    *core,
                                                     GList            *drawables,
                                                     PikaPaintOptions *paint_options,
                                                     const PikaCoords *coords,
                                                     GError          **error);
void      pika_paint_core_finish                    (PikaPaintCore    *core,
                                                     GList            *drawables,
                                                     gboolean          push_undo);
void      pika_paint_core_cancel                    (PikaPaintCore    *core,
                                                     GList            *drawables);
void      pika_paint_core_cleanup                   (PikaPaintCore    *core);

void      pika_paint_core_interpolate               (PikaPaintCore    *core,
                                                     GList            *drawables,
                                                     PikaPaintOptions *paint_options,
                                                     const PikaCoords *coords,
                                                     guint32           time);

void      pika_paint_core_set_show_all              (PikaPaintCore    *core,
                                                     gboolean          show_all);
gboolean  pika_paint_core_get_show_all              (PikaPaintCore    *core);

void      pika_paint_core_set_current_coords        (PikaPaintCore    *core,
                                                     const PikaCoords *coords);
void      pika_paint_core_get_current_coords        (PikaPaintCore    *core,
                                                     PikaCoords       *coords);

void      pika_paint_core_set_last_coords           (PikaPaintCore    *core,
                                                     const PikaCoords *coords);
void      pika_paint_core_get_last_coords           (PikaPaintCore    *core,
                                                     PikaCoords       *coords);

void      pika_paint_core_round_line                (PikaPaintCore    *core,
                                                     PikaPaintOptions *options,
                                                     gboolean          constrain_15_degrees,
                                                     gdouble           constrain_offset_angle,
                                                     gdouble           constrain_xres,
                                                     gdouble           constrain_yres);


/*  protected functions  */

GeglBuffer * pika_paint_core_get_paint_buffer       (PikaPaintCore    *core,
                                                     PikaDrawable     *drawable,
                                                     PikaPaintOptions *options,
                                                     PikaLayerMode     paint_mode,
                                                     const PikaCoords *coords,
                                                     gint             *paint_buffer_x,
                                                     gint             *paint_buffer_y,
                                                     gint             *paint_width,
                                                     gint             *paint_height);

PikaPickable * pika_paint_core_get_image_pickable   (PikaPaintCore    *core);

GeglBuffer * pika_paint_core_get_orig_image         (PikaPaintCore    *core,
                                                     PikaDrawable     *drawable);
GeglBuffer * pika_paint_core_get_orig_proj          (PikaPaintCore    *core);

void      pika_paint_core_paste             (PikaPaintCore            *core,
                                             const PikaTempBuf        *paint_mask,
                                             gint                      paint_mask_offset_x,
                                             gint                      paint_mask_offset_y,
                                             PikaDrawable             *drawable,
                                             gdouble                   paint_opacity,
                                             gdouble                   image_opacity,
                                             PikaLayerMode             paint_mode,
                                             PikaPaintApplicationMode  mode);

void      pika_paint_core_replace           (PikaPaintCore            *core,
                                             const PikaTempBuf        *paint_mask,
                                             gint                      paint_mask_offset_x,
                                             gint                      paint_mask_offset_y,
                                             PikaDrawable             *drawable,
                                             gdouble                   paint_opacity,
                                             gdouble                   image_opacity,
                                             PikaPaintApplicationMode  mode);

void      pika_paint_core_smooth_coords             (PikaPaintCore    *core,
                                                     PikaPaintOptions *paint_options,
                                                     PikaCoords       *coords);


#endif  /*  __PIKA_PAINT_CORE_H__  */
