/*
 * Copyright © 2025 Red Hat, Inc
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * Authors: Matthias Clasen <mclasen@redhat.com>
 */

#pragma once

#include "gtksvg.h"
#include "gtkenums.h"

G_BEGIN_DECLS

#define INDEFINITE G_MAXINT64
#define REPEAT_FOREVER INFINITY

typedef struct _SvgValue SvgValue;
typedef struct _Shape Shape;
typedef struct _Timeline Timeline;

typedef enum
{
  ALIGN_MIN = 1 << 0,
  ALIGN_MID = 1 << 1,
  ALIGN_MAX = 1 << 2,
} Align;

typedef enum
{
  MEET,
  SLICE,
} MeetOrSlice;

typedef enum
{
  GTK_SVG_RUN_MODE_STOPPED,
  GTK_SVG_RUN_MODE_DISCRETE,
  GTK_SVG_RUN_MODE_CONTINUOUS,
} GtkSvgRunMode;

struct _GtkSvg
{
  GObject parent_instance;
  Shape *content;

  double width, height;
  graphene_rect_t view_box;
  graphene_rect_t bounds;

  Align align;
  MeetOrSlice meet_or_slice;

  double weight;
  unsigned int state;
  unsigned int max_state;
  int64_t state_change_delay;

  int64_t load_time;
  int64_t current_time;

  gboolean playing;
  GtkSvgRunMode run_mode;
  GdkFrameClock *clock;
  unsigned long clock_update_id;
  unsigned int periodic_update_id;

  int64_t next_update;
  unsigned int pending_invalidate;
  gboolean advance_after_snapshot;

  unsigned int gpa_version;
  char *gpa_keywords;

  Timeline *timeline;
};

typedef enum
{
  SHAPE_LINE,
  SHAPE_POLY_LINE,
  SHAPE_POLYGON,
  SHAPE_RECT,
  SHAPE_CIRCLE,
  SHAPE_ELLIPSE,
  SHAPE_PATH,
  SHAPE_GROUP,
  SHAPE_CLIP_PATH,
  SHAPE_MASK,
  SHAPE_DEFS,
  SHAPE_USE,
  SHAPE_LINEAR_GRADIENT,
  SHAPE_RADIAL_GRADIENT,
} ShapeType;

typedef enum
{
  SHAPE_ATTR_VISIBILITY,
  SHAPE_ATTR_TRANSFORM,
  SHAPE_ATTR_OPACITY,
  SHAPE_ATTR_FILTER,
  SHAPE_ATTR_CLIP_PATH,
  SHAPE_ATTR_CLIP_RULE,
  SHAPE_ATTR_MASK,
  SHAPE_ATTR_MASK_TYPE,
  SHAPE_ATTR_FILL,
  SHAPE_ATTR_FILL_OPACITY,
  SHAPE_ATTR_FILL_RULE,
  SHAPE_ATTR_STROKE,
  SHAPE_ATTR_STROKE_OPACITY,
  SHAPE_ATTR_STROKE_WIDTH,
  SHAPE_ATTR_STROKE_LINECAP,
  SHAPE_ATTR_STROKE_LINEJOIN,
  SHAPE_ATTR_STROKE_MITERLIMIT,
  SHAPE_ATTR_STROKE_DASHARRAY,
  SHAPE_ATTR_STROKE_DASHOFFSET,
  SHAPE_ATTR_PAINT_ORDER,
  SHAPE_ATTR_HREF,
  SHAPE_ATTR_PATH_LENGTH,
  SHAPE_ATTR_PATH,
  SHAPE_ATTR_CX,
  SHAPE_ATTR_CY,
  SHAPE_ATTR_R,
  SHAPE_ATTR_X,
  SHAPE_ATTR_Y,
  SHAPE_ATTR_WIDTH,
  SHAPE_ATTR_HEIGHT,
  SHAPE_ATTR_RX,
  SHAPE_ATTR_RY,
  SHAPE_ATTR_X1,
  SHAPE_ATTR_Y1,
  SHAPE_ATTR_X2,
  SHAPE_ATTR_Y2,
  SHAPE_ATTR_POINTS,
  SHAPE_ATTR_SPREAD_METHOD,
  SHAPE_ATTR_GRADIENT_UNITS,
  SHAPE_ATTR_FX,
  SHAPE_ATTR_FY,
  SHAPE_ATTR_FR,
  /* Things below are custom */
  SHAPE_ATTR_STROKE_MINWIDTH,
  SHAPE_ATTR_STROKE_MAXWIDTH,
  SHAPE_ATTR_STOP_OFFSET,
  SHAPE_ATTR_STOP_COLOR,
  SHAPE_ATTR_STOP_OPACITY,
} ShapeAttr;

#define N_SHAPE_ATTRS (SHAPE_ATTR_STOP_OPACITY + 1)

typedef enum
{
  GPA_TRANSITION_NONE,
  GPA_TRANSITION_ANIMATE,
  GPA_TRANSITION_MORPH,
  GPA_TRANSITION_FADE,
} GpaTransition;

typedef enum
{
  GPA_ANIMATION_NONE,
  GPA_ANIMATION_NORMAL,
  GPA_ANIMATION_ALTERNATE,
  GPA_ANIMATION_REVERSE,
  GPA_ANIMATION_REVERSE_ALTERNATE,
  GPA_ANIMATION_IN_OUT,
  GPA_ANIMATION_IN_OUT_ALTERNATE,
  GPA_ANIMATION_IN_OUT_REVERSE,
  GPA_ANIMATION_SEGMENT,
  GPA_ANIMATION_SEGMENT_ALTERNATE,
} GpaAnimation;

typedef enum
{
  GPA_EASING_LINEAR,
  GPA_EASING_EASE_IN_OUT,
  GPA_EASING_EASE_IN,
  GPA_EASING_EASE_OUT,
  GPA_EASING_EASE,
} GpaEasing;

struct _Shape
{
  ShapeType type;
  Shape *parent;
  uint64_t attrs;
  char *id;
  gboolean display;

  /* Dependency order for computing updates */
  Shape *first;
  Shape *next;

  SvgValue *base[N_SHAPE_ATTRS];
  SvgValue *current[N_SHAPE_ATTRS];

  GPtrArray *shapes;
  GPtrArray *animations;
  GPtrArray *color_stops;
  GPtrArray *deps;

  GskPath *path;
  GskPathMeasure *measure;
  union {
    struct {
      double cx, cy, r;
    } circle;
    struct {
      double cx, cy, rx, ry;
    } ellipse;
    struct {
      double x, y, w, h, rx, ry;
    } rect;
    struct {
      double x1, y1, x2, y2;
    } line;
    struct {
      SvgValue *points;
    } polyline;
  } path_for;

  struct {
    uint64_t states;
    GpaTransition transition;
    GpaEasing transition_easing;
    int64_t transition_duration;
    int64_t transition_delay;
    GpaAnimation animation;
    GpaEasing animation_easing;
    int64_t animation_duration;
    double animation_repeat;
    double animation_segment;
    double origin;
    struct {
      char *ref;
      Shape *shape;
      double pos;
    } attach;
  } gpa;
};

typedef enum
{
  SVG_UNIT_NONE,
  SVG_UNIT_PERCENT,
  SVG_UNIT_LENGTH,
} SvgUnit;

typedef enum
{
  PAINT_NONE,
  PAINT_COLOR,
  PAINT_SYMBOLIC,
  PAINT_GRADIENT,
} PaintKind;

typedef enum
{
  PAINT_ORDER_NORMAL,
  PAINT_ORDER_REVERSE,
} PaintOrder;

typedef enum
{
  CLIP_NONE,
  CLIP_PATH,
  CLIP_REF,
} ClipKind;

double
svg_shape_attr_get_number (Shape                 *shape,
                           ShapeAttr              attr,
                           const graphene_size_t *viewport);

GskPath *
svg_shape_attr_get_path   (Shape                 *shape,
                           ShapeAttr              attr);

unsigned int
svg_shape_attr_get_enum   (Shape                 *shape,
                           ShapeAttr              attr);

PaintKind
svg_shape_attr_get_paint  (Shape                 *shape,
                           ShapeAttr              attr,
                           GtkSymbolicColor      *symbolic,
                           GdkRGBA               *color);

double *
svg_shape_attr_get_points (Shape                 *shape,
                           ShapeAttr              attr,
                           unsigned int          *n_params);

ClipKind
svg_shape_attr_get_clip   (Shape                 *shape,
                           ShapeAttr              attr,
                           GskPath              **path);

char *
svg_shape_attr_get_transform (Shape              *shape,
                              ShapeAttr           attr);

char *
svg_shape_attr_get_filter    (Shape              *shape,
                              ShapeAttr           attr);

GskPath *
svg_shape_get_path        (Shape                 *shape,
                           const graphene_size_t *viewport);

void
svg_shape_attr_set        (Shape                 *shape,
                           ShapeAttr              attr,
                           SvgValue              *value);

void       svg_value_unref (SvgValue *value);

SvgValue * svg_number_new (double value);
SvgValue * svg_linecap_new (GskLineCap value);
SvgValue * svg_linejoin_new (GskLineJoin value);
SvgValue * svg_fill_rule_new (GskFillRule rule);
SvgValue * svg_paint_order_new (PaintOrder order);
SvgValue * svg_paint_new_none (void);
SvgValue * svg_paint_new_symbolic (GtkSymbolicColor symbolic);
SvgValue * svg_paint_new_rgba (const GdkRGBA *rgba);
SvgValue * svg_points_new (double *values,
                           unsigned int n_values);
SvgValue * svg_path_new (GskPath *path);
SvgValue * svg_clip_new_none (void);
SvgValue * svg_clip_new_path (GskPath *path);
SvgValue * svg_transform_parse (const char *value);
SvgValue * svg_filter_parse (const char *value);

Shape * svg_shape_add (Shape     *parent,
                       ShapeType  type);

void    svg_shape_delete (Shape  *shape);

/* --- */

void           gtk_svg_set_load_time   (GtkSvg                *self,
                                        int64_t                load_time);

void           gtk_svg_set_playing     (GtkSvg                *self,
                                        gboolean               playing);

void           gtk_svg_advance         (GtkSvg                *self,
                                        int64_t                current_time);

GtkSvgRunMode  gtk_svg_get_run_mode    (GtkSvg *self);

int64_t        gtk_svg_get_next_update (GtkSvg *self);

typedef enum
{
  GTK_SVG_SERIALIZE_DEFAULT           = 0,
  GTK_SVG_SERIALIZE_AT_CURRENT_TIME   = 1 << 0,
  GTK_SVG_SERIALIZE_EXCLUDE_ANIMATION = 1 << 1,
  GTK_SVG_SERIALIZE_INCLUDE_STATE     = 1 << 2,
  GTK_SVG_SERIALIZE_INCLUDE_GPA_ATTRS = 1 << 3,
} GtkSvgSerializeFlags;

GBytes *       gtk_svg_serialize_full  (GtkSvg                *self,
                                        const GdkRGBA         *colors,
                                        size_t                 n_colors,
                                        GtkSvgSerializeFlags   flags);

GtkSvg * gtk_svg_copy (GtkSvg *orig);

gboolean gtk_svg_equal (GtkSvg *svg1,
                        GtkSvg *svg2);


G_END_DECLS
