import React from "react";

type PropsIOS = {
  initialFocus?: boolean;
  id: string;
  groupId?: string;
  preferredFocus?: boolean;
  onRegister?: (focusable: FocusManager.TouchableRef) => void;
  onUnregister?: (focusable: FocusManager.TouchableRef) => void;
  willReceiveFocus?: FocusManager.FocusEventCB;
  hasReceivedFocus?: FocusManager.FocusEventCB;
  willLoseFocus?: FocusManager.FocusEventCB;
  hasLostFocus?: FocusManager.FocusEventCB;
  failedLostFocus?: FocusManager.FocusEventCB;
  onPress?: (nativeEvent: React.SyntheticEvent) => void;
  onFocus?: FocusManager.FocusEventCB;
  onBlur?: FocusManager.FocusEventCB;
  selected?: boolean;
};

export type BoundingRect = {
  x?: number;
  y?: number;
  width?: number;
  height?: number;
  top?: number;
  bottom?: number;
  left?: number;
  right?: number;
};

type State = {
  isActive?: boolean;
  focused?: boolean;
  boundingRect?: BoundingRect;
};

type Props = PropsIOS;

export abstract class BaseFocusable<T extends Props> extends React.Component<
  T,
  State
> {
  node: FocusManager.TouchableRef;
  ref: React.RefObject<any>;
  _isMounted: boolean;
  isGroup: boolean;

  abstract getId(): string;
  /**
   * returns the Rect of the underlying component of the Focusable
   * @returns {Object} {x, y, width, height, top, bottom, left, right }
   */
  abstract getRect(): BoundingRect;
  /**
   * will invoke the underlying component's onRegister method
   */
  abstract onRegister(focusable: FocusManager.TouchableRef): void;
  /**
   * will invoke the underlying component's onUnregister method
   */
  abstract onUnregister(focusable: FocusManager.TouchableRef): void;
  /**
   * will invoke the underlying component's willLoseFocus method
   */
  abstract willLoseFocus(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  /**
   * will invoke the underlying component's willReceiveFocus method
   */
  abstract willReceiveFocus(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  abstract onFocus(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  abstract hasReceivedFocus(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  abstract hasLostFocus(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  abstract failedLostFocus(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  abstract onBlur(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  /**
   * will invoke the underlying component's onPress method
   */
  abstract onPress(
    focusable: FocusManager.FocusableArg,
    direction: FocusManager.Direction
  ): void;

  /**
   * will tell if the item is in the given group
   * @param {Object} group to test
   * @returns {boolean}
   */
  isInGroup(group: FocusManager.TouchableRef) {
    const { groupId } = this.props;
    const { id } = group.props;

    return id === groupId;
  }

  setFocusedState(focused) {
    if (this._isMounted) {
      this.setState({ focused });
    }
  }
}
