import { type AnyObject, type ExtractKeysWithMatchingValues, type PartialWithUndefined, type Values } from '@augment-vir/common';
import { System, type Response as Collision, type Body as Hitbox } from 'detect-collisions';
import { type ShapeDefinition } from 'object-shape-tester';
import { type Application, type ViewContainer } from 'pixi.js';
import { type AbstractConstructor, type Constructor, type IsEqual, type IsNever, type WritableKeysOf } from 'type-fest';
import { ListenTarget } from 'typed-event-target';
import { ConstructorMap } from '../constructor-map.js';
/**
 * Parameters for {@link EntityStore.addEntity}. Flattens itself to an empty array if there are no
 * entity constructor params.
 *
 * @category Internal
 */
export type AddEntityParams<EntityConstructor extends Constructor<BaseEntity>> = ConstructorParameters<EntityConstructor>[0] extends infer Args extends Pick<EntityConstructorParams<any, any>, 'params'> ? Args['params'] extends undefined ? [] : [Args['params']] : ['ERROR: invalid entity constructor'];
/**
 * Parameters for the constructor of {@link EntityStore}.
 *
 * @category Internal
 */
export type EntityStoreConstructorParams<Context, RegisteredEntities extends Values<EntityStore['entityKeyConstructorMap']>> = (IsNever<Extract<Context, undefined | null>> extends true ? {
    context: Context;
} : {
    context?: Context;
}) & {
    /**
     * An [`Application`](https://pixijs.download/release/docs/app.Application.html) instance from
     * the [`pixi.js`](https://www.npmjs.com/package/pixi.js) package.
     */
    pixi: Application;
    /**
     * A `System` instance from the
     * [`detect-collisions`](https://www.npmjs.com/package/detect-collisions) package. If this
     * property is omitted or `undefined`, the {@link EntityStore} instance will create its own.
     */
    hitboxSystem?: System | undefined;
    /**
     * An array of all entity constructors that will be registered with this {@link EntityStore}
     * instance. If an entity is added to the store without it being registered here, adding it will
     * fail.
     */
    registeredEntities: ReadonlyArray<RegisteredEntities>;
};
/**
 * Extract all event types from all given entity constructors (in a union).
 *
 * @category Internal
 */
export type ExtractEntityEvents<Entity extends Constructor<BaseEntity>> = InstanceType<Entity> extends BaseEntity<any, any, infer Events> ? Events : never;
/**
 * The top level storage class of all entities. Add entities with {@link EntityStore.addEntity}.
 *
 * @category Internal
 */
export declare class EntityStore<Context = undefined, const RegisteredEntities extends Values<EntityStore['entityKeyConstructorMap']> = any> {
    /**
     * All current child entities.
     *
     * Instead of modifying this set, use {@link EntityStore.addEntity} or
     * {@link EntityStore.removeEntity}. If you must manually modify this set directly, you'll also
     * need to modify {@link EntityStore.entityInstanceMap}.
     */
    readonly currentEntityInstances: Set<BaseEntity<any, any, never>>;
    /** If true, this entity store should no longer be used or operated upon. */
    readonly isDestroyed: boolean;
    /** An internal mapping of all entity constructors to their current instances. */
    readonly entityInstanceMap: ConstructorMap;
    /** Original pixi app. */
    readonly pixi: Application;
    /** Context given to all entities. This can be undefined. */
    readonly context: Context;
    /** Collision detection system. */
    readonly hitboxSystem: System;
    /** A map of all entity keys to their registered Entity constructors. */
    readonly entityKeyConstructorMap: Record<string, Constructor<BaseEntity> & Pick<typeof BaseEntity, 'deserialize' | 'entityKey'>>;
    /** Events emitted from any child entities. */
    events: ListenTarget<ExtractEntityEvents<RegisteredEntities> | EntityDestroyEvent>;
    constructor(args: Readonly<EntityStoreConstructorParams<Context, RegisteredEntities>>);
    /**
     * Runs `.update()` on all current entities and runs collision detection for all hitboxes. If
     * any entities get marked as destroyed during their update, then they will be removed from the
     * set of entities.
     *
     * @returns All detected hitbox collisions (if any).
     */
    updateAllEntities(): void;
    /** Get all current instances of the given entity class constructor. */
    getEntities<T>(entityClassConstructor: AbstractConstructor<T> | Constructor<T>): Set<T>;
    /** Remove an entity from the store. */
    removeEntity(entity: BaseEntity): void;
    /**
     * Create an entity instance by finding the registered constructor with the given `entityKey`
     * and then deserializing and passing the given `serializedParams` to that constructor.
     */
    deserializeEntity<const EntityKey extends RegisteredEntities['entityKey']>(entityKey: EntityKey, serializedParams: string | undefined): InstanceType<Extract<RegisteredEntities, {
        entityKey: EntityKey;
    }>>;
    /** Create a new instance of the given entity class and add it to this entity store. */
    addEntity<const EntityConstructor extends RegisteredEntities>(entityClass: EntityConstructor, ...params: AddEntityParams<EntityConstructor>): InstanceType<EntityConstructor>;
    /** Destroys the entity store and all entities contained inside it. */
    destroy(): void;
}
/**
 * Shape definition for {@link EntityPositionParams}.
 *
 * @category Util
 */
export declare const entityPositionParamsShape: ShapeDefinition<{
    x: number;
    y: number;
}, false>;
/**
 * Base entity serialization. All entities should at least include these properties.
 *
 * @category Internal
 */
export type EntityPositionParams = typeof entityPositionParamsShape.runtimeType;
/**
 * Parameters for an entity's constructor.
 *
 * @category Internal
 */
export type EntityConstructorParams<Params extends Record<string, any> | undefined = undefined, Context = undefined> = (IsNever<Extract<Context, undefined | null>> extends true ? {
    context: Context;
} : {
    context?: Context;
}) & (IsNever<Extract<Params, undefined | null>> extends true ? {
    params: Params;
} : {
    params?: Params;
}) & {
    paramsMap?: ParamsMap<NoInfer<Params>> | undefined;
    entityStore: EntityStore<Context>;
    pixi: Application;
    hitboxSystem: System;
};
/**
 * Finds all keys from `Params` that match the value at the given `Key` in `OriginalObject`.
 *
 * @category Internal
 */
export type MatchingKeys<Key extends PropertyKey, Params extends Record<string, any> | undefined, OriginalObject extends AnyObject> = Params extends Record<string, any> ? ExtractKeysWithMatchingValues<Params, Extract<OriginalObject, Record<Key, any>>[Key]> : never;
/**
 * An object that controls which entity parameter projects get mapped to view and hitbox properties.
 * Values can be:
 *
 * - `true`: indicates that the property is mapped directly from params to that view or hitbox object
 * - Omitted: the property is not mapped at all
 * - A string: specifies the params key that this view or hitbox property is mapped to
 */
export type ParamsMap<Params extends Record<string, any> | undefined = AnyObject> = IsEqual<Params, undefined> extends true ? undefined : PartialWithUndefined<{
    view: Partial<{
        [Key in WritableKeysOf<ViewContainer> as IsNever<MatchingKeys<Key, Params, ViewContainer>> extends true ? never : Key]: (Key extends keyof Params ? Params[Key] extends ViewContainer[Key] ? true : never : never) | MatchingKeys<Key, Params, ViewContainer>;
    }>;
    hitbox: Partial<{
        [Key in WritableKeysOf<Hitbox> as IsNever<MatchingKeys<Key, Params, Hitbox>> extends true ? never : Key]: (Key extends keyof Params ? Params[Key] extends Extract<Hitbox, Record<Key, any>>[Key] ? true : never : never) | MatchingKeys<Key, Params, Hitbox>;
    }>;
}>;
declare const EntityDestroyEvent_base: (new (eventInitDict?: EventInit) => import("typed-event-target").TypedEvent<"entity-destroy-event">) & Pick<{
    new (type: string, eventInitDict?: EventInit): Event;
    prototype: Event;
    readonly NONE: 0;
    readonly CAPTURING_PHASE: 1;
    readonly AT_TARGET: 2;
    readonly BUBBLING_PHASE: 3;
}, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedEvent<"entity-destroy-event">, "type">;
/**
 * Event emitted by all entities when they are destroyed.
 *
 * @category Internal
 */
export declare class EntityDestroyEvent extends EntityDestroyEvent_base {
}
/**
 * Default value for the optional {@link ParamsMap}.
 *
 * @category Internal
 */
export declare const defaultParamsMap: ParamsMap;
/**
 * Type for {@link BaseEntity.reverseParamsMap}.
 *
 * @category Internal
 */
export type ReverseParamsMap = Record<string, Partial<Record<'hitbox' | 'view', string[]>>>;
/**
 * Base entity class, types, and functionality.
 *
 * @category Internal
 */
export declare abstract class BaseEntity<Context = any, Params extends Record<string, any> | undefined = any, Events extends Readonly<Event> = never> {
    /**
     * This key is used for deserialization of entities to track which class needs to be
     * constructed. You cannot have duplicate keys loaded at the same time.
     */
    static readonly entityKey: string;
    /** Shape definition of this entity's parameters. */
    static readonly paramsShape: ShapeDefinition<AnyObject, any> | undefined;
    /**
     * Defines which properties from {@link BaseEntity.params} will be mapped to hitbox and/or view
     * properties.
     */
    static readonly paramsMap: ParamsMap | undefined;
    /**
     * A mapping from params properties to hitbox or view properties, making it easy to map params
     * values.
     */
    static readonly reverseParamsMap: ReverseParamsMap | undefined;
    /** Parses the serialized params generated by {@link BaseEntity.serialize}. */
    static deserialize(serialized: string | undefined): AnyObject | undefined;
    /**
     * Dispatch an event. This will be dispatched through this entity's entity store (so listen for
     * the event on the store).
     */
    dispatch(event: Events | EntityDestroyEvent): void;
    /** If true, this entity should no longer be used or operated upon. */
    readonly isDestroyed: boolean;
    hitbox: Hitbox<this> | undefined;
    /** The entity store to add all entities to. */
    readonly entityStore: EntityStore<Context>;
    readonly context: Context;
    /** Writable entity params. These should be serializable. */
    readonly params: Params;
    /** Original pixi app. */
    readonly pixi: Application;
    /** Collision detection system. */
    readonly hitboxSystem: System;
    constructor(args: Readonly<EntityConstructorParams<NoInfer<Params>, NoInfer<Context>>>);
    /**
     * Called every game tick. Run all entity updates in here. This should be overridden in all
     * entity definition classes.
     */
    abstract update(): void;
    /** Add a new entity to the entity store. */
    addEntity<const EntityConstructor extends Constructor<BaseEntity>>(entityClass: EntityConstructor, ...params: AddEntityParams<EntityConstructor>): InstanceType<EntityConstructor>;
    /** Marks the entity for destruction in the next entity store update. */
    destroy(): void;
    /**
     * Immediately destroy the current entity, stop its updates.
     *
     * This is probably not what you want to use! See {@link BaseEntity.destroy} instead.
     */
    immediatelyDestroy(): void;
    /**
     * This method is call whenever this entity's hitbox (if it has one) collides with another
     * hitbox. It will be called for each individual collision. Override this to do something about
     * it.
     */
    collide(otherEntity: BaseEntity, collision: Readonly<Collision>): void;
    /**
     * Serialize the entity params for sharing across the network (for multiplayer play). By default
     * this simply calls `JSON.stringify` on `this.params`. This method must be overridden if your
     * entity has params that are not JSON compatible.
     */
    serialize(): string | undefined;
}
/**
 * Output of {@link ViewEntity.createView}.
 *
 * @category Internal
 */
export type ViewCreation = {
    /**
     * A view for rendering. Create with, for example, [`new
     * AnimatedSprite`](https://pixijs.download/release/docs/scene.AnimatedSprite.html) or [`new
     * Graphics`](https://pixijs.download/release/docs/scene.Graphics.html), etc. imported from the
     * [`pixi.js`](https://www.npmjs.com/package/pixi.js) package.
     */
    view?: ViewContainer | undefined;
    /**
     * A Body instance for hitbox collision detection. Create one with, for example,
     * `this.hitboxSystem.createBox()` or import directly from the
     * [`detect-collisions`](https://www.npmjs.com/package/detect-collisions) package, like with
     * [`new Circle`](https://prozi.github.io/detect-collisions/classes/Circle.html#constructor).
     *
     * This property optional, if a hitbox is not provided, collision detection will not be
     * calculated for this entity.
     */
    hitbox?: Hitbox | undefined;
};
/**
 * Base view entity class, types, and functionality.
 *
 * @category Internal
 */
export declare abstract class ViewEntity<Context = any, Params extends Record<string, any> | undefined = any, Events extends Readonly<Event> = never> extends BaseEntity<Context, Params, Events> {
    /** The entity's PixiJS view. */
    view: ViewContainer;
    constructor(args: Readonly<EntityConstructorParams<NoInfer<Params>, NoInfer<Context>>>);
    private wrapParamsInProxy;
    /**
     * Creates the entity's PixiJS view. This will be called on entity construction and added to the
     * PixiJS application stage.
     */
    abstract createView(): ViewCreation;
    /** Detects if the current entity is still within the bounds of the render canvas. */
    isInBounds(options?: PartialWithUndefined<{
        /**
         * If `true`, the entire entity's bounds must be within the canvas's bounds. If `false`,
         * any portion of the entity being within the canvas bounds is counted.
         *
         * @default false
         */
        entirely?: boolean;
    }>): boolean;
    /**
     * Immediately destroy the current entity, stop its updates, and remove it from the view.
     *
     * This is probably not what you want to use! See {@link BaseEntity.destroy} instead.
     */
    immediatelyDestroy(): void;
}
export {};
