/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later */

/** \file
 * \ingroup DNA
 */

#pragma once

#include "BLI_utildefines.h"

#include "DNA_ID.h"
#include "DNA_action_types.h"
#include "DNA_curve_types.h"
#include "DNA_listBase.h"

#ifdef __cplusplus
#  include "BLI_span.hh"

#  include <type_traits>
#endif

/* ************************************************ */
/* F-Curve DataTypes */

/* Modifiers -------------------------------------- */

/**
 * F-Curve Modifiers (fcm)
 *
 * These alter the way F-Curves behave, by altering the value that is returned
 * when evaluating the curve's data at some time (t).
 */
typedef struct FModifier {
  struct FModifier *next, *prev;

  /** Containing curve, only used for updates to CYCLES. */
  struct FCurve *curve;
  /** Pointer to modifier data. */
  void *data;

  /** User-defined description for the modifier - `MAX_ID_NAME - 2`. */
  char name[64];
  /** Type of f-curve modifier. */
  short type;
  /** Settings for the modifier. */
  short flag;
  /**
   * Expansion state for the modifier panel and its sub-panels, stored as a bit-field
   * in depth-first order. (Maximum of `sizeof(short)` total panels).
   */
  short ui_expand_flag;

  char _pad[6];

  /** The amount that the modifier should influence the value. */
  float influence;

  /** Start frame of restricted frame-range. */
  float sfra;
  /** End frame of restricted frame-range. */
  float efra;
  /** Number of frames from sfra before modifier takes full influence. */
  float blendin;
  /** Number of frames from efra before modifier fades out. */
  float blendout;
} FModifier;

/**
 * Types of F-Curve modifier
 * WARNING: order here is important!
 */
typedef enum eFModifier_Types {
  FMODIFIER_TYPE_NULL = 0,
  FMODIFIER_TYPE_GENERATOR = 1,
  FMODIFIER_TYPE_FN_GENERATOR = 2,
  FMODIFIER_TYPE_ENVELOPE = 3,
  FMODIFIER_TYPE_CYCLES = 4,
  FMODIFIER_TYPE_NOISE = 5,
  /** Unimplemented - for applying: FFT, high/low pass filters, etc. */
  FMODIFIER_TYPE_FILTER = 6,
  FMODIFIER_TYPE_PYTHON = 7,
  FMODIFIER_TYPE_LIMITS = 8,
  FMODIFIER_TYPE_STEPPED = 9,

  /* NOTE: all new modifiers must be added above this line */
  FMODIFIER_NUM_TYPES,
} eFModifier_Types;

/** F-Curve Modifier Settings. */
typedef enum eFModifier_Flags {
  /** Modifier is not able to be evaluated for some reason, and should be skipped (internal). */
  FMODIFIER_FLAG_DISABLED = (1 << 0),
#ifdef DNA_DEPRECATED_ALLOW
  /** Modifier's data is expanded (in UI). Deprecated, use `ui_expand_flag`. */
  FMODIFIER_FLAG_EXPANDED = (1 << 1),
#endif
  /** Modifier is active one (in UI) for editing purposes. */
  FMODIFIER_FLAG_ACTIVE = (1 << 2),
  /** User wants modifier to be skipped. */
  FMODIFIER_FLAG_MUTED = (1 << 3),
  /** Restrict range that F-Modifier can be considered over. */
  FMODIFIER_FLAG_RANGERESTRICT = (1 << 4),
  /** Use influence control. */
  FMODIFIER_FLAG_USEINFLUENCE = (1 << 5),
} eFModifier_Flags;

/* --- */

/* Generator modifier data */
typedef struct FMod_Generator {
  /* general generator information */
  /** Coefficients array. */
  float *coefficients;
  /** Size of the coefficients array. */
  unsigned int arraysize;

  /** Order of polynomial generated (i.e. 1 for linear, 2 for quadratic). */
  int poly_order;
  /** Which 'generator' to use eFMod_Generator_Modes. */
  int mode;

  /** Settings. */
  int flag;
} FMod_Generator;

/* generator modes */
typedef enum eFMod_Generator_Modes {
  FCM_GENERATOR_POLYNOMIAL = 0,
  FCM_GENERATOR_POLYNOMIAL_FACTORISED = 1,
} eFMod_Generator_Modes;

/* generator flags
 * - shared by Generator and Function Generator
 */
typedef enum eFMod_Generator_Flags {
  /* generator works in conjunction with other modifiers (i.e. doesn't replace those before it) */
  FCM_GENERATOR_ADDITIVE = (1 << 0),
} eFMod_Generator_Flags;

/**
 * 'Built-In Function' Generator modifier data
 *
 * This uses the general equation for equations:
 * y = amplitude * fn(phase_multiplier*x + phase_offset) + y_offset
 *
 * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients,
 * x is the evaluation 'time', and 'y' is the resultant value
 */
typedef struct FMod_FunctionGenerator {
  /** Coefficients for general equation (as above). */
  float amplitude;
  float phase_multiplier;
  float phase_offset;
  float value_offset;

  /* flags */
  /** #eFMod_Generator_Functions. */
  int type;
  /** #eFMod_Generator_flags. */
  int flag;
} FMod_FunctionGenerator;

/* 'function' generator types */
typedef enum eFMod_Generator_Functions {
  FCM_GENERATOR_FN_SIN = 0,
  FCM_GENERATOR_FN_COS = 1,
  FCM_GENERATOR_FN_TAN = 2,
  FCM_GENERATOR_FN_SQRT = 3,
  FCM_GENERATOR_FN_LN = 4,
  FCM_GENERATOR_FN_SINC = 5,
} eFMod_Generator_Functions;

/* envelope modifier - envelope data */
typedef struct FCM_EnvelopeData {
  /** Min/max values for envelope at this point (absolute values). */
  float min, max;
  /** Time for that this sample-point occurs. */
  float time;

  /** Settings for 'min' control point. */
  short f1;
  /** Settings for 'max' control point. */
  short f2;
} FCM_EnvelopeData;

/* envelope-like adjustment to values (for fade in/out) */
typedef struct FMod_Envelope {
  /** Data-points defining envelope to apply (array). */
  FCM_EnvelopeData *data;
  /** Number of envelope points. */
  int totvert;

  /** Value that envelope's influence is centered around / based on. */
  float midval;
  /** Distances from 'middle-value' for 1:1 envelope influence. */
  float min, max;
} FMod_Envelope;

/* cycling/repetition modifier data */
/* TODO: we can only do complete cycles. */
typedef struct FMod_Cycles {
  /** Extrapolation mode to use before first keyframe. */
  short before_mode;
  /** Extrapolation mode to use after last keyframe. */
  short after_mode;
  /** Number of 'cycles' before first keyframe to do. */
  short before_cycles;
  /** Number of 'cycles' after last keyframe to do. */
  short after_cycles;
} FMod_Cycles;

/* cycling modes */
typedef enum eFMod_Cycling_Modes {
  /** don't do anything */
  FCM_EXTRAPOLATE_NONE = 0,
  /** repeat keyframe range as-is */
  FCM_EXTRAPOLATE_CYCLIC,
  /** repeat keyframe range, but with offset based on gradient between values */
  FCM_EXTRAPOLATE_CYCLIC_OFFSET,
  /** alternate between forward and reverse playback of keyframe range */
  FCM_EXTRAPOLATE_MIRROR,
} eFMod_Cycling_Modes;

/* Python-script modifier data */
typedef struct FMod_Python {
  /** Text buffer containing script to execute. */
  struct Text *script;
  /** ID-properties to provide 'custom' settings. */
  IDProperty *prop;
} FMod_Python;

/* limits modifier data */
typedef struct FMod_Limits {
  /** Rect defining the min/max values. */
  rctf rect;
  /** Settings for limiting. */
  int flag;
  char _pad[4];
} FMod_Limits;

/* limiting flags */
typedef enum eFMod_Limit_Flags {
  FCM_LIMIT_XMIN = (1 << 0),
  FCM_LIMIT_XMAX = (1 << 1),
  FCM_LIMIT_YMIN = (1 << 2),
  FCM_LIMIT_YMAX = (1 << 3),
} eFMod_Limit_Flags;

/* noise modifier data */
typedef struct FMod_Noise {
  float size;
  float strength;
  float phase;
  float offset;

  short depth;
  short modification;
} FMod_Noise;

/* modification modes */
typedef enum eFMod_Noise_Modifications {
  /** Modify existing curve, matching its shape. */
  FCM_NOISE_MODIF_REPLACE = 0,
  /** Add noise to the curve. */
  FCM_NOISE_MODIF_ADD,
  /** Subtract noise from the curve. */
  FCM_NOISE_MODIF_SUBTRACT,
  /** Multiply the curve by noise. */
  FCM_NOISE_MODIF_MULTIPLY,
} eFMod_Noise_Modifications;

/* stepped modifier data */
typedef struct FMod_Stepped {
  /** Number of frames each interpolated value should be held. */
  float step_size;
  /** Reference frame number that stepping starts from. */
  float offset;

  /** Start frame of the frame range that modifier works in. */
  float start_frame;
  /** End frame of the frame range that modifier works in. */
  float end_frame;

  /** Various settings. */
  int flag;
} FMod_Stepped;

/* stepped modifier range flags */
typedef enum eFMod_Stepped_Flags {
  /** Don't affect frames before the start frame. */
  FCM_STEPPED_NO_BEFORE = (1 << 0),
  /** Don't affect frames after the end frame. */
  FCM_STEPPED_NO_AFTER = (1 << 1),
} eFMod_Stepped_Flags;

/* Drivers -------------------------------------- */

/* Driver Target (dtar)
 *
 * Defines how to access a dependency needed for a driver variable.
 */
typedef struct DriverTarget {
  /** ID-block which owns the target, no user count. */
  ID *id;

  /** RNA path defining the setting to use (for DVAR_TYPE_SINGLE_PROP). */
  char *rna_path;

  /**
   * Name of the pose-bone to use
   * (for vars where DTAR_FLAG_STRUCT_REF is used) - `MAX_ID_NAME - 2`.
   */
  char pchan_name[64];
  /** Transform channel index (for #DVAR_TYPE_TRANSFORM_CHAN). */
  short transChan;

  /** Rotation channel calculation type. */
  char rotation_mode;
  char _pad[5];

  /**
   * Flags for the validity of the target
   * (NOTE: these get reset every time the types change).
   */
  short flag;
  /** Single-bit user-visible toggles (not reset on type change) from eDriverTarget_Options. */
  short options;
  /** Type of ID-block that this target can use. */
  int idtype;

  /* Context-dependent property of a "Context Property" type target.
   * The `rna_path` of this property is used as a target.
   * This is a value of enumerator #eDriverTarget_ContextProperty. */
  int context_property;

  /* Fallback value to use with DTAR_OPTION_USE_FALLBACK. */
  float fallback_value;
} DriverTarget;

/** Driver Target options. */
typedef enum eDriverTarget_Options {
  /** Use the fallback value when the target is invalid (rna_path cannot be resolved). */
  DTAR_OPTION_USE_FALLBACK = (1 << 0),
} eDriverTarget_Options;

/** Driver Target flags. */
typedef enum eDriverTarget_Flag {
  /** used for targets that use the pchan_name instead of RNA path
   * (i.e. rotation difference) */
  DTAR_FLAG_STRUCT_REF = (1 << 0),
  /** idtype can only be 'Object' */
  DTAR_FLAG_ID_OB_ONLY = (1 << 1),

  /* "local-space" flags. */
  /** base flag - basically "pre parent+constraints" */
  DTAR_FLAG_LOCALSPACE = (1 << 2),
  /** include constraints transformed to space including parents */
  DTAR_FLAG_LOCAL_CONSTS = (1 << 3),

  /** error flags */
  DTAR_FLAG_INVALID = (1 << 4),

  /** the fallback value was actually used */
  DTAR_FLAG_FALLBACK_USED = (1 << 5),
} eDriverTarget_Flag;

/* Transform Channels for Driver Targets */
typedef enum eDriverTarget_TransformChannels {
  DTAR_TRANSCHAN_LOCX = 0,
  DTAR_TRANSCHAN_LOCY,
  DTAR_TRANSCHAN_LOCZ,
  DTAR_TRANSCHAN_ROTX,
  DTAR_TRANSCHAN_ROTY,
  DTAR_TRANSCHAN_ROTZ,
  DTAR_TRANSCHAN_SCALEX,
  DTAR_TRANSCHAN_SCALEY,
  DTAR_TRANSCHAN_SCALEZ,
  DTAR_TRANSCHAN_SCALE_AVG,
  DTAR_TRANSCHAN_ROTW,

  MAX_DTAR_TRANSCHAN_TYPES,
} eDriverTarget_TransformChannels;

/* Rotation channel mode for Driver Targets */
typedef enum eDriverTarget_RotationMode {
  /** Automatic euler mode. */
  DTAR_ROTMODE_AUTO = 0,

  /** Explicit euler rotation modes - must sync with BLI_math_rotation.h defines. */
  DTAR_ROTMODE_EULER_XYZ = 1,
  DTAR_ROTMODE_EULER_XZY,
  DTAR_ROTMODE_EULER_YXZ,
  DTAR_ROTMODE_EULER_YZX,
  DTAR_ROTMODE_EULER_ZXY,
  DTAR_ROTMODE_EULER_ZYX,

  DTAR_ROTMODE_QUATERNION,

  /**
   * Implements the very common Damped Track + child trick to decompose
   * rotation into bending followed by twist around the remaining axis.
   */
  DTAR_ROTMODE_SWING_TWIST_X,
  DTAR_ROTMODE_SWING_TWIST_Y,
  DTAR_ROTMODE_SWING_TWIST_Z,

  DTAR_ROTMODE_EULER_MIN = DTAR_ROTMODE_EULER_XYZ,
  DTAR_ROTMODE_EULER_MAX = DTAR_ROTMODE_EULER_ZYX,
} eDriverTarget_RotationMode;

typedef enum eDriverTarget_ContextProperty {
  DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE = 0,
  DTAR_CONTEXT_PROPERTY_ACTIVE_VIEW_LAYER = 1,
} eDriverTarget_ContextProperty;

/* --- */

/* maximum number of driver targets per variable */
#define MAX_DRIVER_TARGETS 8

/**
 * Driver Variable (dvar)
 *
 * A 'variable' for use as an input for the driver evaluation.
 * Defines a way of accessing some channel to use, that can be
 * referred to in the expression as a variable, thus simplifying
 * expressions and also Depsgraph building.
 */
typedef struct DriverVar {
  struct DriverVar *next, *prev;

  /**
   * Name of the variable to use in py-expression
   * (must be valid python identifier) - `MAX_ID_NAME - 2`.
   */
  char name[64];

  /** MAX_DRIVER_TARGETS, target slots. */
  DriverTarget targets[8];

  /** Number of targets actually used by this variable. */
  char num_targets;
  /** Type of driver variable (eDriverVar_Types). */
  char type;

  /** Validation tags, etc. (eDriverVar_Flags). */
  short flag;
  /** Result of previous evaluation. */
  float curval;
} DriverVar;

/** Driver Variable Types.* */
typedef enum eDriverVar_Types {
  /** single RNA property */
  DVAR_TYPE_SINGLE_PROP = 0,
  /** rotation difference (between 2 bones) */
  DVAR_TYPE_ROT_DIFF,
  /** distance between objects/bones */
  DVAR_TYPE_LOC_DIFF,
  /** 'final' transform for object/bones */
  DVAR_TYPE_TRANSFORM_CHAN,
  /** Property within a current evaluation context */
  DVAR_TYPE_CONTEXT_PROP,

  /**
   * Maximum number of variable types.
   *
   * \note This must always be the last item in this list,
   * so add new types above this line.
   */
  MAX_DVAR_TYPES,
} eDriverVar_Types;

/* Driver Variable Flags */
typedef enum eDriverVar_Flags {
  /* variable is not set up correctly */
  DVAR_FLAG_ERROR = (1 << 0),

  /* variable name doesn't pass the validation tests */
  DVAR_FLAG_INVALID_NAME = (1 << 1),
  /* name starts with a number */
  DVAR_FLAG_INVALID_START_NUM = (1 << 2),
  /* name starts with a special character (!, $, @, #, _, etc.) */
  DVAR_FLAG_INVALID_START_CHAR = (1 << 3),
  /* name contains a space */
  DVAR_FLAG_INVALID_HAS_SPACE = (1 << 4),
  /* name contains a dot */
  DVAR_FLAG_INVALID_HAS_DOT = (1 << 5),
  /* name contains invalid chars */
  DVAR_FLAG_INVALID_HAS_SPECIAL = (1 << 6),
  /* name is a reserved keyword */
  DVAR_FLAG_INVALID_PY_KEYWORD = (1 << 7),
  /* name is zero-length */
  DVAR_FLAG_INVALID_EMPTY = (1 << 8),
} eDriverVar_Flags;

/* All invalid dvar name flags */
#define DVAR_ALL_INVALID_FLAGS \
  (DVAR_FLAG_INVALID_NAME | DVAR_FLAG_INVALID_START_NUM | DVAR_FLAG_INVALID_START_CHAR | \
   DVAR_FLAG_INVALID_HAS_SPACE | DVAR_FLAG_INVALID_HAS_DOT | DVAR_FLAG_INVALID_HAS_SPECIAL | \
   DVAR_FLAG_INVALID_PY_KEYWORD | DVAR_FLAG_INVALID_EMPTY)

/* --- */

/**
 * Channel Driver (i.e. Drivers / Expressions) (driver)
 *
 * Channel Drivers are part of the dependency system, and are executed in addition to
 * normal user-defined animation. They take the animation result of some channel(s), and
 * use that (optionally combined with its own F-Curve for modification of results) to define
 * the value of some setting semi-procedurally.
 *
 * Drivers are stored as part of F-Curve data, so that the F-Curve's RNA-path settings (for storing
 * what setting the driver will affect). The order in which they are stored defines the order that
 * they're evaluated in. This order is set by the Depsgraph's sorting stuff.
 */
typedef struct ChannelDriver {
  /** Targets for this driver (i.e. list of DriverVar). */
  ListBase variables;

  /* python expression to execute (may call functions defined in an accessory file)
   * which relates the target 'variables' in some way to yield a single usable value
   */
  /** Expression to compile for evaluation. */
  char expression[256];
  /** PyObject - compiled expression, don't save this. */
  void *expr_comp;

  /** Compiled simple arithmetic expression. */
  struct ExprPyLike_Parsed *expr_simple;

  /** Result of previous evaluation. */
  float curval;
  /* XXX to be implemented... this is like the constraint influence setting. */
  /** Influence of driver on result. */
  float influence;

  /* general settings */
  /** Type of driver. */
  int type;
  /** Settings of driver. */
  int flag;
} ChannelDriver;

/** Driver type. */
typedef enum eDriver_Types {
  /** target values are averaged together. */
  DRIVER_TYPE_AVERAGE = 0,
  /** python expression/function relates targets. */
  DRIVER_TYPE_PYTHON,
  /** sum of all values. */
  DRIVER_TYPE_SUM,
  /** smallest value. */
  DRIVER_TYPE_MIN,
  /** largest value. */
  DRIVER_TYPE_MAX,
} eDriver_Types;

/** Driver flags. */
typedef enum eDriver_Flags {
  /** Driver has invalid settings (internal flag). */
  DRIVER_FLAG_INVALID = (1 << 0),
  DRIVER_FLAG_DEPRECATED = (1 << 1),
  /** Driver does replace value, but overrides (for layering of animation over driver) */
  /* TODO: this needs to be implemented at some stage or left out... */
  // DRIVER_FLAG_LAYERING  = (1 << 2),
  /** Use when the expression needs to be recompiled. */
  DRIVER_FLAG_RECOMPILE = (1 << 3),
  /** The names are cached so they don't need have python unicode versions created each time */
  DRIVER_FLAG_RENAMEVAR = (1 << 4),
  /* Set if the driver cannot run because it uses Python which isn't allowed to execute. */
  DRIVER_FLAG_PYTHON_BLOCKED = (1 << 5),
  /** Include 'self' in the drivers namespace. */
  DRIVER_FLAG_USE_SELF = (1 << 6),
} eDriver_Flags;

/* F-Curves -------------------------------------- */

/** When #active_keyframe_index is set to this, the FCurve does not have an active keyframe. */
#define FCURVE_ACTIVE_KEYFRAME_NONE -1

/**
 * FPoint (fpt)
 *
 * This is the bare-minimum data required storing motion samples. Should be more efficient
 * than using BPoints, which contain a lot of other unnecessary data...
 */
typedef struct FPoint {
  /** Time + value. */
  float vec[2];
  /** Selection info. */
  int flag;
  char _pad[4];
} FPoint;

/** 'Function-Curve' - defines values over time for a given setting (fcu). */
typedef struct FCurve {
  struct FCurve *next, *prev;

  /* group */
  /** Group that F-Curve belongs to. */
  bActionGroup *grp;

  /* driver settings */
  /** Only valid for drivers (i.e. stored in AnimData not Actions). */
  ChannelDriver *driver;
  /* evaluation settings */
  /** FCurve Modifiers. */
  ListBase modifiers;

  /* motion data */
  /** User-editable keyframes (array). */
  BezTriple *bezt;
  /** 'baked/imported' motion samples (array). */
  FPoint *fpt;
  /** Total number of points which define the curve (i.e. size of arrays in FPoints). */
  unsigned int totvert;

  /**
   * Index of active keyframe in #bezt for numerical editing in the interface. A value of
   * #FCURVE_ACTIVE_KEYFRAME_NONE indicates that the FCurve has no active keyframe.
   *
   * Do not access directly, use #BKE_fcurve_active_keyframe_index() and
   * #BKE_fcurve_active_keyframe_set() instead.
   */
  int active_keyframe_index;

  /* value cache + settings */
  /** Value stored from last time curve was evaluated (not threadsafe, debug display only!). */
  float curval;
  /** User-editable settings for this curve. */
  short flag;
  /** Value-extending mode for this curve (does not cover). */
  short extend;
  /** Auto-handle smoothing mode. */
  char auto_smoothing;

  char _pad[3];

  /* RNA - data link */
  /**
   * When the RNA property from `rna_path` is an array, use this to access the array index.
   *
   * \note This may be negative (as it wasn't prevented in 2.91 and older).
   * Currently it silently fails to resolve the data-path in this case.
   */
  int array_index;
  /**
   * RNA-path to resolve data-access, see: #RNA_path_resolve_property.
   *
   * \note String look-ups for collection and custom-properties are escaped using #BLI_str_escape.
   */
  char *rna_path;

  /* curve coloring (for editor) */
  /** Coloring method to use (eFCurve_Coloring). */
  int color_mode;
  /** The last-color this curve took. */
  float color[3];

  float prev_norm_factor, prev_offset;
} FCurve;

/* user-editable flags/settings */
typedef enum eFCurve_Flags {
  /** Curve/keyframes are visible in editor */
  FCURVE_VISIBLE = (1 << 0),
  /** Curve is selected for editing. */
  FCURVE_SELECTED = (1 << 1),
  /** Curve is active one. */
  FCURVE_ACTIVE = (1 << 2),
  /** Keyframes (beztriples) cannot be edited. */
  FCURVE_PROTECTED = (1 << 3),
  /** FCurve will not be evaluated for the next round. */
  FCURVE_MUTED = (1 << 4),

#ifdef DNA_DEPRECATED_ALLOW
  /** fcurve uses 'auto-handles', which stay horizontal... */
  FCURVE_AUTO_HANDLES = (1 << 5), /* Dirty. */
#endif
  FCURVE_MOD_OFF = (1 << 6),
  /** skip evaluation, as RNA-path cannot be resolved
   * (similar to muting, but cannot be set by user) */
  FCURVE_DISABLED = (1 << 10),
  /** curve can only have whole-number values (integer types) */
  FCURVE_INT_VALUES = (1 << 11),
  /** curve can only have certain discrete-number values
   * (no interpolation at all, for enums/booleans) */
  FCURVE_DISCRETE_VALUES = (1 << 12),

  /** temporary tag for editing */
  FCURVE_TAGGED = (1 << 15),
} eFCurve_Flags;

/* extrapolation modes (only simple value 'extending') */
typedef enum eFCurve_Extend {
  /** Just extend min/max keyframe value. */
  FCURVE_EXTRAPOLATE_CONSTANT = 0,
  /** Just extend gradient of segment between first segment keyframes. */
  FCURVE_EXTRAPOLATE_LINEAR,
} eFCurve_Extend;

/* curve coloring modes */
typedef enum eFCurve_Coloring {
  /** automatically determine color using rainbow (calculated at drawtime) */
  FCURVE_COLOR_AUTO_RAINBOW = 0,
  /** automatically determine color using XYZ (array index) <-> RGB */
  FCURVE_COLOR_AUTO_RGB = 1,
  /** automatically determine color where XYZ <-> RGB, but index(X) != 0 */
  FCURVE_COLOR_AUTO_YRGB = 3,
  /** custom color */
  FCURVE_COLOR_CUSTOM = 2,
} eFCurve_Coloring;

/* curve smoothing modes */
typedef enum eFCurve_Smoothing {
  /** legacy mode: auto handles only consider adjacent points */
  FCURVE_SMOOTH_NONE = 0,
  /** maintain continuity of the acceleration */
  FCURVE_SMOOTH_CONT_ACCEL = 1,
} eFCurve_Smoothing;

/* ************************************************ */
/* 'Action' Data-types */

/* NOTE: Although these are part of the Animation System,
 * they are not stored here, see `DNA_action_types.h` instead. */

/* ************************************************ */
/* NLA - Non-Linear Animation */

/* NLA Strips ------------------------------------- */

/**
 * NLA Strip (strip)
 *
 * A NLA Strip is a container for the reuse of Action data, defining parameters
 * to control the remapping of the Action data to some destination.
 */
typedef struct NlaStrip {
  struct NlaStrip *next, *prev;

  /** 'Child' strips (used for 'meta' strips). */
  ListBase strips;
  /** Action that is referenced by this strip (strip is 'user' of the action). */
  bAction *act;

  /** F-Curves for controlling this strip's influence and timing */ /* TODO: move out? */
  ListBase fcurves;
  /** F-Curve modifiers to be applied to the entire strip's referenced F-Curves. */
  ListBase modifiers;

  /** User-Visible Identifier for Strip - `MAX_ID_NAME - 2`. */
  char name[64];

  /** Influence of strip. */
  float influence;
  /** Current 'time' within action being used (automatically evaluated, but can be overridden). */
  float strip_time;

  /** Extents of the strip. */
  float start, end;
  /** Range of the action to use. */
  float actstart, actend;

  /** The number of times to repeat the action range (only when no F-Curves). */
  float repeat;
  /** The amount the action range is scaled by (only when no F-Curves). */
  float scale;

  /** Strip blending length (only used when there are no F-Curves). */
  float blendin, blendout;
  /** Strip blending mode (layer-based mixing). */
  short blendmode;

  /** Strip extrapolation mode (time-based mixing). */
  short extendmode;
  char _pad1[2];

  /** Type of NLA strip. */
  short type;

  /** Handle for speaker objects. */
  void *speaker_handle;

  /** Settings. */
  int flag;
  char _pad2[4];

  /* Pointer to an original NLA strip. */
  struct NlaStrip *orig_strip;

  void *_pad3;
} NlaStrip;

/* NLA Strip Blending Mode */
typedef enum eNlaStrip_Blend_Mode {
  NLASTRIP_MODE_REPLACE = 0,
  NLASTRIP_MODE_ADD,
  NLASTRIP_MODE_SUBTRACT,
  NLASTRIP_MODE_MULTIPLY,
  NLASTRIP_MODE_COMBINE,
} eNlaStrip_Blend_Mode;

/** NLA Strip Extrapolation Mode. */
typedef enum eNlaStrip_Extrapolate_Mode {
  /* extend before first frame if no previous strips in track,
   * and always hold+extend last frame */
  NLASTRIP_EXTEND_HOLD = 0,
  /* only hold+extend last frame */
  NLASTRIP_EXTEND_HOLD_FORWARD = 1,
  /* don't contribute at all */
  NLASTRIP_EXTEND_NOTHING = 2,
} eNlaStrip_Extrapolate_Mode;

/** NLA Strip Settings. */
typedef enum eNlaStrip_Flag {
  /* UI selection flags */
  /** NLA strip is the active one in the track (also indicates if strip is being tweaked) */
  NLASTRIP_FLAG_ACTIVE = (1 << 0),
  /* NLA strip is selected for editing */
  NLASTRIP_FLAG_SELECT = (1 << 1),
  // NLASTRIP_FLAG_SELECT_L      = (1 << 2),   /* left handle selected. */
  // NLASTRIP_FLAG_SELECT_R      = (1 << 3),   /* right handle selected. */

  /**
   * NLA strip uses the same action that the action being tweaked uses
   * (not set for the tweaking one though).
   */
  NLASTRIP_FLAG_TWEAKUSER = (1 << 4),

  /* controls driven by local F-Curves */
  /** strip influence is controlled by local F-Curve */
  NLASTRIP_FLAG_USR_INFLUENCE = (1 << 5),
  NLASTRIP_FLAG_USR_TIME = (1 << 6),
  NLASTRIP_FLAG_USR_TIME_CYCLIC = (1 << 7),

  /** NLA strip length is synced to the length of the referenced action */
  NLASTRIP_FLAG_SYNC_LENGTH = (1 << 9),

  /* playback flags (may be overridden by F-Curves) */
  /** NLA strip blendin/out values are set automatically based on overlaps */
  NLASTRIP_FLAG_AUTO_BLENDS = (1 << 10),
  /** NLA strip is played back in reverse order */
  NLASTRIP_FLAG_REVERSE = (1 << 11),
  /** NLA strip is muted (i.e. doesn't contribute in any way) */
  NLASTRIP_FLAG_MUTED = (1 << 12),
  /** NLA Strip is played back in 'ping-pong' style */
  /* NLASTRIP_FLAG_MIRROR = (1 << 13), */ /* UNUSED */

  /* temporary editing flags */

  /**
   * When transforming strips, this flag is set when the strip is placed in an invalid location
   * such as overlapping another strip or moved to a locked track. In such cases, the strip's
   * location must be corrected after the transform operator is done.
   */
  NLASTRIP_FLAG_INVALID_LOCATION = (1 << 28),
  /** NLA strip should ignore frame range and hold settings, and evaluate at global time. */
  NLASTRIP_FLAG_NO_TIME_MAP = (1 << 29),
  /** NLA-Strip is really just a temporary meta used to facilitate easier transform code */
  NLASTRIP_FLAG_TEMP_META = (1 << 30),
  NLASTRIP_FLAG_EDIT_TOUCHED = (1u << 31),
} eNlaStrip_Flag;

/* NLA Strip Type */
typedef enum eNlaStrip_Type {
  /* 'clip' - references an Action */
  NLASTRIP_TYPE_CLIP = 0,
  /* 'transition' - blends between the adjacent strips */
  NLASTRIP_TYPE_TRANSITION,
  /* 'meta' - a strip which acts as a container for a few others */
  NLASTRIP_TYPE_META,

  /* 'emit sound' - a strip which is used for timing when speaker emits sounds */
  NLASTRIP_TYPE_SOUND,
} eNlaStrip_Type;

/* NLA Tracks ------------------------------------- */

/**
 * NLA Track (nlt)
 *
 * A track groups a bunch of 'strips', which should form a continuous set of
 * motion, on top of which other such groups can be layered. This should allow
 * for animators to work in a non-destructive manner, layering tweaks, etc. over
 * 'rough' blocks of their work.
 */
typedef struct NlaTrack {
  struct NlaTrack *next, *prev;

  /** BActionStrips in this track. */
  ListBase strips;

  /** Settings for this track. */
  int flag;
  /** Index of the track in the stack
   * \note not really useful, but we need a '_pad' var anyways! */
  int index;

  /** Short user-description of this track - `MAX_ID_NAME - 2`. */
  char name[64];
} NlaTrack;

/* settings for track */
typedef enum eNlaTrack_Flag {
  /** track is the one that settings can be modified on,
   * also indicates if track is being 'tweaked' */
  NLATRACK_ACTIVE = (1 << 0),
  /** track is selected in UI for relevant editing operations */
  NLATRACK_SELECTED = (1 << 1),
  /** track is not evaluated */
  NLATRACK_MUTED = (1 << 2),
  /** track is the only one evaluated (must be used in conjunction with adt->flag) */
  NLATRACK_SOLO = (1 << 3),
  /** track's settings (and strips) cannot be edited (to guard against unwanted changes) */
  NLATRACK_PROTECTED = (1 << 4),

  /** track is not allowed to execute,
   * usually as result of tweaking being enabled (internal flag) */
  NLATRACK_DISABLED = (1 << 10),

  /** Marks tracks automatically added for space while dragging strips vertically.
   * Internal flag that's only set during transform operator. */
  NLATRACK_TEMPORARILY_ADDED = (1 << 11),

  /** This NLA track is added to an override ID, which means it is fully editable.
   * Irrelevant in case the owner ID is not an override. */
  NLATRACK_OVERRIDELIBRARY_LOCAL = 1 << 16,
} eNlaTrack_Flag;

/* ************************************ */
/* KeyingSet Data-types */

/**
 * Path for use in KeyingSet definitions (ksp)
 *
 * Paths may be either specific (specifying the exact sub-ID
 * dynamic data-block - such as PoseChannels - to act upon, ala
 * Maya's 'Character Sets' and XSI's 'Marking Sets'), or they may
 * be generic (using various placeholder template tags that will be
 * replaced with appropriate information from the context).
 */
typedef struct KS_Path {
  struct KS_Path *next, *prev;

  /** ID block that keyframes are for. */
  ID *id;
  /** Name of the group to add to - `MAX_ID_NAME - 2`. */
  char group[64];

  /** ID-type that path can be used on. */
  int idtype;

  /** Group naming (eKSP_Grouping). */
  short groupmode;
  /** Various settings, etc. */
  short flag;

  /** Dynamically (or statically in the case of predefined sets) path. */
  char *rna_path;
  /** Index that path affects. */
  int array_index;

  /** (#eInsertKeyFlags) settings to supply insert-key() with. */
  short keyingflag;
  /** (#eInsertKeyFlags) for each flag set, the relevant keying-flag bit overrides the default. */
  short keyingoverride;
} KS_Path;

/* KS_Path->flag */
typedef enum eKSP_Settings {
  /* entire array (not just the specified index) gets keyframed */
  KSP_FLAG_WHOLE_ARRAY = (1 << 0),
} eKSP_Settings;

/* KS_Path->groupmode */
typedef enum eKSP_Grouping {
  /** Path should be grouped using group name stored in path. */
  KSP_GROUP_NAMED = 0,
  /** Path should not be grouped at all. */
  KSP_GROUP_NONE,
  /** Path should be grouped using KeyingSet's name. */
  KSP_GROUP_KSNAME,
  /** Path should be grouped using name of inner-most context item from templates
   * - this is most useful for relative KeyingSets only. */
  /* KSP_GROUP_TEMPLATE_ITEM, */ /* UNUSED */
} eKSP_Grouping;

/* ---------------- */

/**
 * KeyingSet definition (ks)
 *
 * A KeyingSet defines a group of properties that should
 * be keyframed together, providing a convenient way for animators
 * to insert keyframes without resorting to Auto-Keyframing.
 *
 * A few 'generic' (non-absolute and dependent on templates) KeyingSets
 * are defined 'built-in' to facilitate easy animating for the casual
 * animator without the need to add extra steps to the rigging process.
 */
typedef struct KeyingSet {
  struct KeyingSet *next, *prev;

  /** (KS_Path) paths to keyframe to. */
  ListBase paths;

  /** Unique name (for search, etc.) - `MAX_ID_NAME - 2`. */
  char idname[64];
  /** User-viewable name for KeyingSet (for menus, etc.) - `MAX_ID_NAME - 2`. */
  char name[64];
  /** (#RNA_DYN_DESCR_MAX) help text. */
  char description[1024];
  /** Name of the typeinfo data used for the relative paths - `MAX_ID_NAME - 2`. */
  char typeinfo[64];

  /** Index of the active path. */
  int active_path;

  /** Settings for KeyingSet. */
  short flag;

  /** (eInsertKeyFlags) settings to supply insertkey() with. */
  short keyingflag;
  /** (eInsertKeyFlags) for each flag set, the relevant keyingflag bit overrides the default. */
  short keyingoverride;

  char _pad[6];
} KeyingSet;

/* KeyingSet settings */
typedef enum eKS_Settings {
  /** Keyingset cannot be removed (and doesn't need to be freed). */
  /* KEYINGSET_BUILTIN = (1 << 0), */ /* UNUSED */
  /** Keyingset does not depend on context info (i.e. paths are absolute). */
  KEYINGSET_ABSOLUTE = (1 << 1),
} eKS_Settings;
ENUM_OPERATORS(eKS_Settings, KEYINGSET_ABSOLUTE)

/* Flags for use by keyframe creation/deletion calls */
typedef enum eInsertKeyFlags {
  INSERTKEY_NOFLAGS = 0,
  /** only insert keyframes where they're needed */
  INSERTKEY_NEEDED = (1 << 0),
  /** insert 'visual' keyframes where possible/needed */
  INSERTKEY_MATRIX = (1 << 1),
  /** don't recalculate handles,etc. after adding key */
  INSERTKEY_FAST = (1 << 2),
  /** don't realloc mem (or increase count, as array has already been set out) */
  /* INSERTKEY_FASTR = (1 << 3), */ /* UNUSED */
  /** only replace an existing keyframe (this overrides INSERTKEY_NEEDED) */
  INSERTKEY_REPLACE = (1 << 4),
  /** ignore user-prefs (needed for predictable API use) */
  INSERTKEY_NO_USERPREF = (1 << 6),
  /**
   * Allow to make a full copy of new key into existing one, if any,
   * instead of 'reusing' existing handles.
   * Used by copy/paste code.
   */
  INSERTKEY_OVERWRITE_FULL = (1 << 7),
  /** for cyclic FCurves, adjust key timing to preserve the cycle period and flow */
  INSERTKEY_CYCLE_AWARE = (1 << 9),
  /** don't create new F-Curves (implied by INSERTKEY_REPLACE) */
  INSERTKEY_AVAILABLE = (1 << 10),
} eInsertKeyFlags;
ENUM_OPERATORS(eInsertKeyFlags, INSERTKEY_AVAILABLE);

/* ************************************************ */
/* Animation Data */

/* AnimOverride ------------------------------------- */

/**
 * Animation Override (aor)
 *
 * This is used to as temporary storage of values which have been changed by the user, but not
 * yet keyframed (thus, would get overwritten by the animation system before the user had a chance
 * to see the changes that were made).
 *
 * It is probably not needed for overriding keyframed values in most cases, as those will only get
 * evaluated on frame-change now. That situation may change in future.
 */
typedef struct AnimOverride {
  struct AnimOverride *next, *prev;

  /** RNA-path to use to resolve data-access. */
  char *rna_path;
  /** If applicable, the index of the RNA-array item to get. */
  int array_index;

  /** Value to override setting with. */
  float value;
} AnimOverride;

/* AnimData ------------------------------------- */

/**
 * Animation data for some ID block (adt)
 *
 * This block of data is used to provide all of the necessary animation data for a data-block.
 * Currently, this data will not be reusable, as there shouldn't be any need to do so.
 *
 * This information should be made available for most if not all ID-blocks, which should
 * enable all of its settings to be animatable locally. Animation from 'higher-up' ID-AnimData
 * blocks may override local settings.
 *
 * This data-block should be placed immediately after the ID block where it is used, so that
 * the code which retrieves this data can do so in an easier manner.
 * See `blenkernel/intern/anim_sys.cc` for details.
 */
typedef struct AnimData {
  /**
   * Active action - acts as the 'tweaking track' for the NLA.
   * Either use BKE_animdata_set_action() to set this, or call
   * #BKE_animdata_action_ensure_idroot() after setting.
   */
  bAction *action;

  /**
   * Identifier for which ActionBinding of the above Animation is actually animating this
   * data-block.
   *
   * Do not set this directly, use one of the assignment functions in ANIM_action.hh instead.
   */
  int32_t binding_handle;
  /**
   * Binding name, primarily used for mapping to the right binding when assigning
   * another Animation data-block. Should be the same type as #ActionBinding::name.
   *
   * \see #ActionBinding::name
   */
  char binding_name[66]; /* MAX_ID_NAME */
  uint8_t _pad0[2];

  /**
   * Temp-storage for the 'real' active action (i.e. the one used before the tweaking-action
   * took over to be edited in the Animation Editors)
   */
  bAction *tmpact;

  /* nla-tracks */
  ListBase nla_tracks;
  /**
   * Active NLA-track
   * (only set/used during tweaking, so no need to worry about dangling pointers).
   */
  NlaTrack *act_track;
  /**
   * Active NLA-strip
   * (only set/used during tweaking, so no need to worry about dangling pointers).
   */
  NlaStrip *actstrip;

  /* 'drivers' for this ID-block's settings - FCurves, but are completely
   * separate from those for animation data
   */
  /** Standard user-created Drivers/Expressions (used as part of a rig). */
  ListBase drivers;
  /** Temp storage (AnimOverride) of values for settings that are animated
   * (but the value hasn't been keyframed). */
  ListBase overrides;

  /** Runtime data, for depsgraph evaluation. */
  FCurve **driver_array;

  /* settings for animation evaluation */
  /** User-defined settings. */
  int flag;

  /* settings for active action evaluation (based on NLA strip settings) */
  /** Accumulation mode for active action. */
  short act_blendmode;
  /** Extrapolation mode for active action. */
  short act_extendmode;
  /** Influence for active action. */
  float act_influence;

  uint8_t _pad1[4];
} AnimData;

#ifdef __cplusplus
/* Some static assertions that things that should have the same type actually do. */
static_assert(std::is_same_v<decltype(ActionBinding::handle), decltype(AnimData::binding_handle)>);
static_assert(std::is_same_v<decltype(ActionBinding::name), decltype(AnimData::binding_name)>);
#endif

/* Animation Data settings (mostly for NLA) */
typedef enum eAnimData_Flag {
  /** Only evaluate a single track in the NLA. */
  ADT_NLA_SOLO_TRACK = (1 << 0),
  /** Don't use NLA */
  ADT_NLA_EVAL_OFF = (1 << 1),
  /** NLA is being 'tweaked' (i.e. in EditMode). */
  ADT_NLA_EDIT_ON = (1 << 2),
  /** Active Action for 'tweaking' does not have mapping applied for editing. */
  ADT_NLA_EDIT_NOMAP = (1 << 3),
  /** NLA-Strip F-Curves are expanded in UI. */
  ADT_NLA_SKEYS_COLLAPSED = (1 << 4),
  /* Evaluate tracks above tweaked strip. Only relevant in tweak mode. */
  ADT_NLA_EVAL_UPPER_TRACKS = (1 << 5),

  /** Drivers expanded in UI. */
  ADT_DRIVERS_COLLAPSED = (1 << 10),
  /** Don't execute drivers. */
  /* ADT_DRIVERS_DISABLED = (1 << 11), */ /* UNUSED */

  /** AnimData block is selected in UI. */
  ADT_UI_SELECTED = (1 << 14),
  /** AnimData block is active in UI. */
  ADT_UI_ACTIVE = (1 << 15),

  /** F-Curves from this AnimData block are not visible in the Graph Editor. */
  ADT_CURVES_NOT_VISIBLE = (1 << 16),

  /** F-Curves from this AnimData block are always visible. */
  ADT_CURVES_ALWAYS_VISIBLE = (1 << 17),

  /** Animation pointer to by this AnimData block is expanded in UI. This is stored on the AnimData
   * so that each user of the Animation can have its own expansion/contraction state. */
  ADT_UI_EXPANDED = (1 << 18),
} eAnimData_Flag;

/* Base Struct for Anim ------------------------------------- */

/**
 * Used for #BKE_animdata_from_id()
 * All ID-data-blocks which have their own 'local' AnimData
 * should have the same arrangement in their structs.
 */
typedef struct IdAdtTemplate {
  ID id;
  AnimData *adt;
} IdAdtTemplate;

/* From: `DNA_object_types.h`, see its doc-string there. */
#define SELECT 1
