import * as React from 'react';
import ReactDOM from 'react-dom';
import { Component, View } from 'reactxp';
import { colors } from '../../config.json';
import { LiftedConstraint, RelayLocation } from '../../shared/daemon-rpc-types';
import { messages } from '../../shared/gettext';
import { IRelayLocationRedux } from '../redux/settings/reducers';
import { LocationScope } from '../redux/userinterface/reducers';
import * as Cell from './Cell';
import CustomScrollbars from './CustomScrollbars';
import { Container, Layout } from './Layout';
import LocationList from './LocationList';
import {
  CloseBarItem,
  NavigationBar,
  NavigationContainer,
  NavigationItems,
  NavigationScrollbars,
  ScopeBar,
  ScopeBarItem,
  TitleBarItem,
} from './NavigationBar';
import styles from './SelectLocationStyles';
import { HeaderSubTitle } from './SettingsHeader';

interface IProps {
  locationScope: LocationScope;
  selectedExitLocation?: RelayLocation;
  selectedBridgeLocation?: LiftedConstraint<RelayLocation>;
  relayLocations: IRelayLocationRedux[];
  bridgeLocations: IRelayLocationRedux[];
  allowBridgeSelection: boolean;
  onClose: () => void;
  onChangeLocationScope: (location: LocationScope) => void;
  onSelectExitLocation: (location: RelayLocation) => void;
  onSelectBridgeLocation: (location: RelayLocation) => void;
  onSelectClosestToExit: () => void;
}

export default class SelectLocation extends Component<IProps> {
  private scrollView = React.createRef<CustomScrollbars>();
  private exitLocationList = React.createRef<LocationList>();
  private bridgeLocationList = React.createRef<LocationList>();

  private scrollPositionByScope: { [index: number]: [number, number] } = {};

  public componentDidMount() {
    this.scrollToSelectedCell();
  }

  public componentDidUpdate(prevProps: IProps, _prevState: {}, snapshot?: [number, number]) {
    if (this.props.locationScope !== prevProps.locationScope) {
      this.restoreScrollPosition(this.props.locationScope);

      if (snapshot) {
        this.saveScrollPosition(prevProps.locationScope, snapshot);
      }
    }
  }

  public getSnapshotBeforeUpdate(_prevProps: IProps) {
    const scrollView = this.scrollView.current;

    if (scrollView) {
      return scrollView.getScrollPosition();
    } else {
      return undefined;
    }
  }

  public render() {
    return (
      <Layout>
        <Container>
          <View style={styles.select_location}>
            <NavigationContainer>
              <NavigationBar alwaysDisplayBarTitle={true}>
                <NavigationItems>
                  <CloseBarItem action={this.props.onClose} />
                  <TitleBarItem>
                    {// TRANSLATORS: Title label in navigation bar
                    messages.pgettext('select-location-nav', 'Select location')}
                  </TitleBarItem>
                </NavigationItems>
                <View style={styles.navigationBarAttachment}>
                  <HeaderSubTitle>
                    {this.props.allowBridgeSelection
                      ? messages.pgettext(
                          'select-location-view',
                          'While connected, your traffic will be routed through two secure locations, the entry point (a bridge server) and the exit point (a VPN server).',
                        )
                      : messages.pgettext(
                          'select-location-view',
                          'While connected, your real location is masked with a private and secure location in the selected region.',
                        )}
                  </HeaderSubTitle>
                  {this.props.allowBridgeSelection && (
                    <ScopeBar
                      style={styles.scopeBar}
                      defaultSelectedIndex={this.props.locationScope}
                      onChange={this.props.onChangeLocationScope}>
                      <ScopeBarItem>
                        {messages.pgettext('select-location-nav', 'Entry')}
                      </ScopeBarItem>
                      <ScopeBarItem>
                        {messages.pgettext('select-location-nav', 'Exit')}
                      </ScopeBarItem>
                    </ScopeBar>
                  )}
                </View>
              </NavigationBar>
              <View style={styles.container}>
                <NavigationScrollbars ref={this.scrollView}>
                  <View style={styles.content}>
                    {this.props.locationScope === LocationScope.relay ? (
                      <LocationList
                        key={'exit-locations'}
                        ref={this.exitLocationList}
                        selectedLocation={this.props.selectedExitLocation}
                        relayLocations={this.props.relayLocations}
                        onSelect={this.props.onSelectExitLocation}
                      />
                    ) : (
                      <React.Fragment>
                        <View>
                          <ClosestToExitCell
                            onSelect={this.props.onSelectClosestToExit}
                            isSelected={this.props.selectedBridgeLocation === 'any'}
                          />
                        </View>
                        <LocationList
                          key={'bridge-locations'}
                          ref={this.bridgeLocationList}
                          selectedLocation={
                            this.props.selectedBridgeLocation !== 'any'
                              ? this.props.selectedBridgeLocation
                              : undefined
                          }
                          relayLocations={this.props.bridgeLocations}
                          onSelect={this.props.onSelectBridgeLocation}
                        />
                      </React.Fragment>
                    )}
                  </View>
                </NavigationScrollbars>
              </View>
            </NavigationContainer>
          </View>
        </Container>
      </Layout>
    );
  }

  public saveScrollPosition(scope: LocationScope, position: [number, number]) {
    this.scrollPositionByScope[scope] = position;
  }

  public restoreScrollPosition(scope: LocationScope) {
    const prevScrollPos = this.scrollPositionByScope[scope];

    if (prevScrollPos) {
      this.scrollToPosition(...prevScrollPos);
    } else {
      this.scrollToSelectedCell();
    }
  }

  private scrollToPosition(x: number, y: number) {
    const scrollView = this.scrollView.current;
    if (scrollView) {
      scrollView.scrollTo(x, y);
    }
  }

  private scrollToSelectedCell() {
    const ref =
      this.props.locationScope === LocationScope.relay
        ? this.exitLocationList
        : this.bridgeLocationList;
    const locationList = ref.current;

    if (locationList) {
      const cell = locationList.selectedCell.current;
      const scrollView = this.scrollView.current;

      if (scrollView) {
        if (cell) {
          const cellDOMNode = ReactDOM.findDOMNode(cell as Element);
          if (cellDOMNode instanceof HTMLElement) {
            scrollView.scrollToElement(cellDOMNode, 'middle');
          }
        } else {
          scrollView.scrollToTop();
        }
      }
    }
  }
}

interface IClosestToExitCellProps {
  isSelected: boolean;
  onSelect: () => void;
}

function ClosestToExitCell(props: IClosestToExitCellProps) {
  return (
    <Cell.CellButton
      style={props.isSelected ? styles.selectedCell : undefined}
      cellHoverStyle={props.isSelected ? styles.selectedCell : undefined}
      onPress={props.onSelect}>
      <Cell.Icon
        source={props.isSelected ? 'icon-tick' : 'icon-nearest'}
        tintColor={colors.white}
        height={24}
        width={24}
      />
      <Cell.Label>{messages.pgettext('select-location-view', 'Closest to exit server')}</Cell.Label>
    </Cell.CellButton>
  );
}
