/**
 * Audio Plugin for ECSpresso
 *
 * Web Audio API integration via Howler.js for sound effects and music playback.
 * User-defined channels with type-safe volume control, hybrid resource + component API,
 * and asset manager integration.
 */
import { type BasePluginOptions } from 'ecspresso';
import type { AssetsOfWorld, AnyECSpresso, ChannelOfWorld } from 'ecspresso';
import type { Howl } from 'howler';
/**
 * Configuration for a single audio channel.
 */
export interface AudioChannelConfig {
    readonly volume: number;
}
/**
 * Define audio channels with type-safe names and initial volumes.
 * Mirrors `defineCollisionLayers` pattern.
 *
 * @param channels Object mapping channel names to their configuration
 * @returns Frozen channel configuration with inferred channel name union
 *
 * @example
 * ```typescript
 * const channels = defineAudioChannels({
 *   sfx: { volume: 1 },
 *   music: { volume: 0.7 },
 *   ui: { volume: 0.8 },
 * });
 * type Ch = ChannelsOf<typeof channels>; // 'sfx' | 'music' | 'ui'
 * ```
 */
export declare function defineAudioChannels<const T extends Record<string, AudioChannelConfig>>(channels: T): Readonly<T>;
/**
 * Extract channel name union from a `defineAudioChannels` result.
 */
export type ChannelsOf<T> = T extends Record<infer K extends string, AudioChannelConfig> ? K : never;
/**
 * Audio source component attached to entities for positional/entity-bound audio.
 */
export interface AudioSource<Ch extends string = string> {
    /** Asset key for the sound */
    readonly sound: string;
    /** Channel this sound plays on */
    readonly channel: Ch;
    /** Individual volume (0-1) */
    volume: number;
    /** Whether sound loops */
    loop: boolean;
    /** Remove entity when sound ends (like timer autoRemove) */
    autoRemove: boolean;
    /** Whether sound is currently playing (system-managed) */
    playing: boolean;
    /** Howler sound ID (system-managed, -1 = not started) */
    _soundId: number;
}
/**
 * Component types provided by the audio plugin.
 */
export interface AudioComponentTypes<Ch extends string = string> {
    audioSource: AudioSource<Ch>;
}
/**
 * Event to trigger fire-and-forget sound playback from any system.
 */
export interface PlaySoundEvent<Ch extends string = string> {
    /** Asset key for the sound */
    sound: string;
    /** Channel to play on */
    channel?: Ch;
    /** Individual volume (0-1) */
    volume?: number;
    /** Whether sound loops */
    loop?: boolean;
}
/**
 * Event to stop music on a channel.
 */
export interface StopMusicEvent<Ch extends string = string> {
    /** Channel to stop music on. If omitted, stops all music. */
    channel?: Ch;
}
/**
 * Event published when a sound finishes playing.
 */
export interface SoundEndedEvent {
    /** Entity ID if sound was entity-attached, -1 for fire-and-forget */
    entityId: number;
    /** Howler sound ID */
    soundId: number;
    /** Asset key of the sound */
    sound: string;
}
/**
 * Event types provided by the audio plugin.
 */
export interface AudioEventTypes<Ch extends string = string> {
    playSound: PlaySoundEvent<Ch>;
    stopMusic: StopMusicEvent<Ch>;
    soundEnded: SoundEndedEvent;
}
/**
 * Play options for fire-and-forget sound effects.
 */
export interface PlayOptions<Ch extends string = string> {
    /** Channel to play on (uses first defined channel if omitted) */
    channel?: Ch;
    /** Individual volume (0-1, default: 1) */
    volume?: number;
    /** Whether to loop (default: false) */
    loop?: boolean;
}
/**
 * Music playback options.
 */
export interface MusicOptions<Ch extends string = string> {
    /** Channel to play music on (uses first defined channel if omitted) */
    channel?: Ch;
    /** Volume (0-1, default: 1) */
    volume?: number;
    /** Whether to loop (default: true) */
    loop?: boolean;
}
/**
 * Audio state resource providing fire-and-forget SFX and music control.
 * Effective volume = individual * channel * master.
 */
export interface AudioState<Ch extends string = string> {
    /** Play a fire-and-forget sound effect. Returns the Howler sound ID. */
    play(sound: string, options?: PlayOptions<Ch>): number;
    /** Stop a specific sound by its Howler sound ID. */
    stop(soundId: number): void;
    /** Play music on a channel. Stops any existing music on that channel first. */
    playMusic(sound: string, options?: MusicOptions<Ch>): void;
    /** Stop music on a channel. If omitted, stops all music. */
    stopMusic(channel?: Ch): void;
    /** Pause music on a channel. If omitted, pauses all music. */
    pauseMusic(channel?: Ch): void;
    /** Resume music on a channel. If omitted, resumes all music. */
    resumeMusic(channel?: Ch): void;
    /** Set volume for a channel (0-1). */
    setChannelVolume(channel: Ch, volume: number): void;
    /** Get current volume for a channel. */
    getChannelVolume(channel: Ch): number;
    /** Set master volume (0-1). */
    setMasterVolume(volume: number): void;
    /** Get current master volume. */
    getMasterVolume(): number;
    /** Mute all audio. */
    mute(): void;
    /** Unmute all audio. */
    unmute(): void;
    /** Toggle mute state. */
    toggleMute(): void;
    /** Check if audio is muted. */
    isMuted(): boolean;
}
/**
 * Resource types provided by the audio plugin.
 */
export interface AudioResourceTypes<Ch extends string = string> {
    audioState: AudioState<Ch>;
}
/**
 * Configuration options for the audio plugin.
 */
export interface AudioPluginOptions<Ch extends string, G extends string = 'audio'> extends BasePluginOptions<G> {
    /** Channel definitions from defineAudioChannels */
    channels: Readonly<Record<Ch, AudioChannelConfig>>;
}
/**
 * Create an audioSource component for entity-attached audio.
 *
 * @param sound Asset key for the sound
 * @param channel Channel to play on
 * @param options Optional configuration
 * @returns Component object suitable for spreading into spawn()
 *
 * @example
 * ```typescript
 * ecs.spawn({
 *   ...createAudioSource('explosion', 'sfx'),
 *   ...createTransform(100, 200),
 * });
 * ```
 */
export declare function createAudioSource<Ch extends string>(sound: string, channel: Ch, options?: {
    volume?: number;
    loop?: boolean;
    autoRemove?: boolean;
}): Pick<AudioComponentTypes<Ch>, 'audioSource'>;
/**
 * Create a loader function for use with the asset manager.
 * Returns a factory function that loads a Howl when called.
 *
 * @param src URL(s) for the sound file
 * @param options Optional Howl configuration
 * @returns Factory function compatible with asset manager's loader parameter
 *
 * @example
 * ```typescript
 * const ecs = ECSpresso.create()
 *   .withAssets(a => a
 *     .add('explosion', loadSound('/sounds/explosion.mp3'))
 *     .add('bgm', loadSound(['/sounds/bgm.webm', '/sounds/bgm.mp3']))
 *   )
 *   .build();
 * ```
 */
export declare function loadSound(src: string | string[], options?: {
    html5?: boolean;
    preload?: boolean;
}): () => Promise<Howl>;
/**
 * Create an audio plugin for ECSpresso.
 *
 * Provides:
 * - `audioState` resource for fire-and-forget SFX and music
 * - `audioSource` component for entity-attached sounds
 * - Volume hierarchy: individual * channel * master
 * - `playSound` / `stopMusic` event handlers
 * - `soundEnded` event on completion
 * - Automatic cleanup on entity removal (dispose callback)
 *
 * Sounds must be preloaded through the asset pipeline (`loadSound` helper).
 *
 * @example
 * ```typescript
 * const channels = defineAudioChannels({
 *   sfx: { volume: 1 },
 *   music: { volume: 0.7 },
 * });
 *
 * const ecs = ECSpresso.create()
 *   .withAssets(a => a.add('explosion', loadSound('/sfx/boom.mp3')))
 *   .withPlugin(createAudioPlugin({ channels }))
 *   .build();
 *
 * await ecs.initialize();
 * const audio = ecs.getResource('audioState');
 * audio.play('explosion', { channel: 'sfx' });
 * ```
 */
export declare function createAudioPlugin<Ch extends string, G extends string = 'audio'>(options: AudioPluginOptions<Ch, G>): import("ecspresso").Plugin<import("ecspresso").WithResources<import("ecspresso").WithEvents<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, AudioComponentTypes<Ch>>, AudioEventTypes<Ch>>, AudioResourceTypes<Ch>>, import("ecspresso").EmptyConfig, "audio-sync", G, never, "audio-sources">;
/**
 * Typed helpers for the audio plugin.
 * Creates helpers that validate sound keys and channel names against the world type W.
 * Call after .build() using typeof ecs.
 *
 * @template W - Concrete ECS world type (e.g. `typeof ecs`)
 *
 * @example
 * ```typescript
 * const ecs = ECSpresso.create()
 *   .withPlugin(createAudioPlugin({ channels }))
 *   .withAssets(a => a.add('boom', loadSound('/sfx/boom.mp3')))
 *   .build();
 *
 * const { createAudioSource } = createAudioHelpers<typeof ecs>();
 * // Type-safe: 'boom' must be a registered asset, 'sfx' a valid channel
 * createAudioSource('boom', 'sfx');
 * ```
 */
export interface AudioHelpers<W extends AnyECSpresso> {
    createAudioSource: (sound: keyof AssetsOfWorld<W> & string, channel: ChannelOfWorld<W>, options?: {
        volume?: number;
        loop?: boolean;
        autoRemove?: boolean;
    }) => Pick<AudioComponentTypes<ChannelOfWorld<W>>, 'audioSource'>;
}
export declare function createAudioHelpers<W extends AnyECSpresso>(_world?: W): AudioHelpers<W>;
