import { Intersection, Ray, Vector2 } from 'three';
import { Context } from './engine_setup.js';
import type { ButtonName, CursorTypeName, IGameObject, IInput, Vec2 } from './engine_types.js';
import { type EnumToPrimitiveUnion } from './engine_utils.js';
/**
 * Types of pointer input devices supported by Needle Engine.
 */
export declare const enum PointerType {
    /** Mouse or trackpad input */
    Mouse = "mouse",
    /** Touch screen input */
    Touch = "touch",
    /** XR controller input (e.g., VR controllers) */
    Controller = "controller",
    /** XR hand tracking input */
    Hand = "hand"
}
export type PointerTypeNames = EnumToPrimitiveUnion<PointerType>;
declare const enum PointerEnumType {
    PointerDown = "pointerdown",
    PointerUp = "pointerup",
    PointerMove = "pointermove"
}
declare const enum KeyboardEnumType {
    KeyDown = "keydown",
    KeyUp = "keyup",
    KeyPressed = "keypress"
}
/**
 * Event types that can be listened to via {@link Input.addEventListener}.
 * @see {@link NEPointerEvent} for pointer event data
 * @see {@link NEKeyboardEvent} for keyboard event data
 */
export declare const enum InputEvents {
    /** Fired when a pointer button is pressed */
    PointerDown = "pointerdown",
    /** Fired when a pointer button is released */
    PointerUp = "pointerup",
    /** Fired when a pointer moves */
    PointerMove = "pointermove",
    /** Fired when a key is pressed down */
    KeyDown = "keydown",
    /** Fired when a key is released */
    KeyUp = "keyup",
    /** Fired when a key produces a character value */
    KeyPressed = "keypress"
}
/** e.g. `pointerdown` */
type PointerEventNames = EnumToPrimitiveUnion<PointerEnumType>;
type KeyboardEventNames = EnumToPrimitiveUnion<KeyboardEnumType>;
export type InputEventNames = PointerEventNames | KeyboardEventNames;
declare type PointerEventListener = (evt: NEPointerEvent) => void;
declare type KeyboardEventListener = (evt: NEKeyboardEvent) => void;
export declare type NEPointerEventInit = PointerEventInit & {
    clientZ?: number;
    origin: object;
    pointerId: number;
    /** the index of the device */
    deviceIndex: number;
    pointerType: PointerTypeNames;
    mode: XRTargetRayMode;
    ray?: Ray;
    /** The control object for this input. In the case of spatial devices the controller,
     * otherwise a generated object in screen space. The object may not be in the scene. */
    device: IGameObject;
    buttonName: ButtonName | "none";
};
declare type OnPointerHitsEvent = (args: OnPointerHitEvent) => void;
declare type OnPointerHitEvent = {
    /** The object that raised the event */
    sender: object;
    /** The pointer event that invoked the event */
    event: NEPointerEvent;
    /** The intersections that were generated from this event (or are associated with this event in any way) */
    hits: Intersection[];
};
export interface IPointerHitEventReceiver {
    onPointerHits: OnPointerHitsEvent;
}
/** An intersection that is potentially associated with a pointer event */
export declare type NEPointerEventIntersection = Intersection & {
    event?: NEPointerEvent;
};
/**
 * Extended PointerEvent with Needle Engine-specific data.
 * Contains information about the input device, spatial data for XR, and world-space ray.
 *
 * @example Accessing event data in a component
 * ```ts
 * onPointerDown(args: PointerEventData) {
 *   const evt = args.event;
 *   console.log(`Pointer ${evt.pointerId} (${evt.pointerType})`);
 *   if (evt.isSpatial) {
 *     console.log("XR input, ray:", evt.ray);
 *   }
 * }
 * ```
 *
 * @see {@link Input} for the input management system
 * @see {@link PointerType} for available pointer types
 */
export declare class NEPointerEvent extends PointerEvent {
    /**
     * Spatial input data
     */
    clientZ?: number;
    /** the device index: mouse and touch are always 0, otherwise e.g. index of the connected Gamepad or XRController */
    readonly deviceIndex: number;
    /** The origin of the event contains a reference to the creator of this event.
     * This can be the Needle Engine input system or e.g. a XR controller.
     * Implement `onPointerHits` to receive the intersections of this event.
     */
    readonly origin: object & Partial<IPointerHitEventReceiver>;
    /** the browser event that triggered this event (if any) */
    readonly source: Event | null;
    /** Is the pointer event created via a touch on screen or a spatial device like a XR controller or hand tracking? */
    readonly mode: XRTargetRayMode | "transient-pointer";
    /** Returns true if the input was emitted in 3D space (and not by e.g. clicking on a 2D screen). You can use {@link mode} if you need more information about the input source */
    get isSpatial(): boolean;
    /** A ray in worldspace for the event.
     * If the ray is undefined you can also use `space.worldForward` and `space.worldPosition` */
    get ray(): Ray;
    private set ray(value);
    /**@returns true if this event has a ray. If you access the ray property a ray will automatically created */
    get hasRay(): boolean;
    private _ray;
    /** The device space (this object is not necessarily rendered in the scene but you can access or copy the matrix)
     * E.g. you can access the input world space source position with `space.worldPosition` or world direction with `space.worldForward`
    */
    readonly space: IGameObject;
    /** true if this event is a click */
    isClick: boolean;
    /** true if this event is a double click */
    isDoubleClick: boolean;
    /** @returns `true` if the event is marked to be used (when `use()` has been called). Default: `false` */
    get used(): boolean;
    private _used;
    /** Call to mark an event to be used */
    use(): void;
    /** Identifier for this pointer event.
     * For mouse and touch this is always 0.
     * For XR input: a combination of the deviceIndex + button to uniquely identify the exact input (e.g. LeftController:Button0 = 0, RightController:Button1 = 11)
     */
    get pointerId(): number;
    private readonly _pointerid;
    /** What type of input created this event: touch, mouse, xr controller, xr hand tracking... */
    get pointerType(): PointerTypeNames;
    private readonly _pointerType;
    /**
     * The button name that raised this event (e.g. for mouse events "left", "right", "middle" or for XRTrigger "xr-standard-trigger" or "xr-standard-thumbstick")
     * Use {@link button} to get the numeric button index (e.g. 0, 1, 2...) on the controller or mouse.
     */
    readonly buttonName?: ButtonName | "none";
    /** The input that raised this event like `pointerdown` */
    get type(): InputEventNames;
    private readonly _type;
    /** metadata can be used to associate additional information with the event */
    readonly metadata: {};
    /** intersections that were generated from this event (or are associated with this event in any way) */
    readonly intersections: NEPointerEventIntersection[];
    constructor(type: InputEvents | InputEventNames, source: Event | null, init: NEPointerEventInit);
    private _immediatePropagationStopped;
    get immediatePropagationStopped(): boolean;
    private _propagationStopped;
    get propagationStopped(): boolean;
    stopImmediatePropagation(): void;
    stopPropagation(): void;
}
export declare class NEKeyboardEvent extends KeyboardEvent {
    source?: Event;
    constructor(type: InputEvents, source: Event, init: KeyboardEventInit);
    stopImmediatePropagation(): void;
}
export declare class KeyEventArgs {
    key: string;
    keyType: string;
    source?: Event;
    constructor(evt: KeyboardEvent);
}
export declare enum InputEventQueue {
    Early = -100,
    Default = 0,
    Late = 100
}
declare type EventListenerOptions = {
    /** For addEventListener: The queue to add the listener to. Listeners in the same queue are called in the order they were added. Default is 0.
     * For removeEventListener: The queue to remove the listener from. If no queue is specified the listener will be removed from all queues
     */
    queue?: InputEventQueue | number;
    /** If true, the listener will be removed after it is invoked once. */
    once?: boolean;
    /** The listener will be removed when the given AbortSignal object's `abort()` method is called. If not specified, no AbortSignal is associated with the listener. */
    signal?: AbortSignal;
};
/**
 * Handles all input events including mouse, touch, keyboard, and XR controllers.
 * Access via `this.context.input` from any component.
 *
 * @example Checking mouse/pointer state
 * ```ts
 * update() {
 *   if (this.context.input.mouseDown) {
 *     console.log("Mouse button pressed");
 *   }
 *   if (this.context.input.mouseClick) {
 *     console.log("Click detected");
 *   }
 *   const pos = this.context.input.mousePosition;
 *   console.log(`Mouse at: ${pos.x}, ${pos.y}`);
 * }
 * ```
 * @example Keyboard input
 * ```ts
 * update() {
 *   if (this.context.input.isKeyDown("Space")) {
 *     console.log("Space pressed this frame");
 *   }
 *   if (this.context.input.isKeyPressed("w")) {
 *     console.log("W key is held down");
 *   }
 * }
 * ```
 * @example Event-based input
 * ```ts
 * onEnable() {
 *   this.context.input.addEventListener("pointerdown", this.onPointerDown);
 * }
 * onDisable() {
 *   this.context.input.removeEventListener("pointerdown", this.onPointerDown);
 * }
 * onPointerDown = (evt: NEPointerEvent) => {
 *   console.log("Pointer down:", evt.pointerId);
 * }
 * ```
 *
 * @see {@link NEPointerEvent} for pointer event data
 * @see {@link InputEvents} for available event types
 * @see {@link PointerType} for pointer device types
 * @link https://engine.needle.tools/docs/scripting.html
 */
export declare class Input implements IInput {
    /** This is a list of event listeners per event type (e.g. pointerdown, pointerup, keydown...). Each entry contains a priority and list of listeners.
     * That way users can control if they want to receive events before or after other listeners (e.g subscribe to pointer events before the EventSystem receives them) - this allows certain listeners to be always invoked first (or last) and stop propagation
     * Listeners per event are sorted
     */
    private readonly _eventListeners;
    /** Adds an event listener for the specified event type. The callback will be called when the event is triggered.
     * @param type The event type to listen for
     * @param callback The callback to call when the event is triggered
     * @param options The options for adding the event listener.
     * @example Basic usage
     * ```ts
     * input.addEventListener("pointerdown", (evt) => {
     *   console.log("Pointer down", evt.pointerId, evt.pointerType);
     * });
     * ```
     * @example Adding a listener that is called after all other listeners
     * By using a higher value for the queue the listener will be called after other listeners (default queue is 0).
     * ```ts
     * input.addEventListener("pointerdown", (evt) => {
     *  console.log("Pointer down", evt.pointerId, evt.pointerType);
     * }, { queue: 10 });
     * ```
     * @example Adding a listener that is only called once
     * ```ts
     * input.addEventListener("pointerdown", (evt) => {
     *   console.log("Pointer down", evt.pointerId, evt.pointerType);
     * }, { once: true });
     * ```
     */
    addEventListener(type: PointerEventNames, callback: PointerEventListener, options?: EventListenerOptions): any;
    addEventListener(type: KeyboardEventNames, callback: KeyboardEventListener, options?: EventListenerOptions): any;
    /** Removes the event listener from the specified event type. If no queue is specified the listener will be removed from all queues.
     * @param type The event type to remove the listener from
     * @param callback The callback to remove
     * @param options The options for removing the event listener
     */
    removeEventListener(type: PointerEventNames, callback: PointerEventListener, options?: EventListenerOptions): any;
    removeEventListener(type: KeyboardEventNames, callback: KeyboardEventListener, options?: EventListenerOptions): any;
    private dispatchEvent;
    _doubleClickTimeThreshold: number;
    _longPressTimeThreshold: number;
    get mousePosition(): Vector2;
    get mousePositionRC(): Vector2;
    get mouseDown(): boolean;
    get mouseUp(): boolean;
    /** Is the primary pointer clicked (usually the left button). This is equivalent to `input.click` */
    get mouseClick(): boolean;
    /** Was a double click detected for the primary pointer? This is equivalent to `input.doubleClick` */
    get mouseDoubleClick(): boolean;
    get mousePressed(): boolean;
    get mouseWheelChanged(): boolean;
    /** Is the primary pointer double clicked (usually the left button). This is equivalent to `input.mouseDoubleClick` */
    get click(): boolean;
    /** Was a double click detected for the primary pointer? */
    get doubleClick(): boolean;
    /**
     * Get a connected Gamepad
     * Note: For a gamepad to be available to the browser it must have received input before while the page was focused.
     * @link https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
     * @returns The gamepad or null if no gamepad is connected
     */
    getGamepad(index?: number): Gamepad | null;
    private readonly _setCursorTypes;
    /** @deprecated use setCursor("pointer") */
    setCursorPointer(): void;
    /** @deprecated use unsetCursor() */
    setCursorNormal(): void;
    /**
     * Set a custom cursor. This will set the cursor type until unsetCursor is called
     */
    setCursor(type: CursorTypeName): void;
    /**
     * Unset a custom cursor. This will set the cursor type to the previous type or default
     */
    unsetCursor(type: CursorTypeName): void;
    private updateCursor;
    /**
     * Check if a pointer id is currently used.
     */
    getIsPointerIdInUse(pointerId: number): boolean;
    /** how many pointers are currently pressed */
    getPointerPressedCount(): number;
    /**
     * Gets the position of the given pointer index in pixel
     * @param i The pointer index
     * @returns The position of the pointer in pixel
     */
    getPointerPosition(i: number): Vector2 | null;
    getPointerPositionLastFrame(i: number): Vector2 | null;
    getPointerPositionDelta(i: number): Vector2 | null;
    /**
     * The pointer position in screenspace coordinates (-1 to 1) where 0 is the center of the screen.
     * This can be useful for e.g. raycasting (see https://threejs.org/docs/#api/en/core/Raycaster.setFromCamera)
     */
    getPointerPositionRC(i: number): Vector2 | null;
    getPointerDown(i: number): boolean;
    getPointerUp(i: number): boolean;
    getPointerPressed(i: number): boolean;
    getPointerClicked(i: number): boolean;
    getPointerDoubleClicked(i: number): boolean;
    getPointerDownTime(i: number): number;
    getPointerUpTime(i: number): number;
    getPointerLongPress(i: number): boolean;
    getIsMouse(i: number): boolean;
    getIsTouch(i: number): boolean;
    getTouchesPressedCount(): number;
    getMouseWheelChanged(i?: number): boolean;
    getMouseWheelDeltaY(i?: number): number;
    getPointerEvent(i: number): Event | undefined;
    foreachPointerId(pointerType?: string | PointerType | string[] | PointerType[]): Generator<number>;
    foreachTouchId(): Generator<number>;
    private _pointerIsActive;
    private context;
    private _pointerDown;
    private _pointerUp;
    private _pointerClick;
    private _pointerDoubleClick;
    private _pointerPressed;
    private _pointerPositions;
    private _pointerPositionsLastFrame;
    private _pointerPositionsDelta;
    private _pointerPositionsRC;
    private _pointerPositionDown;
    private _pointerDownTime;
    private _pointerUpTime;
    private _pointerUpTimestamp;
    private _pointerIds;
    private _pointerTypes;
    private _mouseWheelChanged;
    private _mouseWheelDeltaY;
    private _pointerEvent;
    /** current pressed pointer events. Used to check if any of those events was used  */
    private _pointerEventsPressed;
    /** This is added/updated for pointers. screenspace pointers set this to the camera near plane  */
    private _pointerSpace;
    private readonly _pressedStack;
    private onDownButton;
    private onReleaseButton;
    /** the first button that was down and is currently pressed */
    getFirstPressedButtonForPointer(pointerId: number): number | undefined;
    /** the last (most recent) button that was down and is currently pressed */
    getLatestPressedButtonForPointer(pointerId: number): number | undefined;
    /** Get a key (if any) that was just pressed this frame (this is only true for the frame it was pressed down) */
    getKeyDown(): string | null;
    /** Get true or false if the given key was pressed this frame */
    getKeyDown(key: KeyCode | ({} & string)): boolean;
    /** Get a key (if any) that is currently being pressed (held down) */
    getKeyPressed(): string | null;
    /** Get true or false if the given key is pressed */
    getKeyPressed(key: KeyCode | ({} & string)): boolean;
    /** Get a key (if any) that was released in this frame */
    getKeyUp(): string | null;
    /** Get true or false if the given key was released this frame */
    getKeyUp(key: KeyCode | ({} & string)): boolean;
    isKeyDown(keyCode: KeyCode | ({} & string)): boolean;
    isKeyUp(keyCode: KeyCode | ({} & string)): boolean;
    isKeyPressed(keyCode: KeyCode | ({} & string)): boolean;
    private getCodeForCommonKeyName;
    createInputEvent(args: NEPointerEvent): void;
    convertScreenspaceToRaycastSpace<T extends Vec2 | Vector2>(vec2: T): T;
    /** @internal */
    constructor(context: Context);
    /** this is the html element we subscribed to for events */
    private _htmlEventSource;
    bindEvents(): void;
    unbindEvents(): void;
    dispose(): void;
    private onLostFocus;
    private readonly _receivedPointerMoveEventsThisFrame;
    private onEndOfFrame;
    private canReceiveInput;
    private onContextMenu;
    private keysPressed;
    private onKeyDown;
    private onKeyPressed;
    private onKeyUp;
    private onWheelWindow;
    private onMouseWheel;
    private onPointerDown;
    private onPointerMove;
    private onPointerCancel;
    private onPointerUp;
    private getPointerId;
    private getButtonName;
    private onTouchStart;
    private onTouchMove;
    private onTouchEnd;
    private readonly tempNearPlaneVector;
    private readonly tempFarPlaneVector;
    private readonly tempLookMatrix;
    private getAndUpdateSpatialObjectForScreenPosition;
    private isInRect;
    private onDown;
    private onMove;
    private onUp;
    private updatePointerPosition;
    /** get the next free id */
    private getPointerIndex;
    private setPointerState;
    private setPointerStateT;
    private onDispatchEvent;
}
export declare type KeyCode = "Tab" | "Enter" | "ShiftLeft" | "ShiftRight" | "ControlLeft" | "ControlRight" | "AltLeft" | "AltRight" | "Pause" | "CapsLock" | "Escape" | "Space" | "PageUp" | "PageDown" | "End" | "Home" | "ArrowLeft" | "ArrowUp" | "ArrowRight" | "ArrowDown" | "Insert" | "Delete" | "Digit0" | "Digit1" | "Digit2" | "Digit3" | "Digit4" | "Digit5" | "Digit6" | "Digit7" | "Digit8" | "Digit9" | "KeyA" | "KeyB" | "KeyC" | "KeyD" | "KeyE" | "KeyF" | "KeyG" | "KeyH" | "KeyI" | "KeyJ" | "KeyK" | "KeyL" | "KeyM" | "KeyN" | "KeyO" | "KeyP" | "KeyQ" | "KeyR" | "KeyS" | "KeyT" | "KeyU" | "KeyV" | "KeyW" | "KeyX" | "KeyY" | "KeyZ" | "Select" | "Numpad0" | "Numpad1" | "Numpad2" | "Numpad3" | "Numpad4" | "Numpad5" | "Numpad6" | "Numpad7" | "Numpad8" | "Numpad9" | "Multiply" | "Add" | "Subtract" | "Decimal" | "Divide" | "F1" | "F2" | "F3" | "F4" | "F5" | "F6" | "F7" | "F8" | "F9" | "F10" | "F11" | "F12";
export {};
