import { I18nManager } from 'react-native';
import getSceneIndicesForInterpolationInputRange from '../../utils/getSceneIndicesForInterpolationInputRange';
import { SceneInterpolatorProps } from '../../types';

const EPS = 1e-5;

/**
 * Utility that builds the style for the card in the cards stack.
 *
 *     +------------+
 *   +-+            |
 * +-+ |            |
 * | | |            |
 * | | |  Focused   |
 * | | |   Card     |
 * | | |            |
 * +-+ |            |
 *   +-+            |
 *     +------------+
 */

/**
 * Render the initial style when the initial layout isn't measured yet.
 */
function forInitial(props: SceneInterpolatorProps) {
  const { navigation, scene } = props;

  const focused = navigation.state.index === scene.index;
  const opacity = focused ? 1 : 0;
  // If not focused, move the scene far away.
  const translate = focused ? 0 : 1000000;
  return {
    opacity,
    transform: [{ translateX: translate }, { translateY: translate }],
  };
}

/**
 * Standard iOS-style slide in from the right.
 */
function forHorizontal(props: SceneInterpolatorProps) {
  const { layout, position, scene } = props;

  if (!layout.isMeasured) {
    return forInitial(props);
  }
  const interpolate = getSceneIndicesForInterpolationInputRange(props);

  if (!interpolate) return { opacity: 0 };

  const { first, last } = interpolate;
  const index = scene.index;

  const width = layout.initWidth;
  const translateX = position.interpolate({
    inputRange: [first, index, last],
    outputRange: I18nManager.isRTL
      ? [-width, 0, width * 0.3]
      : [width, 0, width * -0.3],
    extrapolate: 'clamp',
  });

  const shadowOpacity = props.shadowEnabled
    ? position.interpolate({
        inputRange: [first, index, last],
        outputRange: [0, 0.7, 0],
        extrapolate: 'clamp',
      })
    : null;

  let overlayOpacity = props.cardOverlayEnabled
    ? position.interpolate({
        inputRange: [index, last - 0.5, last, last + EPS],
        outputRange: [0, 0.07, 0.07, 0],
        extrapolate: 'clamp',
      })
    : null;

  return {
    transform: [{ translateX }],
    overlayOpacity,
    shadowOpacity,
  };
}

/**
 * Standard iOS-style slide in from the bottom (used for modals).
 */
function forVertical(props: SceneInterpolatorProps) {
  const { layout, position, scene } = props;

  if (!layout.isMeasured) {
    return forInitial(props);
  }
  const interpolate = getSceneIndicesForInterpolationInputRange(props);

  if (!interpolate) return { opacity: 0 };

  const { first, last } = interpolate;
  const index = scene.index;
  const height = layout.initHeight;
  const translateY = position.interpolate({
    inputRange: [first, index, last],
    outputRange: [height, 0, 0],
    extrapolate: 'clamp',
  });

  return {
    transform: [{ translateY }],
  };
}

/**
 * Standard Android-style fade in from the bottom.
 */
function forFadeFromBottomAndroid(props: SceneInterpolatorProps) {
  const { layout, position, scene } = props;

  if (!layout.isMeasured) {
    return forInitial(props);
  }
  const interpolate = getSceneIndicesForInterpolationInputRange(props);

  if (!interpolate) return { opacity: 0 };

  const { first, last } = interpolate;
  const index = scene.index;
  const opacity = position.interpolate({
    inputRange: [first, first + 0.5, first + 0.9, index, last - 1e-5, last],
    outputRange: [0, 0.25, 0.7, 1, 1, 0],
    extrapolate: 'clamp',
  });

  const height = layout.initHeight;
  const maxTranslation = height * 0.08;
  const translateY = position.interpolate({
    inputRange: [first, index, last],
    outputRange: [maxTranslation, 0, 0],
    extrapolate: 'clamp',
  });

  return {
    opacity,
    transform: [{ translateY }],
  };
}

function forFadeToBottomAndroid(props: SceneInterpolatorProps) {
  const { layout, position, scene } = props;

  if (!layout.isMeasured) {
    return forInitial(props);
  }
  const interpolate = getSceneIndicesForInterpolationInputRange(props);

  if (!interpolate) return { opacity: 0 };

  const { first, last } = interpolate;
  const index = scene.index;
  const inputRange = [first, index, last];

  const opacity = position.interpolate({
    inputRange,
    outputRange: [0, 1, 1],
    extrapolate: 'clamp',
  });

  const height = layout.initHeight;
  const maxTranslation = height * 0.08;

  const translateY = position.interpolate({
    inputRange,
    outputRange: [maxTranslation, 0, 0],
    extrapolate: 'clamp',
  });

  return {
    opacity,
    transform: [{ translateY }],
  };
}

/**
 *  fadeIn and fadeOut
 */
function forFade(props: SceneInterpolatorProps) {
  const { layout, position, scene } = props;

  if (!layout.isMeasured) {
    return forInitial(props);
  }
  const interpolate = getSceneIndicesForInterpolationInputRange(props);

  if (!interpolate) return { opacity: 0 };

  const { first, last } = interpolate;
  const index = scene.index;
  const opacity = position.interpolate({
    inputRange: [first, index, last],
    outputRange: [0, 1, 1],
    extrapolate: 'clamp',
  });

  return {
    opacity,
  };
}

function forNoAnimation() {
  return {};
}

export default {
  forHorizontal,
  forVertical,
  forFadeFromBottomAndroid,
  forFadeToBottomAndroid,
  forFade,
  forNoAnimation,
};
