/**
 * Collision Plugin for ECSpresso
 *
 * Provides layer-based collision detection with events.
 * Uses worldTransform for position (world-space collision).
 * Supports AABB and circle colliders.
 */
import { type BasePluginOptions } from 'ecspresso';
import type { TransformWorldConfig } from '../spatial/transform';
/**
 * Axis-Aligned Bounding Box collider.
 */
export interface AABBCollider {
    /** Width of the bounding box */
    width: number;
    /** Height of the bounding box */
    height: number;
    /** X offset from entity position (default: 0) */
    offsetX?: number;
    /** Y offset from entity position (default: 0) */
    offsetY?: number;
}
/**
 * Circle collider.
 */
export interface CircleCollider {
    /** Radius of the circle */
    radius: number;
    /** X offset from entity position (default: 0) */
    offsetX?: number;
    /** Y offset from entity position (default: 0) */
    offsetY?: number;
}
/**
 * Collision layer configuration.
 */
export interface CollisionLayer<L extends string = never> {
    /** The layer this entity belongs to */
    layer: L;
    /** Layers this entity can collide with */
    collidesWith: readonly L[];
}
/**
 * Component types provided by the collision plugin.
 * Included automatically via `.withPlugin(createCollisionPlugin())`.
 *
 * @example
 * ```typescript
 * const ecs = ECSpresso.create()
 *   .withPlugin(createCollisionPlugin())
 *   .withComponentTypes<{ sprite: Sprite; enemy: boolean }>()
 *   .build();
 * ```
 */
export interface CollisionComponentTypes<L extends string = never> {
    aabbCollider: AABBCollider;
    circleCollider: CircleCollider;
    collisionLayer: CollisionLayer<L>;
}
/**
 * Event fired when two entities collide.
 *
 * Normal components are flattened (`normalX`/`normalY`) rather than nested
 * in a sub-object to avoid a per-event allocation in the collision hot path.
 */
export interface CollisionEvent<L extends string = never> {
    /** First entity in the collision */
    entityA: number;
    /** Second entity in the collision */
    entityB: number;
    /** Layer of the first entity */
    layerA: L;
    /** Layer of the second entity */
    layerB: L;
    /** Contact normal X, pointing from entityA toward entityB */
    normalX: number;
    /** Contact normal Y, pointing from entityA toward entityB */
    normalY: number;
    /** Penetration depth (positive = overlapping) */
    depth: number;
}
/**
 * Event types provided by the collision plugin.
 */
export interface CollisionEventTypes<L extends string = never> {
    collision: CollisionEvent<L>;
}
/**
 * Configuration options for the collision plugin.
 */
export interface CollisionPluginOptions<G extends string = 'physics'> extends BasePluginOptions<G> {
    /** Name of the collision event (default: 'collision') */
    collisionEventName?: string;
}
/**
 * Create an AABB collider component.
 *
 * @param width Width of the bounding box
 * @param height Height of the bounding box
 * @param offsetX X offset from entity position
 * @param offsetY Y offset from entity position
 * @returns Component object suitable for spreading into spawn()
 *
 * @example
 * ```typescript
 * ecs.spawn({
 *   ...createTransform(100, 200),
 *   ...createAABBCollider(50, 30),
 * });
 * ```
 */
export declare function createAABBCollider(width: number, height: number, offsetX?: number, offsetY?: number): {
    aabbCollider: AABBCollider;
};
/**
 * Create a circle collider component.
 *
 * @param radius Radius of the circle
 * @param offsetX X offset from entity position
 * @param offsetY Y offset from entity position
 * @returns Component object suitable for spreading into spawn()
 *
 * @example
 * ```typescript
 * ecs.spawn({
 *   ...createTransform(100, 200),
 *   ...createCircleCollider(25),
 * });
 * ```
 */
export declare function createCircleCollider(radius: number, offsetX?: number, offsetY?: number): {
    circleCollider: CircleCollider;
};
/**
 * Create a collision layer component.
 *
 * @param layer The layer this entity belongs to
 * @param collidesWith Layers this entity can collide with
 * @returns Component object suitable for spreading into spawn()
 *
 * @example
 * ```typescript
 * ecs.spawn({
 *   ...createTransform(100, 200),
 *   ...createAABBCollider(50, 30),
 *   ...createCollisionLayer('player', ['enemy', 'obstacle']),
 * });
 * ```
 */
export declare function createCollisionLayer<L extends string>(layer: L, collidesWith: readonly L[]): Pick<CollisionComponentTypes<L>, 'collisionLayer'>;
/**
 * Layer factory result from defineCollisionLayers.
 */
export type LayerFactories<T extends Record<string, readonly string[]>> = {
    [K in keyof T]: () => Pick<CollisionComponentTypes<Extract<keyof T, string>>, 'collisionLayer'>;
};
/**
 * Extract layer names from a `defineCollisionLayers` result for use with
 * `createCollisionPairHandler`'s `L` type parameter.
 *
 * @example
 * ```typescript
 * const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });
 * type Layer = LayersOf<typeof layers>;
 * const handler = createCollisionPairHandler<ECS, Layer>({
 *   'player:enemy': (playerId, enemyId, ecs) => { ... },
 * });
 * ```
 */
export type LayersOf<T> = Extract<keyof T, string>;
/**
 * Define collision layer relationships and get factory functions.
 *
 * @param rules Object mapping layer names to arrays of layers they collide with
 * @returns Object with factory functions for each layer
 *
 * @example
 * ```typescript
 * const layers = defineCollisionLayers({
 *   player: ['enemy', 'enemyProjectile'],
 *   playerProjectile: ['enemy'],
 *   enemy: ['playerProjectile'],
 *   enemyProjectile: ['player'],
 * });
 *
 * // Usage
 * ecs.spawn({
 *   ...createTransform(100, 200),
 *   ...createAABBCollider(50, 30),
 *   ...layers.player(),
 * });
 * ```
 */
/**
 * Validates that all `collidesWith` values reference actual layer keys.
 * Catches typos at compile time.
 */
type ValidateCollidesWith<T> = {
    [K in keyof T]: T[K] extends readonly (infer V)[] ? [V] extends [Extract<keyof T, string>] ? T[K] : readonly Extract<keyof T, string>[] : never;
};
export declare function defineCollisionLayers<const T extends Record<string, readonly string[]>>(rules: T & ValidateCollidesWith<T>): LayerFactories<T>;
/**
 * Callback for a collision pair handler.
 *
 * @param firstEntityId Entity belonging to the first layer in the pair key
 * @param secondEntityId Entity belonging to the second layer in the pair key
 * @param ecs The ECS world instance (passed through from the subscriber)
 */
export type CollisionPairCallback<W = unknown> = (firstEntityId: number, secondEntityId: number, ecs: W) => void;
/**
 * Create a collision pair handler that routes collision events to
 * layer-pair-specific callbacks.
 *
 * Registering `"a:b"` automatically handles both `(layerA=a, layerB=b)` and
 * `(layerA=b, layerB=a)`. Entity arguments are swapped to match the declared
 * key order. If both `"a:b"` and `"b:a"` are explicitly registered, each gets
 * its own handler with no implicit reverse.
 *
 * @typeParam W - The ECS world type (e.g. `ECSpresso<C, E, R>`). Defaults to `unknown`.
 * @typeParam L - Union of valid layer names. Defaults to `string`.
 *   Provide specific layer names for compile-time key validation:
 *   `createCollisionPairHandler<ECS, keyof typeof layers>({...})`
 *
 * @param pairs Object mapping `"layerA:layerB"` keys to callbacks
 * @returns A dispatch function to call with collision event data and ECS instance
 *
 * @example
 * ```typescript
 * // Basic usage:
 * const handler = createCollisionPairHandler<ECS>({
 *   'playerProjectile:enemy': (projectileId, enemyId, ecs) => {
 *     ecs.commands.removeEntity(projectileId);
 *   },
 * });
 *
 * // With layer name validation:
 * const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });
 * type Layer = LayersOf<typeof layers>;
 * const handler = createCollisionPairHandler<ECS, Layer>({
 *   'player:enemy': (playerId, enemyId, ecs) => { ... },
 * });
 *
 * ecs.eventBus.subscribe('collision', (data) => handler({ data, ecs }));
 * ```
 */
export declare function createCollisionPairHandler<W = unknown, L extends string = string>(pairs: {
    [K in `${L}:${L}`]?: CollisionPairCallback<W>;
}): (ctx: {
    data: CollisionEvent<L>;
    ecs: W;
}) => void;
/**
 * Create a collision plugin for ECSpresso.
 *
 * This plugin provides:
 * - Collision detection between entities with colliders
 * - AABB-AABB, circle-circle, and AABB-circle collision
 * - Layer-based filtering for collision pairs
 * - Deduplication of A-B / B-A collisions
 * - Automatic broadphase acceleration when spatialIndex resource is present
 *
 * Uses worldTransform for position (world-space collision detection).
 * The `layers` parameter is required for type inference — at runtime the
 * plugin does not consume it.
 *
 * @example
 * ```typescript
 * const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });
 * const ecs = ECSpresso
 *   .create()
 *   .withPlugin(createTransformPlugin())
 *   .withPlugin(createCollisionPlugin({ layers }))
 *   .build();
 *
 * // Entity with collision
 * ecs.spawn({
 *   ...createTransform(100, 200),
 *   ...createAABBCollider(50, 30),
 *   ...layers.player(),
 * });
 * ```
 */
export declare function createCollisionPlugin<L extends string, G extends string = 'physics'>(options: CollisionPluginOptions<G> & {
    layers: LayerFactories<Record<L, readonly string[]>>;
}): import("ecspresso").Plugin<import("ecspresso").WithEvents<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, CollisionComponentTypes<L>>, CollisionEventTypes<L>>, TransformWorldConfig, "collision-detection", G, never, never>;
export {};
