text: /**
   * Copyright (c) 2013-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   *
   * Facebook, Inc. ("Facebook") owns all right, title and interest, including
   * all intellectual property and other proprietary rights, in and to the React
   * Native CustomComponents software (the "Software").  Subject to your
   * compliance with these terms, you are hereby granted a non-exclusive,
   * worldwide, royalty-free copyright license to (1) use and copy the Software;
   * and (2) reproduce and distribute the Software as part of your own software
   * ("Your Software").  Facebook reserves all rights not expressly granted to
   * you in this license agreement.
   *
   * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
   * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
   * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
   * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * @providesModule NavigationLegacyNavigator
   * @flow
   */
  'use strict';
  
  const Animated = require('react-native').Animated;
  const NavigationAnimatedValueSubscription = require('./NavigationAnimatedValueSubscription');
  const NavigationAnimatedView = require('./NavigationAnimatedView');
  const NavigationCard = require('./NavigationCard');
  const NavigationCardStackStyleInterpolator = require('./NavigationCardStackStyleInterpolator');
  const NavigationContext = require('./NavigationContext');
  const NavigationLegacyNavigatorRouteStack = require('./NavigationLegacyNavigatorRouteStack');
  const NavigationCardStackPanResponder = require('./NavigationCardStackPanResponder');
  const NavigationPagerPanResponder = require('./NavigationPagerPanResponder');
  const NavigationPagerStyleInterpolator = require('./NavigationPagerStyleInterpolator');
  const NavigatorBreadcrumbNavigationBar = require('./NavigatorBreadcrumbNavigationBar');
  const NavigatorNavigationBar = require('./NavigatorNavigationBar');
  const NavigatorSceneConfigs = require('./NavigatorSceneConfigs');
  const React = require('react');
  
  import type  {
    NavigationActionCaller,
    NavigationAnimatedValue,
    NavigationAnimationSetter,
    NavigationParentState,
    NavigationSceneRenderer,
    NavigationSceneRendererProps,
  } from 'NavigationTypeDefinition';
  
  type Props = {
    configureScene: any,
    initialRoute: any,
    initialRouteStack: any,
    navigationBar: any,
    navigationBarNavigator: any,
    navigator: any,
    onDidFocus: any,
    onWillFocus: any,
    renderScene: any,
    renderScene: any,
    style: any,
  };
  
  type State = {
    presentedIndex: number,
    routeStack: Array<any>,
  };
  
  const RouteStack = NavigationLegacyNavigatorRouteStack;
  
  /**
   * NavigationLegacyNavigator is meant to replace Navigator seemlessly with
   * minimum API changes.
   *
   * While the APIs remain compatible with Navigator, it is built with good
   * intention by using the new Navigation API such as
   * `NavigationAnimatedView`...etc.
   */
  class NavigationLegacyNavigator extends React.PureComponent<any, Props, State> {
    static BreadcrumbNavigationBar: any;
    static NavigationBar: any;
    static SceneConfigs: any;
  
    _applyAnimation: NavigationAnimationSetter;
    _navigationBarRef: any;
    _onNavigationBarRef: (ref: any) => void;
    _onPositionChange: (data: {value: number}) => void;
    _positionListener: ?NavigationAnimatedValueSubscription;
    _previousStack: NavigationLegacyNavigatorRouteStack;
    _renderCard: NavigationSceneRenderer;
    _renderHeader: NavigationSceneRenderer;
    _renderScene: NavigationSceneRenderer;
    _routeFocused: any;
    _routeToFocus: any;
    _onNavigate: NavigationActionCaller;
    _stack: NavigationLegacyNavigatorRouteStack;
    _useAnimation: boolean;
  
    navigationContext: NavigationContext;
    parentNavigator: any;
    props: Props;
    state: State;
  
    constructor(props: Props, context: any) {
      super(props, context);
  
      const stack = this._getInitialRouteStack();
  
      // Unfortunately, due to historical reasons, the `state` has been exposed
      // as public members of the navigator, therefore we'd keep private state
      // as private members.
      this._previousStack = stack;
      this._stack = stack;
      this._useAnimation = false;
  
      // Legacy members portred from `Navigator`.
      this.parentNavigator = props.navigator;
      this.navigationContext = new NavigationContext();
  
      this.state = {
        routeStack: stack.toArray(),
        presentedIndex: stack.index,
      };
    }
  
    jumpTo(route: any): void {
      this._applyStack(this._stack.jumpTo(route));
    }
  
    jumpForward(): void {
      this._applyStack(this._stack.jumpForward());
    }
  
    jumpBack(): void {
      this._applyStack(this._stack.jumpBack());
    }
  
    push(route: any): void {
      this._applyStack(this._stack.push(route));
    }
  
    pop(): void {
      this._applyStack(this._stack.pop());
    }
  
    replaceAtIndex(route: any, index: number): void {
      this._applyStack(this._stack.replaceAtIndex(index, route));
    }
  
    replace(route: any): void {
      this.replaceAtIndex(route, this._stack.index);
    }
  
    replacePrevious(route: any): void {
      this.replaceAtIndex(route, this._stack.index - 1);
    }
  
    popToTop(): void {
      this._applyStack(this._stack.slice(0, 1));
    }
  
    popToRoute(route: any): void {
      this._applyStack(this._stack.popToRoute(route));
    }
  
    replacePreviousAndPop(route: any): void {
      this._applyStack(this._stack.replacePreviousAndPop(route));
    }
  
    resetTo(route: any): void {
      this._applyStack(this._stack.resetTo(route));
    }
  
    immediatelyResetRouteStack(routes: Array<any>): void {
      this._applyStack(this._stack.resetRoutes(routes), true);
    }
  
    getCurrentRoutes(): Array<any> {
      return this._stack.toArray();
    }
  
    // Life cycle and private methods below.
  
    componentWillMount(): void {
      this._applyAnimation = this._applyAnimation.bind(this);
      this._onNavigate = this._onNavigate.bind(this);
      this._onNavigationBarRef = this._onNavigationBarRef.bind(this);
      this._onPositionChange = this._onPositionChange.bind(this);
      this._renderCard = this._renderCard.bind(this);
      this._renderHeader = this._renderHeader.bind(this);
      this._renderScene = this._renderScene.bind(this);
  
      this._willFocus();
    }
  
    componentDidMount(): void {
      this._didFocus();
    }
  
    componentWillUnmount(): void {
      this._positionListener && this._positionListener.remove();
    }
  
    componentWillUpdate(nextProps: Props, nextState: State): void {
      this._willFocus();
    }
  
    componentDidUpdate(prevProps: Props, prevState: State): void {
      if (this._useAnimation) {
        // will play animation.
        return;
      }
      this._didFocus();
    }
  
    render(): ReactElement {
      return (
        <NavigationAnimatedView
          applyAnimation={this._applyAnimation}
          navigationState={this._stack.toNavigationState()}
          onNavigate={this._onNavigate}
          renderOverlay={this._renderHeader}
          renderScene={this._renderCard}
          style={this.props.style}
        />
      );
    }
  
    _getInitialRouteStack(): RouteStack {
      const {initialRouteStack, initialRoute} = this.props;
      const routes = initialRouteStack || [initialRoute];
      const index = initialRoute ?
        routes.indexOf(initialRoute) :
        routes.length - 1;
      return new RouteStack(index, routes);
    }
  
    _renderHeader(props: NavigationSceneRendererProps): ?ReactElement {
      // `_renderHeader` is always called before `_renderCard`. We should
      // subscribe to the position here.
      this._positionListener && this._positionListener.remove();
      this._positionListener = new NavigationAnimatedValueSubscription(
        props.position,
        this._onPositionChange,
      );
  
      const {navigationBar, navigationBarNavigator} = this.props;
      if (!navigationBar) {
        return null;
      }
  
      return React.cloneElement(
        navigationBar,
        {
          key: 'header_' + props.scene.key,
          ref: this._onNavigationBarRef,
          navigator: navigationBarNavigator || this,
          navState: {...this.state},
        }
      );
    }
  
    _renderCard(props: NavigationSceneRendererProps): ReactElement {
      const {scene} = props;
      const {configureScene} = this.props;
  
      // Default getters for style and pan responders.
      let styleGetter = NavigationCardStackStyleInterpolator.forHorizontal;
      let panResponderGetter =  NavigationCardStackPanResponder.forHorizontal;
  
      if (configureScene) {
        const route = RouteStack.getRouteByNavigationState(scene.navigationState);
        const config = configureScene(route, this.state.routeStack);
  
        if (config) {
          const gestures = config.gestures || {};
          if (gestures.pop && gestures.pop.direction === 'left-to-right') {
            // pass, will use default getters.
          } else if (gestures.pop && gestures.pop.direction === 'top-to-bottom') {
            styleGetter = NavigationCardStackStyleInterpolator.forVertical;
            panResponderGetter = NavigationCardStackPanResponder.forVertical;
          } else if (
            gestures.jumpBack &&
            gestures.jumpForward &&
            gestures.jumpBack.direction === 'left-to-right' &&
            gestures.jumpForward.direction === 'right-to-left'
          ) {
            styleGetter = NavigationPagerStyleInterpolator.forHorizontal;
            panResponderGetter = NavigationPagerPanResponder.forHorizontal;
          } else if (__DEV__) {
            console.warn('unsupported scene configuration', config);
          }
        }
      }
  
      const style = styleGetter(props);
      const panHandlers = panResponderGetter(props);
  
      return (
        <NavigationCard
          {...props}
          key={'card_' + props.scene.key}
          panHandlers={panHandlers}
          renderScene={this._renderScene}
          style={style}
        />
      );
    }
  
    _renderScene(props: NavigationSceneRendererProps): ReactElement {
      const {navigationState} = props.scene;
      const route = RouteStack.getRouteByNavigationState(navigationState);
      return this.props.renderScene(route, this);
    }
  
    _applyStack(
      stack: NavigationLegacyNavigatorRouteStack,
      noAnimation: ?boolean,
    ): void {
      if (stack !== this._stack) {
        this._previousStack = this._stack;
        this._stack = stack;
  
        this._useAnimation = noAnimation ||
          this._previousStack.index !== stack.index;
  
        this.setState({
          presentedIndex: stack.index,
          routeStack: stack.toArray(),
        });
      }
    }
  
    _onNavigationBarRef(navigationBarRef: any): void {
      this._navigationBarRef = navigationBarRef;
      const {navigationBar} = this.props;
      if (navigationBar && typeof navigationBar.ref === 'function') {
        navigationBar.ref(navigationBarRef);
      }
    }
  
    _onPositionChange(data: {value: number}): void {
      const fromIndex = this._previousStack.index;
      const toIndex = this._stack.index;
  
      if (
        fromIndex !== toIndex &&
        this._navigationBarRef &&
        typeof this._navigationBarRef.updateProgress === 'function'
      ) {
        const progress = (data.value - fromIndex) / (toIndex - fromIndex);
        this._navigationBarRef.updateProgress(progress, fromIndex, toIndex);
      }
  
      const diff = this._stack.index - data.value;
      // When animation stops, the `diff` can still be very a small non-zero value
      // (e.g. 0.00000002). Call `willFocus` when `diff` is small enough.
      if (diff < 0.05) {
        this._didFocus();
      }
    }
  
    _applyAnimation(
      position: NavigationAnimatedValue,
      nextState: NavigationParentState,
      prevState: NavigationParentState,
    ): void {
      const {index} = nextState;
  
      if (!this._useAnimation) {
        position.setValue(index);
        return;
      }
  
      Animated.timing(
        position,
        {
          duration: 500,
          toValue: index,
        }
      ).start();
    }
  
    _willFocus(): void {
      const route = this._stack.get(this._stack.index);
      if (this._routeToFocus === route) {
        return;
      }
      this._routeToFocus = route;
      this.navigationContext.emit('willfocus', {route: route});
      this.props.onWillFocus && this.props.onWillFocus(route);
    }
  
    _didFocus(): void {
      const route = this._stack.get(this._stack.index);
      if (this._routeFocused === route) {
        return;
      }
      this._routeFocused = route;
      this.navigationContext.emit('didfocus', {route: route});
      this.props.onDidFocus && this.props.onDidFocus(route);
    }
  
    _onNavigate(action: any): void {
      switch (action) {
        case NavigationCardStackPanResponder.Actions.BACK:
          this.pop();
          break;
        case NavigationPagerPanResponder.Actions.JUMP_BACK:
          this.jumpBack();
          break;
        case NavigationPagerPanResponder.Actions.JUMP_FORWARD:
          this.jumpForward();
          break;
        default:
          if (__DEV__) {
            console.warn('unsupported gesture action', action);
          }
      }
    }
  }
  
  // Legacy static members.
  NavigationLegacyNavigator.BreadcrumbNavigationBar = NavigatorBreadcrumbNavigationBar;
  NavigationLegacyNavigator.NavigationBar = NavigatorNavigationBar;
  NavigationLegacyNavigator.SceneConfigs = NavigatorSceneConfigs;
  
  module.exports = NavigationLegacyNavigator;
  