/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

'use strict';

type ClickEvent = any;
type KeyboardEvent = any;
type ResponderEvent = any;
export type PressResponderConfig = $ReadOnly<{|
  // The gesture can be interrupted by a parent gesture, e.g., scroll.
  // Defaults to true.
  cancelable?: ?boolean,
  // Whether to disable initialization of the press gesture.
  disabled?: ?boolean,
  // Duration (in addition to `delayPressStart`) after which a press gesture is
  // considered a long press gesture. Defaults to 500 (milliseconds).
  delayLongPress?: ?number,
  // Duration to wait after press down before calling `onPressStart`.
  delayPressStart?: ?number,
  // Duration to wait after letting up before calling `onPressEnd`.
  delayPressEnd?: ?number,
  // Called when a long press gesture has been triggered.
  onLongPress?: ?(event: ResponderEvent) => void,
  // Called when a press gestute has been triggered.
  onPress?: ?(event: ClickEvent) => void,
  // Called when the press is activated to provide visual feedback.
  onPressChange?: ?(event: ResponderEvent) => void,
  // Called when the press is activated to provide visual feedback.
  onPressStart?: ?(event: ResponderEvent) => void,
  // Called when the press location moves. (This should rarely be used.)
  onPressMove?: ?(event: ResponderEvent) => void,
  // Called when the press is deactivated to undo visual feedback.
  onPressEnd?: ?(event: ResponderEvent) => void,
|}>;
export type EventHandlers = $ReadOnly<{|
  onClick: (event: ClickEvent) => void,
  onContextMenu: (event: ClickEvent) => void,
  onKeyDown: (event: KeyboardEvent) => void,
  onResponderGrant: (event: ResponderEvent) => void,
  onResponderMove: (event: ResponderEvent) => void,
  onResponderRelease: (event: ResponderEvent) => void,
  onResponderTerminate: (event: ResponderEvent) => void,
  onResponderTerminationRequest: (event: ResponderEvent) => boolean,
  onStartShouldSetResponder: (event: ResponderEvent) => boolean,
|}>;
type TouchState = 'NOT_RESPONDER' | 'RESPONDER_INACTIVE_PRESS_START' | 'RESPONDER_ACTIVE_PRESS_START' | 'RESPONDER_ACTIVE_LONG_PRESS_START' | 'ERROR';
type TouchSignal = 'DELAY' | 'RESPONDER_GRANT' | 'RESPONDER_RELEASE' | 'RESPONDER_TERMINATED' | 'LONG_PRESS_DETECTED';
const DELAY = 'DELAY';
const ERROR = 'ERROR';
const LONG_PRESS_DETECTED = 'LONG_PRESS_DETECTED';
const NOT_RESPONDER = 'NOT_RESPONDER';
const RESPONDER_ACTIVE_LONG_PRESS_START = 'RESPONDER_ACTIVE_LONG_PRESS_START';
const RESPONDER_ACTIVE_PRESS_START = 'RESPONDER_ACTIVE_PRESS_START';
const RESPONDER_INACTIVE_PRESS_START = 'RESPONDER_INACTIVE_PRESS_START';
const RESPONDER_GRANT = 'RESPONDER_GRANT';
const RESPONDER_RELEASE = 'RESPONDER_RELEASE';
const RESPONDER_TERMINATED = 'RESPONDER_TERMINATED';
const Transitions = Object.freeze({
  NOT_RESPONDER: {
    DELAY: ERROR,
    RESPONDER_GRANT: RESPONDER_INACTIVE_PRESS_START,
    RESPONDER_RELEASE: ERROR,
    RESPONDER_TERMINATED: ERROR,
    LONG_PRESS_DETECTED: ERROR
  },
  RESPONDER_INACTIVE_PRESS_START: {
    DELAY: RESPONDER_ACTIVE_PRESS_START,
    RESPONDER_GRANT: ERROR,
    RESPONDER_RELEASE: NOT_RESPONDER,
    RESPONDER_TERMINATED: NOT_RESPONDER,
    LONG_PRESS_DETECTED: ERROR
  },
  RESPONDER_ACTIVE_PRESS_START: {
    DELAY: ERROR,
    RESPONDER_GRANT: ERROR,
    RESPONDER_RELEASE: NOT_RESPONDER,
    RESPONDER_TERMINATED: NOT_RESPONDER,
    LONG_PRESS_DETECTED: RESPONDER_ACTIVE_LONG_PRESS_START
  },
  RESPONDER_ACTIVE_LONG_PRESS_START: {
    DELAY: ERROR,
    RESPONDER_GRANT: ERROR,
    RESPONDER_RELEASE: NOT_RESPONDER,
    RESPONDER_TERMINATED: NOT_RESPONDER,
    LONG_PRESS_DETECTED: RESPONDER_ACTIVE_LONG_PRESS_START
  },
  ERROR: {
    DELAY: NOT_RESPONDER,
    RESPONDER_GRANT: RESPONDER_INACTIVE_PRESS_START,
    RESPONDER_RELEASE: NOT_RESPONDER,
    RESPONDER_TERMINATED: NOT_RESPONDER,
    LONG_PRESS_DETECTED: NOT_RESPONDER
  }
});
declare var isActiveSignal: (signal: any) => any;
declare var isButtonRole: (element: any) => any;
declare var isPressStartSignal: (signal: any) => any;
declare var isTerminalSignal: (signal: any) => any;
declare var isValidKeyPress: (event: any) => any;
const DEFAULT_LONG_PRESS_DELAY_MS = 450; // 500 - 50
const DEFAULT_PRESS_DELAY_MS = 50;

/**
 * =========================== PressResponder Tutorial ===========================
 *
 * The `PressResponder` class helps you create press interactions by analyzing the
 * geometry of elements and observing when another responder (e.g. ScrollView)
 * has stolen the touch lock. It offers hooks for your component to provide
 * interaction feedback to the user:
 *
 * - When a press has activated (e.g. highlight an element)
 * - When a press has deactivated (e.g. un-highlight an element)
 * - When a press sould trigger an action, meaning it activated and deactivated
 *   while within the geometry of the element without the lock being stolen.
 *
 * A high quality interaction isn't as simple as you might think. There should
 * be a slight delay before activation. Moving your finger beyond an element's
 * bounds should trigger deactivation, but moving the same finger back within an
 * element's bounds should trigger reactivation.
 *
 * In order to use `PressResponder`, do the following:
 *
 *     const pressResponder = new PressResponder(config);
 *
 * 2. Choose the rendered component who should collect the press events. On that
 *    element, spread `pressability.getEventHandlers()` into its props.
 *
 *    return (
 *      <View {...this.state.pressResponder.getEventHandlers()} />
 *    );
 *
 * 3. Reset `PressResponder` when your component unmounts.
 *
 *    componentWillUnmount() {
 *      this.state.pressResponder.reset();
 *    }
 *
 * ==================== Implementation Details ====================
 *
 * `PressResponder` only assumes that there exists a `HitRect` node. The `PressRect`
 * is an abstract box that is extended beyond the `HitRect`.
 *
 * # Geometry
 *
 *  ┌────────────────────────┐
 *  │  ┌──────────────────┐  │ - Presses start anywhere within `HitRect`.
 *  │  │  ┌────────────┐  │  │
 *  │  │  │ VisualRect │  │  │
 *  │  │  └────────────┘  │  │ - When pressed down for sufficient amount of time
 *  │  │    HitRect       │  │   before letting up, `VisualRect` activates.
 *  │  └──────────────────┘  │
 *  │       Out Region   o   │
 *  └────────────────────│───┘
 *                       └────── When the press is released outside the `HitRect`,
 *                               the responder is NOT eligible for a "press".
 *
 * # State Machine
 *
 * ┌───────────────┐ ◀──── RESPONDER_RELEASE
 * │ NOT_RESPONDER │
 * └───┬───────────┘ ◀──── RESPONDER_TERMINATED
 *     │
 *     │ RESPONDER_GRANT (HitRect)
 *     │
 *     ▼
 * ┌─────────────────────┐          ┌───────────────────┐              ┌───────────────────┐
 * │ RESPONDER_INACTIVE_ │  DELAY   │ RESPONDER_ACTIVE_ │  T + DELAY   │ RESPONDER_ACTIVE_ │
 * │ PRESS_START         ├────────▶ │ PRESS_START       ├────────────▶ │ LONG_PRESS_START  │
 * └─────────────────────┘          └───────────────────┘              └───────────────────┘
 *
 * T + DELAY => LONG_PRESS_DELAY + DELAY
 *
 * Not drawn are the side effects of each transition. The most important side
 * effect is the invocation of `onLongPress`. Only when the browser produces a
 * `click` event is `onPress` invoked.
 */
declare export default class PressResponder {
  _config: PressResponderConfig,
  _eventHandlers: ?EventHandlers,
  _isPointerTouch: ?boolean,
  _longPressDelayTimeout: ?TimeoutID,
  _longPressDispatched: ?boolean,
  _pressDelayTimeout: ?TimeoutID,
  _pressOutDelayTimeout: ?TimeoutID,
  _selectionTerminated: ?boolean,
  _touchActivatePosition: ?$ReadOnly<{|
    pageX: number,
    pageY: number,
  |}>,
  _touchState: TouchState,
  constructor(config: PressResponderConfig): any,
  configure(config: PressResponderConfig): void,
  reset(): void,
  getEventHandlers(): EventHandlers,
  _createEventHandlers(): EventHandlers,
  _receiveSignal(signal: TouchSignal, event: ResponderEvent): void,
  _performTransitionSideEffects(prevState: TouchState, nextState: TouchState, signal: TouchSignal, event: ResponderEvent): void,
  _activate(event: ResponderEvent): void,
  _deactivate(event: ResponderEvent): void,
  _handleLongPress(event: ResponderEvent): void,
  _cancelLongPressDelayTimeout(): void,
  _cancelPressDelayTimeout(): void,
  _cancelPressOutDelayTimeout(): void,
}
declare function normalizeDelay(delay: ?number, min: any, fallback: any): number;
declare function getTouchFromResponderEvent(event: ResponderEvent): any;