text: 'use strict';
  
  var React = require('react');
  var PropTypes = require('prop-types');
  var {
      View,
      StyleSheet,
      PanResponder,
      Animated,
      TouchableWithoutFeedback,
      Dimensions,
      Easing,
      BackHandler,
      Platform,
      Modal,
      Keyboard
  } = require('react-native');
  
  var createReactClass = require('create-react-class');
  
  var BackButton = BackHandler;
  
  var screen = Dimensions.get('window');
  
  var styles = StyleSheet.create({
  
      wrapper: {
          backgroundColor: "white"
      },
  
      transparent: {
          zIndex: 2,
          backgroundColor: 'rgba(0,0,0,0)'
      },
  
      absolute: {
          position: "absolute",
          top: 0,
          bottom: 0,
          left: 0,
          right: 0
      }
  
  });
  
  var ModalBox = createReactClass({
  
      propTypes: {
          isOpen: PropTypes.bool,
          isDisabled: PropTypes.bool,
          startOpen: PropTypes.bool,
          backdropPressToClose: PropTypes.bool,
          swipeToClose: PropTypes.bool,
          swipeThreshold: PropTypes.number,
          swipeArea: PropTypes.number,
          position: PropTypes.string,
          entry: PropTypes.string,
          backdrop: PropTypes.bool,
          backdropOpacity: PropTypes.number,
          backdropColor: PropTypes.string,
          backdropContent: PropTypes.element,
          animationDuration: PropTypes.number,
          backButtonClose: PropTypes.bool,
          easing: PropTypes.func,
          coverScreen: PropTypes.bool,
          keyboardTopOffset: PropTypes.number,
          onClosed: PropTypes.func,
          onOpened: PropTypes.func,
          onClosingState: PropTypes.func,
      },
  
      getDefaultProps: function () {
          return {
              startOpen: false,
              backdropPressToClose: true,
              swipeToClose: true,
              swipeThreshold: 50,
              position: "center",
              backdrop: true,
              backdropOpacity: 0.5,
              backdropColor: "black",
              backdropContent: null,
              animationDuration: 400,
              backButtonClose: false,
              easing: Easing.elastic(0.8),
              coverScreen: false,
              keyboardTopOffset: Platform.OS == 'ios' ? 22 : 0,
              useNativeDriver: true
          };
      },
  
      getInitialState: function () {
          var position = this.props.entry === 'top' ? -screen.height : screen.height;
          return {
              position: this.props.startOpen ? new Animated.Value(0) : new Animated.Value(position),
              backdropOpacity: new Animated.Value(0),
              isOpen: this.props.startOpen,
              isAnimateClose: false,
              isAnimateOpen: false,
              swipeToClose: false,
              height: screen.height,
              width: screen.width,
              containerHeight: screen.height,
              containerWidth: screen.width,
              isInitialized: false,
              keyboardOffset: 0
          };
      },
  
      onBackPress () {
          this.close()
          return true
      },
  
      componentDidMount() {
          this.createPanResponder();
          this.handleOpenning(this.props);
          // Needed for IOS because the keyboard covers the screen
          if (Platform.OS === 'ios') {
              this.subscriptions = [
                  Keyboard.addListener('keyboardWillChangeFrame', this.onKeyboardChange),
                  Keyboard.addListener('keyboardDidHide', this.onKeyboardHide)
              ];
          }
      },
  
      componentWillUnmount: function() {
          if (this.subscriptions) this.subscriptions.forEach((sub) => sub.remove());
          if (this.props.backButtonClose && Platform.OS === 'android') BackButton.removeEventListener('hardwareBackPress', this.onBackPress);
      },
  
      componentDidUpdate(prevProps, prevState, snapshot) {
          if(this.props.isOpen !== prevProps.isOpen){
              this.handleOpenning(props);
          }
      },
  
      handleOpenning: function(props) {
          if (typeof props.isOpen == "undefined") return;
          if (props.isOpen)
              this.open();
          else
              this.close();
      },
  
      /****************** ANIMATIONS **********************/
  
      /*
       * The keyboard is hidden (IOS only)
       */
      onKeyboardHide: function(evt) {
          this.setState({ keyboardOffset: 0 });
      },
  
      /*
       * The keyboard frame changed, used to detect when the keyboard open, faster than keyboardDidShow (IOS only)
       */
      onKeyboardChange: function(evt) {
          if (!evt) return;
          if (!this.state.isOpen) return;
          var keyboardFrame = evt.endCoordinates;
          var keyboardHeight = this.state.containerHeight - keyboardFrame.screenY;
  
          this.setState({ keyboardOffset: keyboardHeight }, () => {
              this.animateOpen();
          });
      },
  
      /*
       * Open animation for the backdrop, will fade in
       */
      animateBackdropOpen: function() {
          if (this.state.isAnimateBackdrop && this.state.animBackdrop) {
              this.state.animBackdrop.stop();
          }
          this.setState({ isAnimateBackdrop: true });
  
          let animBackdrop = Animated.timing(
              this.state.backdropOpacity,
              {
                  toValue: 1,
                  duration: this.props.animationDuration,
                  easing: this.props.easing,
                  useNativeDriver: this.props.useNativeDriver,
              }
          ).start(() => {
              this.setState({
                  isAnimateBackdrop: false,
                  animBackdrop
              });
          });
      },
  
      /*
       * Close animation for the backdrop, will fade out
       */
      animateBackdropClose: function() {
          if (this.state.isAnimateBackdrop && this.state.animBackdrop) {
              this.state.animBackdrop.stop();
          }
          this.setState({ isAnimateBackdrop: true });
  
          let animBackdrop = Animated.timing(
              this.state.backdropOpacity,
              {
                  toValue: 0,
                  duration: this.props.animationDuration,
                  easing: this.props.easing,
                  useNativeDriver: this.props.useNativeDriver,
              }
          ).start(() => {
              this.setState({
                  isAnimateBackdrop: false,
                  animBackdrop
              });
          });
      },
  
      /*
       * Stop opening animation
       */
      stopAnimateOpen: function() {
          if (this.state.isAnimateOpen) {
              if (this.state.animOpen) this.state.animOpen.stop();
              this.setState({ isAnimateOpen: false });
          }
      },
  
      /*
       * Open animation for the modal, will move up
       */
      animateOpen: function() {
          this.stopAnimateClose();
  
          // Backdrop fadeIn
          if (this.props.backdrop)
              this.animateBackdropOpen();
  
          this.setState({
              isAnimateOpen: true,
              isOpen: true,
          }, () => {
              requestAnimationFrame(() => {
                  // Detecting modal position
                  let positionDest = this.calculateModalPosition(this.state.containerHeight - this.state.keyboardOffset, this.state.containerWidth);
                  if (this.state.keyboardOffset && (positionDest < this.props.keyboardTopOffset)) {
                      positionDest = this.props.keyboardTopOffset;
                  }
                  let animOpen = Animated.timing(
                      this.state.position,
                      {
                          toValue: positionDest,
                          duration: this.props.animationDuration,
                          easing: this.props.easing,
                          useNativeDriver: this.props.useNativeDriver,
                      }
                  ).start(() => {
                      this.setState({
                          isAnimateOpen: false,
                          animOpen,
                          positionDest
                      });
                      if (this.props.onOpened) this.props.onOpened();
                  });
              })
          });
      },
  
      /*
       * Stop closing animation
       */
      stopAnimateClose: function() {
          if (this.state.isAnimateClose) {
              if (this.state.animClose) this.state.animClose.stop();
              this.setState({ isAnimateClose: false });
          }
      },
  
      /*
       * Close animation for the modal, will move down
       */
      animateClose: function() {
          this.stopAnimateOpen();
  
          // Backdrop fadeout
          if (this.props.backdrop)
              this.animateBackdropClose();
  
          this.setState({
              isAnimateClose: true,
              isOpen: false,
          }, () => {
              let animClose = Animated.timing(
                  this.state.position,
                  {
                      toValue: this.props.entry === 'top' ? -this.state.containerHeight : this.state.containerHeight,
                      duration: this.props.animationDuration,
                      easing: this.props.easing,
                      useNativeDriver: this.props.useNativeDriver,
                  }
              ).start(() => {
                  // Keyboard.dismiss();   // make this optional. Easily user defined in .onClosed() callback
                  this.setState({
                      isAnimateClose: false,
                      animClose
                  });
                  if (this.props.onClosed) this.props.onClosed();
              });
          });
      },
  
      /*
       * Calculate when should be placed the modal
       */
      calculateModalPosition: function(containerHeight, containerWidth) {
          var position = 0;
  
          if (this.props.position == "bottom") {
              position = containerHeight - this.state.height;
          }
          else if (this.props.position == "center") {
              position = containerHeight / 2 - this.state.height / 2;
          }
          // Checking if the position >= 0
          if (position < 0) position = 0;
          return position;
      },
  
      /*
       * Create the pan responder to detect gesture
       * Only used if swipeToClose is enabled
       */
      createPanResponder: function() {
          var closingState = false;
          var inSwipeArea  = false;
  
          var onPanRelease = (evt, state) => {
              if (!inSwipeArea) return;
              inSwipeArea = false;
              if (this.props.entry === 'top' ? -state.dy > this.props.swipeThreshold : state.dy > this.props.swipeThreshold)
                  this.animateClose();
              else if (!this.state.isOpen) {
                  this.animateOpen();
              }
          };
  
          var animEvt = Animated.event([null, {customY: this.state.position}]);
  
          var onPanMove = (evt, state) => {
              var newClosingState = this.props.entry === 'top' ? -state.dy > this.props.swipeThreshold : state.dy > this.props.swipeThreshold;
              if (this.props.entry === 'top' ? state.dy > 0 : state.dy < 0) return;
              if (newClosingState != closingState && this.props.onClosingState)
                  this.props.onClosingState(newClosingState);
              closingState = newClosingState;
              state.customY = state.dy + this.state.positionDest;
  
              animEvt(evt, state);
          };
  
          var onPanStart = (evt, state) => {
              if (!this.props.swipeToClose || this.props.isDisabled || (this.props.swipeArea && (evt.nativeEvent.pageY - this.state.positionDest) > this.props.swipeArea)) {
                  inSwipeArea = false;
                  return false;
              }
              inSwipeArea = true;
              return true;
          };
  
          this.setState({
              pan: PanResponder.create({
                  onStartShouldSetPanResponder: onPanStart,
                  onPanResponderMove: onPanMove,
                  onPanResponderRelease: onPanRelease,
                  onPanResponderTerminate: onPanRelease,
              }),
          });
      },
  
      /*
       * Event called when the modal view layout is calculated
       */
      onViewLayout: function(evt) {
          var height = evt.nativeEvent.layout.height;
          var width = evt.nativeEvent.layout.width;
  
          // If the dimensions are still the same we're done
          let newState = {};
          if (height !== this.state.height) newState.height = height;
          if (width !== this.state.width) newState.width = width;
          this.setState(newState);
  
          if (this.onViewLayoutCalculated) this.onViewLayoutCalculated();
      },
  
      /*
       * Event called when the container view layout is calculated
       */
      onContainerLayout: function(evt) {
          var height = evt.nativeEvent.layout.height;
          var width = evt.nativeEvent.layout.width;
  
          // If the container size is still the same we're done
          if (height == this.state.containerHeight && width == this.state.containerWidth) {
              this.setState({ isInitialized: true });
              return;
          }
  
          if (this.state.isOpen || this.state.isAnimateOpen) {
              this.animateOpen();
          }
  
          if (this.props.onLayout) this.props.onLayout(evt);
          this.setState({
              isInitialized: true,
              containerHeight: height,
              containerWidth: width
          });
      },
  
      /*
       * Render the backdrop element
       */
      renderBackdrop: function() {
          var backdrop  = null;
  
          if (this.props.backdrop) {
              backdrop = (
                  <TouchableWithoutFeedback onPress={this.props.backdropPressToClose ? this.close : null}>
                      <Animated.View importantForAccessibility="no" style={[styles.absolute, {opacity: this.state.backdropOpacity}]}>
                          <View style={[styles.absolute, {backgroundColor:this.props.backdropColor, opacity: this.props.backdropOpacity}]}/>
                          {this.props.backdropContent || []}
                      </Animated.View>
                  </TouchableWithoutFeedback>
              );
          }
  
          return backdrop;
      },
  
      renderContent() {
          var size    = {height: this.state.containerHeight, width: this.state.containerWidth};
          var offsetX = (this.state.containerWidth - this.state.width) / 2;
  
          return (
              <Animated.View
                  onLayout={this.onViewLayout}
                  style={[styles.wrapper, size, this.props.style, {transform: [{translateY: this.state.position}, {translateX: offsetX}]} ]}
                  {...this.state.pan.panHandlers}>
                  {this.props.backdropPressToClose && <TouchableWithoutFeedback onPress={this.close}><View style={[styles.absolute]} /></TouchableWithoutFeedback>}
                  {this.props.children}
              </Animated.View>
          )
      },
  
      /*
       * Render the component
       */
      render: function() {
  
          var visible = this.state.isOpen || this.state.isAnimateOpen || this.state.isAnimateClose;
  
          if (!visible) return <View/>
  
          var content = (
              <View importantForAccessibility="yes" accessibilityViewIsModal={true} style={[styles.transparent, styles.absolute]} pointerEvents={'box-none'}>
                  <View style={{ flex: 1 }} pointerEvents={'box-none'} onLayout={this.onContainerLayout}>
                      {visible && this.renderBackdrop()}
                      {visible && this.renderContent()}
                  </View>
              </View>
          )
  
          if (!this.props.coverScreen) return content;
  
          return (
              <Modal
                  onRequestClose={() => {
                      if (this.props.backButtonClose) {
                          this.close()
                      }
                  }}
                  supportedOrientations={['landscape', 'portrait', 'portrait-upside-down']} transparent visible={visible}
                  hardwareAccelerated={true}
              >
                  {content}
              </Modal>
          );
      },
  
      /****************** PUBLIC METHODS **********************/
  
      open: function() {
          if (this.props.isDisabled) return;
          if (!this.state.isAnimateOpen && (!this.state.isOpen || this.state.isAnimateClose)) {
              this.onViewLayoutCalculated = () => {
                  this.setState({});
                  this.animateOpen();
                  if(this.props.backButtonClose && Platform.OS === 'android') BackButton.addEventListener('hardwareBackPress', this.onBackPress)
                  delete this.onViewLayoutCalculated;
              };
              this.setState({isAnimateOpen : true});
          }
      },
  
      close: function() {
          if (this.props.isDisabled) return;
          if (!this.state.isAnimateClose && (this.state.isOpen || this.state.isAnimateOpen)) {
              this.animateClose();
              if(this.props.backButtonClose && Platform.OS === 'android') BackButton.removeEventListener('hardwareBackPress', this.onBackPress)
          }
      }
  
  
  });
  
  module.exports = ModalBox;
  