/**
 * @import {ColorSource} from "../../../common/_types.mjs";
 * @import {Color} from "../../../common/utils/_module.mjs";
 * @import ParticleGenerator from "./particle-generator.mjs";
 * @import SpriteMesh from "../containers/elements/sprite-mesh.mjs";
 * @import BaseSamplerShader from "../rendering/shaders/samplers/base-sampler.mjs";
 */

/**
 * @typedef {"ambient"|"effect"} ParticleGeneratorMode
 */

/**
 * @typedef {number|number[]|{min: number, max: number}} ParticleGeneratorRange
 *   A numeric range:
 *   - number: a fixed value
 *   - [min, max]: a uniform range
 *   - {min, max}: a uniform range
 */

/**
 * @typedef ParticleGeneratorCurvePoint
 * @property {number} time    Normalized lifetime position, from 0 to 1.
 * @property {number} value   Value at this lifetime position.
 */

/**
 * A user-defined function that computes a particle value during particle update.
 * @callback ParticleGeneratorValueFunction
 * @param {ParticleMesh} particle The particle being evaluated.
 * @param {number} dt Frame delta in milliseconds.
 * @returns {number}
 */

/**
 * Advanced particle value configuration.
 * If a base range is combined with a curve, the owning attribute decides how they compose.
 * `fn` is a complete override and must not be combined with the other fields.
 * @typedef ParticleGeneratorValueOptions
 * @property {number} [min]                           Minimum base value sampled once per particle.
 * @property {number} [max]                           Maximum base value sampled once per particle.
 * @property {ParticleGeneratorCurvePoint[]} [curve]  Optional curve over normalized lifetime. Points must start at
 *   time 0, end at time 1, and use strictly increasing times.
 * @property {boolean} [step=false]                   Evaluate curve values in discrete steps instead of interpolation.
 * @property {ParticleGeneratorValueFunction} [fn]    Complete custom value function.
 */

/**
 * @typedef {number|number[]|ParticleGeneratorValueOptions} ParticleGeneratorValue
 */

/**
 * @typedef ParticleGeneratorColorCurvePoint
 * @property {number} time    Normalized lifetime position, from 0 to 1.
 * @property {ColorSource} value A color source at this lifetime position.
 */

/**
 * A user-defined function that computes a particle color during particle update.
 * @callback ParticleGeneratorColorFunction
 * @param {ParticleMesh} particle The particle being evaluated.
 * @param {number} dt Frame delta in milliseconds.
 * @returns {number} A 0xRRGGBB color value.
 */

/**
 * Advanced particle color configuration.
 * `fn` is a complete override and must not be combined with `curve`.
 * @typedef ParticleGeneratorColorValueOptions
 * @property {ParticleGeneratorColorCurvePoint[]} [curve] Optional color curve over normalized lifetime. Points must
 *   start at time 0, end at time 1, and use strictly increasing times.
 * @property {ParticleGeneratorColorFunction} [fn] Complete custom color function.
 */

/**
 * @typedef {ColorSource|ParticleGeneratorColorValueOptions} ParticleGeneratorColorValue
 */

/**
 * @typedef {PIXI.IPointData} ParticleGeneratorPoint
 */

/**
 * @typedef {PIXI.Rectangle|{x: number, y: number, width: number, height: number}} ParticleGeneratorRectangle
 */

/**
 * An anchor source used to attach spawn areas and behaviors to a moving object.
 * Supported sources:
 * - A {@link PlaceableObject} (for example a Token): uses {@link ParticleGeneratorAnchorPoint} to choose a point.
 * - A point in scene coordinates.
 * - A function which returns a point-like object in scene coordinates.
 * @typedef {PIXI.IPointData|(() => ParticleGeneratorPoint)|null} ParticleGeneratorAnchor
 */

/**
 * Which point to use when anchoring to an object.
 * - "center": use `source.center` when available (recommended for Tokens).
 * - "position": use `{x: source.x, y: source.y}`.
 * - function: invoked as (source) => ({x, y}).
 * @typedef {"center"|"position"|((source: object) => ParticleGeneratorPoint)} ParticleGeneratorAnchorPoint
 */

/**
 * @typedef {"default"|"orbit"|"follow"} ParticleGeneratorBehaviorId
 */

/**
 * @typedef ParticleGeneratorOrbitOptions
 * @property {ParticleGeneratorRange|null} [radius=null] Orbit radius in pixels.
 *   If null, use the particle's initial distance from the anchor.
 * @property {ParticleGeneratorRange} [angularSpeed=120] Angular speed in degrees per second.
 * @property {ParticleGeneratorRange} [phase=[0, 360]] Initial angle in degrees.
 *   Only used when {@link ParticleGeneratorOrbitOptions#radius} is provided.
 * @property {ParticleGeneratorRange} [radialSpeed=0] Radial speed in pixels per second.
 * @property {1|-1|"random"} [direction=1] Orbit direction.
 * @property {"none"|"tangent"|"radial"} [rotation="none"] If set, override sprite rotation each frame.
 */

/**
 * @typedef ParticleGeneratorFollowOptions
 * @property {ParticleGeneratorPoint|null} [offset=null] Fixed local offset from the anchor in pixels.
 *   If null, use the particle's initial offset from the anchor.
 * @property {number} [stiffness=1] A 0..1 smoothing factor. 1 snaps to the target every frame.
 */

/**
 * @typedef ParticleGeneratorBehavior
 * @property {(generator: ParticleGenerator) => void} [initialize]             Called once during construction.
 * @property {(particle: ParticleMesh, ctx: object) => void} [spawn]           Called for each spawned particle.
 * @property {(particle: ParticleMesh, dt: number, ctx: object) => boolean|void} [update]
 *   Called for each particle during update.
 *   Return true to indicate the behavior handled positional integration for this particle.
 */

/**
 * @typedef {"interior"|"boundary"} ParticleGeneratorAreaSampleMode
 */

/**
 * Source data for a {@link foundry.data.BaseShapeData} subclass.
 * @typedef {object} ParticleGeneratorShapeDataSource
 * @property {string} type A valid type from {@link foundry.data.BaseShapeData.TYPES}.
 */

/**
 * @typedef {ParticleGeneratorPoint[]} ParticleGeneratorPointList
 */

/**
 * @typedef {ParticleGeneratorPointList|ParticleGeneratorPointList[]} ParticleGeneratorPolylineDefinition
 */

/**
 * A spawn area definition in scene coordinates.
 * When an {@link ParticleGeneratorAnchor} is provided, object-based areas are interpreted as offsets relative
 * to the anchor point. To provide an absolute rectangle while anchored, pass a {@link PIXI.Rectangle}.
 * {@link foundry.data.BaseShapeData} instances and source data are always interpreted in absolute scene coordinates.
 * Supported shapes:
 * - Point: `{x, y}`
 * - Rect: `{x, y, width, height}` or `PIXI.Rectangle`
 * - Circle: `{x, y, radius}`
 * - Ring: `{x, y, innerRadius, outerRadius}` or `{x, y, radius: [inner, outer]}`
 * - Line: `{from: {x, y}, to: {x, y}}`
 * - Points: `{points: [{x, y}, ...]}` or `{shape: "points", points: [{x, y}, ...]}`
 * - Polyline: `{path: [{x, y}, ...]}`, `{shape: "path", path: [...]}`, or
 *   `{shape: "polyline", points: [{x, y}, ...]}`
 * - Ellipse: `{x, y, radiusX, radiusY}`, `{shape: "ellipse", x, y, radiusX, radiusY}`, with optional
 *   `holeScale`, `minAngle`, `maxAngle`, `shapeRotation`, and `affectRotation`.
 * - Shape instance: {@link foundry.data.BaseShapeData}
 * - Shape source data: `{type: "circle", x, y, radius}` using the {@link foundry.data.BaseShapeData} schema.
 * Plain object areas without a `type` are inferred from their properties.
 * @typedef {PIXI.Rectangle|foundry.data.PolygonTree|foundry.data.BaseShapeData|ParticleGeneratorShapeDataSource|
 *   ParticleGeneratorPoint|
 *   {x: number, y: number, width: number, height: number}|
 *   {x: number, y: number, radius: number|number[]}|
 *   {x: number, y: number, innerRadius: number, outerRadius: number}|
 *   {from: ParticleGeneratorPoint, to: ParticleGeneratorPoint}|
 *   {points: ParticleGeneratorPointList, shape?: "points"}|
 *   {path: ParticleGeneratorPolylineDefinition, shape?: "path"|"polyline"}|
 *   {points: ParticleGeneratorPolylineDefinition, shape: "polyline"}|
 *   {x: number, y: number, radiusX: number, radiusY: number, shape?: "ellipse", holeScale?: number,
 *     minAngle?: number, maxAngle?: number, shapeRotation?: number, rotation?: number,
 *     affectRotation?: boolean}} ParticleGeneratorArea
 */

/**
 * @typedef ParticleGeneratorFadeOptions
 * @property {number} [in=0] Fade-in duration in milliseconds, or a fraction of lifetime if 0 < value < 1.
 * @property {number} [out=0] Fade-out duration in milliseconds, or a fraction of lifetime if 0 < value < 1.
 */

/**
 * Particle rotation configuration that controls initial rotation of spawned particles and their rotational speed.
 *
 * Initial rotation is determined as:
 * 1. Base: Zero or, if `alignVelocity` is true, the particle's velocity angle
 * 2. Offset: `initial` is added to the base
 * 3. Spread: a random value in `[-spread, spread]` is added, default full-circle randomization
 *
 * After spawn, `speed` (degrees per second) governs ongoing rotation.
 *
 * @typedef ParticleGeneratorRotationOptions
 * @property {boolean} [alignVelocity=false]      Align initial rotation to the particle's velocity direction.
 * @property {number} [initial=0]                 Fixed rotation offset in radians, additive to the base.
 * @property {number} [spread=Math.PI]            Symmetric random spread in radians around the base+initial.
 *                                                0 means no randomization; Math.PI gives full-circle random.
 * @property {ParticleGeneratorValue} [speed=0]   Rotation speed over the particle lifetime. Initially specified
 *                                                in degrees-per-second, internally managed in radians-per-second.
 */

/**
 * Particle velocity configuration.
 * Supported shapes:
 * - Fixed vector: `{x, y}`
 * - Cartesian ranges: `{x: range, y: range}`
 * - Polar: `{speed: value, angle: range}` where angle is in degrees.
 * - Function: `{fn: (particle, dt, out) => { ... }}` where `out` is a reusable PIXI.Point. This is evaluated at
 *   spawn and during each update as the full velocity vector for that frame.
 */

/**
 * A user-defined function that computes a particle velocity vector in pixels per second.
 * The function may either write into `out` and return void, or return a point-like velocity object.
 * @callback ParticleGeneratorVelocityFunction
 * @param {ParticleMesh} particle The particle being evaluated.
 * @param {number} dt Frame delta in milliseconds.
 * @param {PIXI.Point} out Reusable point which may receive the velocity.
 * @returns {PIXI.IPointData|void|null}
 */

/**
 * @typedef {ParticleGeneratorPoint|{x: ParticleGeneratorRange, y: ParticleGeneratorRange}|
 *   {speed: ParticleGeneratorValue, angle: ParticleGeneratorRange}|
 *   {fn: ParticleGeneratorVelocityFunction}} ParticleGeneratorVelocityOptions
 */

/**
 * @typedef {"none"|"kill"|"clamp"|"wrap"|"bounce"} ParticleGeneratorConstraintMode
 */

/**
 * @typedef {"random"|"palette"|"byTexture"} ParticleGeneratorDebugTintMode
 */

/**
 * @typedef ParticleGeneratorDebugTintOptions
 * @property {ParticleGeneratorDebugTintMode} [mode="random"] How to apply debug tinting.
 * @property {number[]} [palette] A list of 0xRRGGBB colors used for "palette" or "byTexture" modes.
 */

/**
 * @typedef ParticleGeneratorDebugOptions
 * @property {boolean} [useWhiteTexture=false]  If true, fall back to {@link PIXI.Texture.WHITE} when no textures are
 *                                             configured.
 * @property {ParticleGeneratorDebugTintOptions|boolean|null} [tint=null]  Optional automatic tinting for spawned
 *                                                                         particles.
 * @property {boolean} [stats=false]            Whether to collect debug statistics.
 * @property {boolean} [profile=false]          Whether to capture per-tick timings (requires stats).
 */

/**
 * @typedef ParticleGeneratorDebugStats
 * @property {number} active Current number of active particles.
 * @property {number} pool Current number of pooled particles.
 * @property {number} target Current adjusted target particle count.
 * @property {number} spawnAttempts Number of spawn attempts.
 * @property {number} spawned Number of successfully spawned particles.
 * @property {number} spawnRejectedProbability Spawn attempts rejected by probability (auto-spawn only).
 * @property {number} spawnRejectedPositionTest Spawn attempts rejected by
 *   {@link ParticleGeneratorConfiguration#positionTest}.
 * @property {number} spawnRejectedNoArea Spawn attempts rejected because no valid spawn area was available.
 * @property {number} recycledLifetime Particles recycled due to lifetime expiration.
 * @property {number} recycledConstraint Particles recycled due to constraint handling.
 * @property {number} recycledStop Particles recycled/cleared due to a hard stop.
 * @property {number} newlyVisibleAreaCount Number of newly-visible rectangles this frame (ambient mode).
 * @property {number} updateMS Time spent updating particles during the most recent tick (milliseconds).
 * @property {number} spawnMS Time spent auto-spawning particles during the most recent tick (milliseconds).
 * @property {number} tickMS Total tick time for the most recent tick (milliseconds).
 */

/**
 * @typedef ParticleGeneratorClipOptions
 * @property {boolean|null} [enabled=null] Whether to apply a managed clip mask.
 *   If null, defaults to true in ambient mode and false in effect mode.
 * @property {ParticleGeneratorRectangle|null} [rect=null] Optional clip rectangle in scene coordinates.
 *   If omitted, a shape-based spawn area is used when available. Otherwise the generator bounds are used.
 */

/**
 * @callback ParticleGeneratorPositionTest
 * @param {number} x
 * @param {number} y
 * @param {{generator: ParticleGenerator, particle: ParticleMesh}} context
 * @returns {boolean}
 */

/**
 * @callback ParticleGeneratorParticleCallback
 * @param {ParticleMesh} particle
 * @param {{generator: ParticleGenerator}} context
 * @returns {void}
 */

/**
 * @callback ParticleGeneratorDeathCallback
 * @param {ParticleMesh} particle
 * @param {{generator: ParticleGenerator, reason: string}} context
 * @returns {void}
 */

/**
 * @callback ParticleGeneratorTickCallback
 * @param {number} dt
 * @param {ParticleGenerator} generator
 * @returns {void}
 */

/**
 * @typedef ParticleGeneratorConfiguration
 * @property {ParticleGeneratorMode} [mode="ambient"] The runtime mode.
 *   - "ambient": maintains a stable density in the visible region (viewport-based budget).
 *   - "effect": spawns in a defined area; particles are lifetime-driven unless constrained.
 * @property {ParticleGeneratorRectangle|null} [bounds=null] Optional generator bounds in scene coordinates.
 *   This is used for coordinate conversion, viewport clamping, and optional clipping.
 *   Defaults to the current Scene dimensions.
 * @property {number} [count=0] The target particle count.
 *   - In "ambient" mode, this is the maximum for the full bounds and is scaled by visible area.
 *   - In "effect" mode, this is the absolute target.
 * @property {number} [spawnRate=300] The maximum number of particles that may be spawned per second
 *   (auto-spawn mode).
 * @property {number} [perFrame=5] Deprecated since v14. Use `spawnRate` instead. If `spawnRate` is omitted or null,
 *   this legacy per-frame value is converted using the Pixi ticker target frame rate.
 * @property {number} [initial=0.25] The initial proportion (0..1) of the computed target particle count to spawn on
 *   start.
 * @property {boolean|null} [manual=null] If true, particles are never spawned automatically.
 *   If null, defaults to true in "effect" mode unless `spawnRate` is explicitly configured and false in "ambient"
 *   mode. Legacy `perFrame` does not change the default.
 * @property {number} [probability=1] The chance (0..1) that a spawn attempt actually creates a particle.
 * @property {number} [viewPadding=0] A proportion (0..1+) of extra area around the visible region used for spawning.
 *                                For example, 0.2 extends the spawn region by 20% in each dimension.
 * @property {boolean} [newlyVisible=true] If true, prioritize spawning particles in newly-visible areas when the view
 *   changes (pan/zoom).
 * @property {boolean} [randomizeAgeInPadding=true] If true, particles spawned in padded regions can start partially
 *   through their lifetime.
 * @property {ParticleGeneratorArea|null} [area=null] The default spawn area in "effect" mode (scene coordinates).
 * @property {ParticleGeneratorAreaSampleMode} [sampleMode="interior"] Which part of the spawn area to sample.
 * @property {ParticleGeneratorPositionTest|null} [positionTest=null] An optional spawn validator.
 *   The function is invoked as (x, y, {generator, particle}) and must return true if the location is valid.
 *   Coordinates are scene coordinates in pixels. The generator evaluates a single candidate position per spawn attempt.
 * @property {ParticleGeneratorConstraintMode|null} [constraintMode=null] How to handle particles leaving the constraint
 *   area.
 *   If null, defaults to "kill" in ambient mode and "none" in effect mode.
 * @property {"budget"|"view"|"world"|ParticleGeneratorRectangle|null} [constraintArea=null] The constraint area.
 *   - "budget": the padded viewport rectangle.
 *   - "view": the unpadded viewport rectangle.
 *   - "world": the generator bounds.
 *   - Rectangle: a custom rectangle in scene coordinates.
 *   If null, defaults to "budget" in ambient mode when constraintMode is not "none".
 * @property {number} [restitution=1] Bounce restitution factor (0..1) used when constraintMode is "bounce".
 * @property {ParticleGeneratorClipOptions|ParticleGeneratorRectangle|boolean} [clip]
 *   Clip (=> mask) configuration. Use true for default clipping. If no clip rectangle is provided and
 *   {@link ParticleGeneratorConfiguration#area} is a {@link foundry.data.BaseShapeData}, that shape is used.
 * @property {PIXI.DisplayObject|PIXI.IShape|foundry.data.BaseShapeData|null} [mask=null] An explicit mask for the
 *   particle container. Accepts either a pre-built PIXI.DisplayObject, a PIXI shape, or a
 *   {@link foundry.data.BaseShapeData} which is drawn into a PIXI.Graphics.
 *   Presence implies masking is desired, and this mask takes precedence over `clip`.
 * @property {ParticleGeneratorRange} [lifetime=1000] The particle lifetime in milliseconds.
 * @property {ParticleGeneratorFadeOptions} [fade] Fade envelope configuration.
 * @property {ParticleGeneratorVelocityOptions|null} [velocity=null] The particle velocity in pixels per second.
 * @property {ParticleGeneratorRotationOptions} [rotation] Rotation configuration for particles.
 * @property {{enabled: boolean, intensity: number}} [drift] Optional random drift configuration.
 * @property {(PIXI.Texture|string)[]} [textures=[]] The particle texture sources.
 *   Each entry may be a PIXI.Texture or a string path usable by foundry.canvas.getTexture/PIXI.Texture.from.
 * @property {ParticleGeneratorPoint|null} [particleAnchor=null] Optional sprite anchor override for particle textures.
 *   If null, each texture's `defaultAnchor` is used.
 * @property {typeof BaseSamplerShader|null} [shaderClass=null] Optional shader class used to render particles.
 *   Defaults to {@link BaseSamplerShader}. Batchable shaders are the fast path for sustained effects.
 *   ParticleGenerator creates plain {@link SpriteMesh} instances, so shaders which require
 *   {@link foundry.canvas.primary.PrimarySpriteMesh} occlusion or depth data are not supported.
 * @property {PIXI.BLEND_MODES} [blend=PIXI.BLEND_MODES.NORMAL] The blend mode used to render particles.
 * @property {number|{intensity: number, quality?: number}|
 *   {enabled: boolean, intensity: number, quality?: number}|null} [blur=null]
 *   An optional blur filter applied to the internal container.
 * @property {ParticleGeneratorValue} [alpha=1] The alpha value for particles.
 * @property {ParticleGeneratorValue} [scale=1] The scale value for particles.
 * @property {ParticleGeneratorColorValue} [tint=0xFFFFFF] The tint color for particles.
 * @property {number} [elevation=0] The elevation for the particle container.
 * @property {number} [sort=0] The sorting key for the particle container.
 * @property {PIXI.Container} [container] The parent container which receives the internal particle container. Defaults
 *   to canvas.primary.
 * @property {PIXI.Ticker} [ticker] The ticker used to drive the update loop. Defaults to
 *   {@link CanvasAnimation.ticker}.
 * @property {ParticleGeneratorAnchor} [anchor=null] An optional anchor used to attach areas and behaviors.
 * @property {ParticleGeneratorAnchorPoint} [anchorPoint="center"] Which point to use when anchoring.
 * @property {ParticleGeneratorPoint|null} [anchorOffset=null] A fixed offset (scene pixels) applied to the anchor.
 * @property {ParticleGeneratorBehaviorId|ParticleGeneratorBehavior|null} [behavior=null] Optional behavior.
 * @property {ParticleGeneratorOrbitOptions} [orbit] Orbit behavior options.
 * @property {ParticleGeneratorFollowOptions} [follow] Follow behavior options.
 * @property {ParticleGeneratorParticleCallback|null} [onSpawn=null]
 *   An optional callback called after the particle has been placed and configured.
 *   This is a handy place to attach custom per-particle data for batch shaders.
 * @property {ParticleGeneratorParticleCallback|null} [onUpdate=null]
 *   An optional callback called each frame for each live particle, after position, rotation, tint, and alpha have been
 *   computed.
 * @property {ParticleGeneratorDeathCallback|null} [onDeath=null]
 *   An optional callback called when a particle is recycled.
 * @property {ParticleGeneratorTickCallback|null} [onTick=null]
 *   An optional callback called per frame (not per particle!).
 * @property {ParticleGeneratorDebugOptions|boolean|null} [debug=null] Optional debugging helpers.
 */

/**
 * @typedef {SpriteMesh & {
 *   generator: ParticleGenerator,
 *   elapsedTime: number,
 *   time: number,
 *   lifetime: number,
 *   fadeInDuration: number,
 *   fadeOutDuration: number,
 *   maxAlpha: number,
 *   _baseScale: number,
 *   _baseTint: number,
 *   _baseSpeed?: number,
 *   _baseRotationSpeed: number,
 *   rotationSpeed: number,
 *   movementSpeed: PIXI.Point,
 *   _movementDirectionX?: number,
 *   _movementDirectionY?: number,
 *   _movementDriftX?: number,
 *   _movementDriftY?: number,
 *   _velocityFunctionBounceX?: number,
 *   _velocityFunctionBounceY?: number
 * }} ParticleMesh
 */

/**
 * @typedef {object} ParticleGeneratorPolylineSegment
 * @property {number} ax
 * @property {number} ay
 * @property {number} bx
 * @property {number} by
 * @property {number} length
 */

/**
 * @typedef {object} ParticleGeneratorLocalArea
 * @property {"shape"|"polygonTree"|"point"|"points"|"polyline"|"ellipse"|"ambientRect"|"line"} type
 * @property {foundry.data.BaseShapeData|foundry.data.PolygonTree} [data]
 * @property {ParticleGeneratorAreaSampleMode} [sampleMode]
 * @property {number} [x]
 * @property {number} [y]
 * @property {ParticleGeneratorPointList} [points]
 * @property {ParticleGeneratorPolylineSegment[]} [segments]
 * @property {number[]} [cumulativeLengths]
 * @property {number} [totalLength]
 * @property {ParticleGeneratorPoint|null} [fallbackPoint]
 * @property {number} [radiusX]
 * @property {number} [radiusY]
 * @property {number} [minAngle]
 * @property {number} [maxAngle]
 * @property {number} [shapeRotation]
 * @property {boolean} [affectRotation]
 * @property {number} [cosRotation]
 * @property {number} [sinRotation]
 * @property {number} [holeAreaScale]
 * @property {PIXI.Rectangle} [rect]
 * @property {number} [ax]
 * @property {number} [ay]
 * @property {number} [bx]
 * @property {number} [by]
 */

/**
 * @typedef CanvasAnimationAttribute
 * @property {string} attribute             The attribute name being animated
 * @property {object} parent                The object within which the attribute is stored
 * @property {number|Color} to              The destination value of the attribute
 * @property {number|Color} [from]          An initial value of the attribute, otherwise `parent[attribute]` is used
 * @property {number} [delta]               The computed delta between to and from
 * @property {number} [done]                The amount of the total delta which has been animated
 * @property {boolean} [color]              Is this a color animation that applies to RGB channels
 */

/**
 * @typedef {"easeInOutCosine"|"easeOutCircle"|"easeInCircle"|
 *   (percentage: number) => number} CanvasAnimationEasingFunction
 */

/**
 * @typedef CanvasAnimationOptions
 * @property {PIXI.DisplayObject} [context] A DisplayObject which defines context to the PIXI.Ticker function
 * @property {string|symbol} [name]         A unique name which can be used to reference the in-progress animation
 * @property {number} [duration]            A duration in milliseconds over which the animation should occur
 * @property {number} [time=0]              The current time of the animation, in milliseconds
 * @property {number} [priority]            A priority in PIXI.UPDATE_PRIORITY which defines when the animation
 *                                          should be evaluated related to others
 * @property {CanvasAnimationEasingFunction} [easing] An easing function used to translate animation time or
 *                                                    the string name of a static member of CanvasAnimation
 * @property {(elapsedMS: number, animation: CanvasAnimationData) => void} [ontick]
 *   A callback function which fires after every frame
 * @property {Promise<any>} [wait]          The animation isn't started until this promise resolves
 */

/**
 * @typedef _CanvasAnimationData
 * @property {() => void} fn                            The animation function being executed each frame
 * @property {CanvasAnimationAttribute[]} attributes    The attributes being animated
 * @property {number} state                             The current state of the animation
 *                                                      (see {@link foundry.canvas.animation.CanvasAnimation.STATES})
 * @property {Promise<boolean>} promise                 A Promise which resolves once the animation is complete
 * @property {(completed: boolean) => void} resolve     The resolution function, allowing animation to be ended early
 * @property {(error: Error) => void} reject            The rejection function, allowing animation to be ended early
 */

/**
 * @typedef {_CanvasAnimationData & CanvasAnimationOptions} CanvasAnimationData
 */

/**
 * @typedef ChatBubbleOptions
 * @property {string[]} [cssClasses]    An optional array of CSS classes to apply to the resulting bubble
 * @property {boolean} [pan=true]       Pan to the token speaker for this bubble, if allowed by the client
 * @property {boolean} [requireVisible=false] Require that the token be visible in order for the bubble to be rendered
 */
