/**
 * Sprite Animation Plugin for ECSpresso
 *
 * ECS-native frame-based sprite animation. Advances through spritesheet frames,
 * handles loop modes (once, loop, pingPong), publishes completion events, and
 * syncs the current frame's texture to the PixiJS Sprite via structural access.
 *
 * Renderer2D is a required dependency — the `sprite` component comes from that plugin.
 * This plugin declares only `spriteAnimation` as its component type.
 */
import { type BasePluginOptions } from 'ecspresso';
import type { BaseWorld } from 'ecspresso';
import type { Spritesheet, SpritesheetData, TextureSource } from 'pixi.js';
/** BaseWorld narrowed to sprite-animation components for typed access in helpers. */
type SpriteAnimationWorld = BaseWorld<SpriteAnimationComponentTypes>;
export type AnimationLoopMode = 'once' | 'loop' | 'pingPong';
/**
 * A single animation clip: an ordered sequence of texture frames with timing.
 * Immutable and shared across entities.
 */
export interface SpriteAnimationClip {
    readonly frames: readonly unknown[];
    readonly frameDuration: number;
    readonly frameDurations: readonly number[] | null;
    readonly loop: AnimationLoopMode;
}
/**
 * Input format for defining a clip. Accepts either uniform or per-frame timing.
 */
export interface SpriteAnimationClipInput {
    /** Array of PixiJS Texture objects */
    frames: readonly unknown[];
    /** Uniform seconds-per-frame (used when frameDurations is not provided) */
    frameDuration?: number;
    /** Per-frame durations in seconds (overrides frameDuration) */
    frameDurations?: readonly number[];
    /** Loop mode (default: 'loop') */
    loop?: AnimationLoopMode;
}
/**
 * A named collection of animation clips. Immutable and shared across entities.
 * Parameterized by A (animation name union) for compile-time validation.
 */
export interface SpriteAnimationSet<A extends string = string> {
    readonly id: string;
    readonly clips: {
        readonly [K in A]: SpriteAnimationClip;
    };
    readonly defaultClip: A;
}
/**
 * Per-entity runtime animation state.
 */
export interface SpriteAnimation<A extends string = string> {
    readonly set: SpriteAnimationSet<A>;
    current: A;
    currentFrame: number;
    elapsed: number;
    playing: boolean;
    speed: number;
    direction: 1 | -1;
    totalLoops: number;
    completedLoops: number;
    justFinished: boolean;
    onComplete?: (data: SpriteAnimationEventData) => void;
}
/**
 * Component types provided by the sprite animation plugin.
 */
export interface SpriteAnimationComponentTypes<A extends string = string> {
    spriteAnimation: SpriteAnimation<A>;
}
/**
 * Data published when an animation completes.
 */
export interface SpriteAnimationEventData {
    entityId: number;
    animation: string;
}
export interface SpriteAnimationPluginOptions<G extends string = 'spriteAnimation'> extends BasePluginOptions<G> {
}
/**
 * Define a single-clip animation set named 'default'.
 * For simple use cases like spinning coins, pulsing effects, etc.
 *
 * @param id Unique identifier for this animation set
 * @param clip Clip definition
 * @returns A frozen SpriteAnimationSet with one clip named 'default'
 */
export declare function defineSpriteAnimation(id: string, clip: SpriteAnimationClipInput): SpriteAnimationSet<'default'>;
/**
 * Define a multi-clip animation set with named animations.
 * Animation names are inferred from the keys of the clips object.
 *
 * @param id Unique identifier for this animation set
 * @param clips Object mapping animation names to clip definitions
 * @param options Optional configuration (defaultClip)
 * @returns A frozen SpriteAnimationSet with inferred animation name union
 */
export declare function defineSpriteAnimations<A extends string>(id: string, clips: Record<A, SpriteAnimationClipInput>, options?: {
    defaultClip?: NoInfer<A>;
}): SpriteAnimationSet<A>;
/**
 * Create a spriteAnimation component from an animation set.
 *
 * @param set The animation set to use
 * @param options Optional configuration (initial clip, speed, onComplete event)
 * @returns Component object suitable for spreading into spawn()
 */
export declare function createSpriteAnimation<A extends string>(set: SpriteAnimationSet<A>, options?: {
    initial?: A;
    speed?: number;
    totalLoops?: number;
    onComplete?: (data: SpriteAnimationEventData) => void;
}): Pick<SpriteAnimationComponentTypes<A>, 'spriteAnimation'>;
/**
 * Switch an entity's current animation at runtime.
 * Resets state if switching to a different animation (or restart=true).
 *
 * @returns false if entity has no spriteAnimation or animation name doesn't exist
 */
export declare function playAnimation(ecs: SpriteAnimationWorld, entityId: number, animation: string, options?: {
    restart?: boolean;
    speed?: number;
}): boolean;
/**
 * Pause an entity's animation.
 *
 * @returns false if entity has no spriteAnimation
 */
export declare function stopAnimation(ecs: SpriteAnimationWorld, entityId: number): boolean;
/**
 * Resume a paused animation.
 *
 * @returns false if entity has no spriteAnimation
 */
export declare function resumeAnimation(ecs: SpriteAnimationWorld, entityId: number): boolean;
/**
 * Create a sprite animation plugin for ECSpresso.
 *
 * Provides:
 * - Frame-based animation system processing spriteAnimation components
 * - Loop modes: once, loop, pingPong
 * - justFinished one-frame flag for completion detection
 * - onComplete event publishing
 * - Sprite texture sync via structural cross-plugin access
 * - Change detection via markChanged
 */
export declare function createSpriteAnimationPlugin<G extends string = 'spriteAnimation'>(options?: SpriteAnimationPluginOptions<G>): import("ecspresso").Plugin<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, SpriteAnimationComponentTypes<string>>, import("ecspresso").EmptyConfig, "sprite-animation-update", G, never, never>;
/**
 * Per-clip timing/loop overrides keyed by animation name. Each entry tweaks
 * a single clip; omitted entries fall back to the top-level defaults.
 */
export type SheetClipOverrides<A extends string> = {
    readonly [K in A]?: Omit<SpriteAnimationClipInput, 'frames'>;
};
/**
 * Extract the animation-name union from a typed SpritesheetData. Falls back
 * to `string` for untyped sheets.
 */
export type SheetAnimationKeys<S extends SpritesheetData> = S extends {
    animations: infer A;
} ? A extends Record<infer K, unknown> ? K extends string ? K : never : string : string;
/**
 * Build a clip from a named animation in a loaded PixiJS Spritesheet.
 *
 * @example
 *   const sheet = await Assets.load<Spritesheet>('/hero.json');
 *   const idle = clipFromSheet(sheet, 'idle', { frameDuration: 1 / 12 });
 */
export declare function clipFromSheet(sheet: Spritesheet, animationName: string, options?: Omit<SpriteAnimationClipInput, 'frames'>): SpriteAnimationClip;
/**
 * Build an animation set from every named animation in a PixiJS Spritesheet.
 * When the sheet is typed as `Spritesheet<MyData>`, animation names and
 * `defaultClip` / `perClip` keys are inferred at compile time.
 *
 * @example
 *   const sheet = ecs.assets.get('hero'); // Spritesheet<HeroData>
 *   const set = animationSetFromSheet('hero', sheet, {
 *     defaultClip: 'idle',
 *     frameDuration: 1 / 12,
 *     perClip: { attack: { loop: 'once' } },
 *   });
 */
export declare function animationSetFromSheet<S extends SpritesheetData = SpritesheetData>(id: string, sheet: Spritesheet<S>, options?: {
    defaultClip?: SheetAnimationKeys<S>;
    frameDuration?: number;
    loop?: AnimationLoopMode;
    perClip?: SheetClipOverrides<SheetAnimationKeys<S>>;
}): SpriteAnimationSet<SheetAnimationKeys<S>>;
/**
 * Slice a grid-arranged sprite sheet into a clip. Use when you don't have a
 * TexturePacker JSON — just an image and uniform cell dimensions. Cells are
 * walked row-major.
 *
 * Specify exactly one of `rows`, `count`, or `indices` to define the cell set
 * (along with `columns`). Combining `count` and `indices` is rejected.
 *
 * Returns a `Promise` because pixi.js is imported lazily — keeps the static
 * module graph free of a runtime pixi dependency for consumers who only use
 * the sheet-based helpers.
 *
 * @example
 *   const tex = await Assets.load<Texture>('/coin.png');
 *   const clip = await clipFromGrid({
 *     source: tex.source,
 *     frameWidth: 16, frameHeight: 16,
 *     columns: 8, count: 8,
 *     frameDuration: 1 / 10,
 *   });
 */
export declare function clipFromGrid(input: {
    source: TextureSource;
    frameWidth: number;
    frameHeight: number;
    columns: number;
    rows?: number;
    /** Explicit row-major, 0-based cell indices. Mutually exclusive with `count`. */
    indices?: readonly number[];
    /** Number of cells to use, walked row-major. Mutually exclusive with `indices`. */
    count?: number;
    /** Pixels between cells. */
    spacing?: number;
    /** Pixels around the sheet edge. */
    margin?: number;
    frameDuration?: number;
    frameDurations?: readonly number[];
    loop?: AnimationLoopMode;
}): Promise<SpriteAnimationClip>;
/**
 * Build an asset-manager-compatible loader for a PixiJS spritesheet atlas
 * (TexturePacker JSON, etc.). Returns the fully-parsed `Spritesheet` object
 * with `.animations` and `.textures` populated.
 *
 * The loader performs a runtime shape check on the resolved value — `Assets.load<T>`
 * is purely nominal in PixiJS, so pointing this at a non-atlas URL would
 * otherwise surface as a misleading 'animation not found' error deep in
 * `clipFromSheet`/`animationSetFromSheet`. The shape check turns that into a
 * load-time error with a clear message.
 *
 * To get literal animation-name inference, declare `S` as an
 * `interface ... extends SpritesheetData` (a `type` alias re-widens via
 * `SpritesheetData.animations`'s `Dict<string[]>` string index signature).
 *
 * @example
 *   interface HeroData extends SpritesheetData {
 *     animations: { idle: string[]; walk: string[]; attack: string[] };
 *   }
 *
 *   ecs.builder.withAssets(a => a
 *     .add('hero', spritesheetLoader<HeroData>('/hero.json'))
 *   );
 *
 *   // Later:
 *   const sheet = ecs.assets.get('hero');           // Spritesheet<HeroData>
 *   const set = animationSetFromSheet('hero', sheet); // names inferred
 */
export declare function spritesheetLoader<S extends SpritesheetData = SpritesheetData>(url: string): () => Promise<Spritesheet<S>>;
export {};
