#import "RNGHUIKit.h"
#import "RNGestureHandlerActionType.h"
#import "RNGestureHandlerDirection.h"
#import "RNGestureHandlerEventHandlerType.h"
#import "RNGestureHandlerEvents.h"
#import "RNGestureHandlerPointerEvents.h"
#import "RNGestureHandlerPointerTracker.h"
#import "RNGestureHandlerPointerType.h"
#import "RNGestureHandlerState.h"

#import <Foundation/Foundation.h>
#import <React/RCTConvert.h>

#define VEC_LEN_SQ(pt) (pt.x * pt.x + pt.y * pt.y)
#define TEST_MIN_IF_NOT_NAN(value, limit) \
  (!isnan(limit) && ((limit < 0 && value <= limit) || (limit >= 0 && value >= limit)))

#define TEST_MAX_IF_NOT_NAN(value, max) (!isnan(max) && ((max < 0 && value < max) || (max >= 0 && value > max)))

#define APPLY_PROP(recognizer, config, type, prop, propName) \
  do {                                                       \
    id value = config[propName];                             \
    if (value != nil) {                                      \
      recognizer.prop = [RCTConvert type:value];             \
    }                                                        \
  } while (0)

#define APPLY_FLOAT_PROP(prop)                              \
  do {                                                      \
    APPLY_PROP(recognizer, config, CGFloat, prop, @ #prop); \
  } while (0)
#define APPLY_INT_PROP(prop)                                  \
  do {                                                        \
    APPLY_PROP(recognizer, config, NSInteger, prop, @ #prop); \
  } while (0)
#define APPLY_NAMED_INT_PROP(prop, propName)                   \
  do {                                                         \
    APPLY_PROP(recognizer, config, NSInteger, prop, propName); \
  } while (0)

@protocol RNGestureHandlerEventEmitter

- (void)sendEvent:(nonnull RNGestureHandlerStateChange *)event
    withActionType:(RNGestureHandlerActionType)actionType
    forHandlerType:(RNGestureHandlerEventHandlerType)eventHandlerType
           forView:(nonnull RNGHUIView *)detectorView;

- (void)sendNativeTouchEventForGestureHandler:(nonnull RNGestureHandler *)handler
                              withPointerType:(NSInteger)pointerType
                               forHandlerType:(RNGestureHandlerEventHandlerType)eventHandlerType;

@end

@protocol RNRootViewGestureRecognizerDelegate <UIGestureRecognizerDelegate>

- (void)gestureRecognizer:(nullable UIGestureRecognizer *)gestureRecognizer
    didActivateInViewWithTouchHandler:(nullable RNGHUIView *)viewWithTouchHandler;

@end

@interface RNGestureHandler : NSObject <UIGestureRecognizerDelegate> {
 @protected
  UIGestureRecognizer *_recognizer;
 @protected
  RNGestureHandlerState _lastState;
 @protected
  NSInteger _pointerType;
}

+ (nullable RNGestureHandler *)findGestureHandlerByRecognizer:(nonnull UIGestureRecognizer *)recognizer;

- (nonnull instancetype)initWithTag:(nonnull NSNumber *)tag;

@property (nonatomic, readonly, nonnull) NSNumber *tag;
@property (nonatomic, weak, nullable) id<RNGestureHandlerEventEmitter> emitter;
@property (nonatomic, readonly, nullable) UIGestureRecognizer *recognizer;
@property (nonatomic, readonly, nullable) RNGestureHandlerPointerTracker *pointerTracker;
@property (nonatomic, nullable) NSString *testID;
@property (nonatomic) BOOL enabled;
@property (nonatomic) RNGestureHandlerActionType actionType;
@property (nonatomic) BOOL shouldCancelWhenOutside;
@property (nonatomic) BOOL needsPointerData;
@property (nonatomic) BOOL manualActivation;
@property (nonatomic) BOOL cancelsJSResponder;
@property (nonatomic) BOOL dispatchesAnimatedEvents;
@property (nonatomic) BOOL dispatchesReanimatedEvents;
@property (nonatomic, weak, nullable) RNGHUIView *hostDetectorView;
@property (nonatomic, nullable, assign) NSNumber *virtualViewTag;
@property (nonatomic, copy, nullable) NSNumber *viewTag;
@property (nonatomic, readonly) RNGestureHandlerState lastState;

/**
 The view whose coordinate space should be used when reporting event positions to JS.
 Handlers attached via the V3 NativeDetector are bound to the `RNGestureHandlerDetector` wrapper,
 which never carries user-applied transforms — those live on its child. When the detector has
 exactly one subview we descend into it so reported coordinates match the visible (transformed)
 view, the same coordinate space V2 and the V3 VirtualGestureDetector report in. With multiple
 subviews there is no JS-side way to disambiguate which child caught the pointer, so we keep
 the detector itself as the reference frame.
 */
@property (nonatomic, readonly, nullable) RNGHUIView *coordinateView;

- (BOOL)isViewParagraphComponent:(nullable RNGHUIView *)view;
- (nonnull RNGHUIView *)chooseViewForInteraction:(nonnull UIGestureRecognizer *)recognizer;
- (void)bindToView:(nonnull RNGHUIView *)view;
- (void)unbindFromView;
- (void)resetConfig NS_REQUIRES_SUPER;
- (void)setConfig:(nullable NSDictionary *)config NS_REQUIRES_SUPER;
- (void)updateConfig:(nullable NSDictionary *)config NS_REQUIRES_SUPER;
- (void)updateRelations:(nonnull NSDictionary *)relations;
- (BOOL)shouldSuppressActiveEvent:(nonnull RNGestureHandlerEventExtraData *)extraData;
- (void)handleGesture:(nonnull id)recognizer;
- (void)handleGesture:(nonnull id)recognizer fromReset:(BOOL)fromReset;
- (void)handleGesture:(nonnull id)recognizer
                fromReset:(BOOL)fromReset
    fromManualStateChange:(BOOL)fromManualStateChange;
- (void)handleGesture:(nonnull id)recognizer inState:(RNGestureHandlerState)state;
- (void)handleGesture:(nonnull id)recognizer
                  inState:(RNGestureHandlerState)state
    fromManualStateChange:(BOOL)fromManualStateChange;
- (BOOL)containsPointInView;
- (BOOL)wantsToHandleEventsAtPoint:(CGPoint)point;
- (RNGestureHandlerState)state;
- (nullable RNGestureHandlerEventExtraData *)eventExtraData:(nonnull id)recognizer;

- (void)stopActivationBlocker;
- (void)reset;
- (void)sendEventsInState:(RNGestureHandlerState)state
           forViewWithTag:(nonnull NSNumber *)reactTag
            withExtraData:(nonnull RNGestureHandlerEventExtraData *)extraData;
- (void)sendEventsInState:(RNGestureHandlerState)state
           forViewWithTag:(nonnull NSNumber *)reactTag
            withExtraData:(nonnull RNGestureHandlerEventExtraData *)extraData
    fromManualStateChange:(BOOL)fromManualStateChange;
- (void)sendEvent:(nonnull RNGestureHandlerStateChange *)event;
- (void)sendTouchEventInState:(RNGestureHandlerState)state forViewWithTag:(nonnull NSNumber *)reactTag;
- (nullable RNGHUIScrollView *)retrieveScrollView:(nonnull RNGHUIView *)view;
- (nonnull RNGHUIView *)findViewForEvents;
- (BOOL)wantsToAttachDirectlyToView;
- (BOOL)usesNativeOrVirtualDetector;
- (BOOL)isContinuous;

#if !TARGET_OS_OSX
- (BOOL)isUIScrollViewPanGestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer;
#else
- (BOOL)isUIScrollViewPanGestureRecognizer:(nonnull NSGestureRecognizer *)gestureRecognizer;
#endif

#if !TARGET_OS_OSX
- (void)setCurrentPointerType:(RNGestureHandlerPointerType)pointerType;
- (void)setCurrentPointerTypeForEvent:(nonnull UIEvent *)event;
#else
- (void)setCurrentPointerTypeToMouse;
#endif

@end

@interface UIGestureRecognizer (GestureHandler)
@property (nonatomic, readonly, nullable) RNGestureHandler *gestureHandler;
@end
