import React from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import { ThemeColors, ThemeContext, SceneView } from '@react-navigation/core';
import DrawerLayout from 'react-native-gesture-handler/DrawerLayout';
import { ScreenContainer } from 'react-native-screens';

import DrawerActions from '../routers/DrawerActions';
import DrawerSidebar from './DrawerSidebar';
import DrawerGestureContext from '../utils/DrawerGestureContext';
import ResourceSavingScene from '../views/ResourceSavingScene';

/**
 * Component that renders the drawer.
 */
export default class DrawerView extends React.PureComponent {
  static contextType = ThemeContext;

  static defaultProps = {
    lazy: true
  };

  static getDerivedStateFromProps(nextProps, prevState) {
    const { index } = nextProps.navigation.state;

    return {
      // Set the current tab to be loaded if it was not loaded before
      loaded: prevState.loaded.includes(index) ? prevState.loaded : [...prevState.loaded, index]
    };
  }

  state = {
    loaded: [this.props.navigation.state.index],
    drawerWidth: typeof this.props.navigationConfig.drawerWidth === 'function' ? this.props.navigationConfig.drawerWidth() : this.props.navigationConfig.drawerWidth
  };

  componentDidMount() {
    Dimensions.addEventListener('change', this._updateWidth);
  }

  componentDidUpdate(prevProps) {
    const {
      openId,
      closeId,
      toggleId,
      isDrawerOpen
    } = this.props.navigation.state;
    const {
      openId: prevOpenId,
      closeId: prevCloseId,
      toggleId: prevToggleId
    } = prevProps.navigation.state;

    let prevIds = [prevOpenId, prevCloseId, prevToggleId];
    let changedIds = [openId, closeId, toggleId].filter(id => !prevIds.includes(id)).sort((a, b) => a > b);

    changedIds.forEach(id => {
      if (id === openId) {
        this._drawer.openDrawer();
      } else if (id === closeId) {
        this._drawer.closeDrawer();
      } else if (id === toggleId) {
        if (isDrawerOpen) {
          this._drawer.closeDrawer();
        } else {
          this._drawer.openDrawer();
        }
      }
    });
  }

  componentWillUnmount() {
    Dimensions.removeEventListener('change', this._updateWidth);
  }

  drawerGestureRef = React.createRef();

  _handleDrawerStateChange = (newState, willShow) => {
    if (newState === 'Idle') {
      if (!this.props.navigation.state.isDrawerIdle) {
        this.props.navigation.dispatch({
          type: DrawerActions.MARK_DRAWER_IDLE,
          key: this.props.navigation.state.key
        });
      }
    } else if (newState === 'Settling') {
      this.props.navigation.dispatch({
        type: DrawerActions.MARK_DRAWER_SETTLING,
        key: this.props.navigation.state.key,
        willShow
      });
    } else {
      if (this.props.navigation.state.isDrawerIdle) {
        this.props.navigation.dispatch({
          type: DrawerActions.MARK_DRAWER_ACTIVE,
          key: this.props.navigation.state.key
        });
      }
    }
  };

  _handleDrawerOpen = () => {
    this.props.navigation.dispatch({
      type: DrawerActions.DRAWER_OPENED,
      key: this.props.navigation.state.key
    });
  };

  _handleDrawerClose = () => {
    this.props.navigation.dispatch({
      type: DrawerActions.DRAWER_CLOSED,
      key: this.props.navigation.state.key
    });
  };

  _updateWidth = () => {
    const drawerWidth = typeof this.props.navigationConfig.drawerWidth === 'function' ? this.props.navigationConfig.drawerWidth() : this.props.navigationConfig.drawerWidth;

    if (this.state.drawerWidth !== drawerWidth) {
      this.setState({ drawerWidth });
    }
  };

  _renderNavigationView = drawerOpenProgress => {
    return <DrawerGestureContext.Provider value={this.drawerGestureRef}>
        <DrawerSidebar screenProps={this.props.screenProps} drawerOpenProgress={drawerOpenProgress} navigation={this.props.navigation} descriptors={this.props.descriptors} contentComponent={this.props.navigationConfig.contentComponent} contentOptions={this.props.navigationConfig.contentOptions} drawerPosition={this.props.navigationConfig.drawerPosition} style={this.props.navigationConfig.style} {...this.props.navigationConfig} />
      </DrawerGestureContext.Provider>;
  };

  _renderContent = () => {
    let { lazy, navigation } = this.props;
    let { loaded } = this.state;
    let { routes } = navigation.state;

    if (this.props.navigationConfig.unmountInactiveRoutes) {
      let activeKey = navigation.state.routes[navigation.state.index].key;
      let descriptor = this.props.descriptors[activeKey];

      return <SceneView navigation={descriptor.navigation} screenProps={this.props.screenProps} component={descriptor.getComponent()} />;
    } else {
      return <ScreenContainer style={styles.pages}>
          {routes.map((route, index) => {
          if (lazy && !loaded.includes(index)) {
            // Don't render a screen if we've never navigated to it
            return null;
          }

          let isFocused = navigation.state.index === index;
          let descriptor = this.props.descriptors[route.key];

          return <ResourceSavingScene key={route.key} style={[StyleSheet.absoluteFill, { opacity: isFocused ? 1 : 0 }]} isVisible={isFocused}>
                <SceneView navigation={descriptor.navigation} screenProps={this.props.screenProps} component={descriptor.getComponent()} />
              </ResourceSavingScene>;
        })}
        </ScreenContainer>;
    }
  };

  _setDrawerGestureRef = ref => {
    this.drawerGestureRef.current = ref;
  };

  render() {
    const { navigation } = this.props;
    const activeKey = navigation.state.routes[navigation.state.index].key;
    const { drawerLockMode } = this.props.descriptors[activeKey].options;
    let { overlayColor, drawerBackgroundColor } = this.props.navigationConfig;

    if (drawerBackgroundColor) {
      drawerBackgroundColor = typeof drawerBackgroundColor === 'string' ? drawerBackgroundColor : drawerBackgroundColor[this.context];
    } else {
      drawerBackgroundColor = ThemeColors[this.context].bodyContent;
    }

    if (overlayColor) {
      overlayColor = typeof overlayColor === 'string' ? overlayColor : overlayColor[this.context];
    } else {
      overlayColor = ThemeColors[this.context].bodyContent;
    }

    return <DrawerLayout ref={c => {
      this._drawer = c;
    }} onGestureRef={this._setDrawerGestureRef} drawerLockMode={drawerLockMode || this.props.screenProps && this.props.screenProps.drawerLockMode || this.props.navigationConfig.drawerLockMode} drawerBackgroundColor={drawerBackgroundColor} overlayColor={overlayColor} keyboardDismissMode={this.props.navigationConfig.keyboardDismissMode} drawerWidth={this.state.drawerWidth} onDrawerOpen={this._handleDrawerOpen} onDrawerClose={this._handleDrawerClose} onDrawerStateChanged={this._handleDrawerStateChange} useNativeAnimations={this.props.navigationConfig.useNativeAnimations} renderNavigationView={this._renderNavigationView} drawerPosition={this.props.navigationConfig.drawerPosition === 'right' ? DrawerLayout.positions.Right : DrawerLayout.positions.Left}
    /* props specific to react-native-gesture-handler/DrawerLayout */
    drawerType={this.props.navigationConfig.drawerType} edgeWidth={this.props.navigationConfig.edgeWidth} hideStatusBar={this.props.navigationConfig.hideStatusBar} statusBarAnimation={this.props.navigationConfig.statusBarAnimation} minSwipeDistance={this.props.navigationConfig.minSwipeDistance} drawerContainerStyle={this.props.navigationConfig.drawerContainerStyle} contentContainerStyle={this.props.navigationConfig.contentContainerStyle}>
        <DrawerGestureContext.Provider value={this.drawerGestureRef}>
          {this._renderContent()}
        </DrawerGestureContext.Provider>
      </DrawerLayout>;
  }
}

const styles = StyleSheet.create({
  pages: {
    flex: 1
  }
});