/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @flow
 */

'use strict';

const React = require('react');
const ReactNative = require('react-native');
const {
  Animated,
  Image,
  StyleSheet,
  Text,
  TouchableHighlight,
  TouchableOpacity,
  Platform,
  TouchableNativeFeedback,
  TouchableWithoutFeedback,
  View,
} = ReactNative;

const NativeModules = require('../../Libraries/BatchedBridge/NativeModules');

const forceTouchAvailable =
  (NativeModules.PlatformConstants &&
    NativeModules.PlatformConstants.forceTouchAvailable) ||
  false;

class TouchableHighlightBox extends React.Component<{}, $FlowFixMeState> {
  state = {
    timesPressed: 0,
  };

  touchableOnPress = () => {
    this.setState({
      timesPressed: this.state.timesPressed + 1,
    });
  };

  render() {
    let textLog = '';
    if (this.state.timesPressed > 1) {
      textLog = this.state.timesPressed + 'x TouchableHighlight onPress';
    } else if (this.state.timesPressed > 0) {
      textLog = 'TouchableHighlight onPress';
    }

    return (
      <View>
        <View style={styles.row}>
          <TouchableHighlight
            style={styles.wrapper}
            testID="touchable_highlight_image_button"
            onPress={this.touchableOnPress}>
            <Image source={heartImage} style={styles.image} />
          </TouchableHighlight>
          <TouchableHighlight
            style={styles.wrapper}
            testID="touchable_highlight_text_button"
            activeOpacity={1}
            tvParallaxProperties={{
              pressMagnification: 1.3,
              pressDuration: 0.6,
            }}
            underlayColor="rgb(210, 230, 255)"
            onPress={this.touchableOnPress}>
            <View style={styles.wrapperCustom}>
              <Text style={styles.text}>Tap Here For Custom Highlight!</Text>
            </View>
          </TouchableHighlight>
        </View>
        <View style={styles.logBox}>
          <Text testID="touchable_highlight_console">{textLog}</Text>
        </View>
      </View>
    );
  }
}

class TouchableWithoutFeedbackBox extends React.Component<{}, $FlowFixMeState> {
  state = {
    timesPressed: 0,
  };

  textOnPress = () => {
    this.setState({
      timesPressed: this.state.timesPressed + 1,
    });
  };

  render() {
    let textLog = '';
    if (this.state.timesPressed > 1) {
      textLog = this.state.timesPressed + 'x TouchableWithoutFeedback onPress';
    } else if (this.state.timesPressed > 0) {
      textLog = 'TouchableWithoutFeedback onPress';
    }

    return (
      <View>
        <TouchableWithoutFeedback
          onPress={this.textOnPress}
          testID="touchable_without_feedback_button">
          <View style={styles.wrapperCustom}>
            <Text style={styles.text}>Tap Here For No Feedback!</Text>
          </View>
        </TouchableWithoutFeedback>
        <View style={styles.logBox}>
          <Text testID="touchable_without_feedback_console">{textLog}</Text>
        </View>
      </View>
    );
  }
}

class TextOnPressBox extends React.Component<{}, $FlowFixMeState> {
  state = {
    timesPressed: 0,
  };

  textOnPress = () => {
    this.setState({
      timesPressed: this.state.timesPressed + 1,
    });
  };

  render() {
    let textLog = '';
    if (this.state.timesPressed > 1) {
      textLog = this.state.timesPressed + 'x text onPress';
    } else if (this.state.timesPressed > 0) {
      textLog = 'text onPress';
    }

    return (
      <View>
        <Text
          style={styles.textBlock}
          testID="tappable_text"
          onPress={this.textOnPress}>
          Text has built-in onPress handling
        </Text>
        <View style={styles.logBox}>
          <Text testID="tappable_text_console">{textLog}</Text>
        </View>
      </View>
    );
  }
}

class TouchableFeedbackEvents extends React.Component<{}, $FlowFixMeState> {
  state = {
    eventLog: [],
  };

  render() {
    return (
      <View testID="touchable_feedback_events">
        <View style={[styles.row, styles.centered]}>
          <TouchableOpacity
            style={styles.wrapper}
            testID="touchable_feedback_events_button"
            accessibilityLabel="touchable feedback events"
            accessibilityRole="button"
            onPress={() => this._appendEvent('press')}
            onPressIn={() => this._appendEvent('pressIn')}
            onPressOut={() => this._appendEvent('pressOut')}
            onLongPress={() => this._appendEvent('longPress')}>
            <Text style={styles.button}>Press Me</Text>
          </TouchableOpacity>
        </View>
        <View
          testID="touchable_feedback_events_console"
          style={styles.eventLogBox}>
          {this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
        </View>
      </View>
    );
  }

  _appendEvent = eventName => {
    const limit = 6;
    const eventLog = this.state.eventLog.slice(0, limit - 1);
    eventLog.unshift(eventName);
    this.setState({eventLog});
  };
}

class TouchableDelayEvents extends React.Component<{}, $FlowFixMeState> {
  state = {
    eventLog: [],
  };

  render() {
    return (
      <View testID="touchable_delay_events">
        <View style={[styles.row, styles.centered]}>
          <TouchableOpacity
            style={styles.wrapper}
            testID="touchable_delay_events_button"
            onPress={() => this._appendEvent('press')}
            delayPressIn={400}
            onPressIn={() => this._appendEvent('pressIn - 400ms delay')}
            delayPressOut={1000}
            onPressOut={() => this._appendEvent('pressOut - 1000ms delay')}
            delayLongPress={800}
            onLongPress={() => this._appendEvent('longPress - 800ms delay')}>
            <Text style={styles.button}>Press Me</Text>
          </TouchableOpacity>
        </View>
        <View
          style={styles.eventLogBox}
          testID="touchable_delay_events_console">
          {this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
        </View>
      </View>
    );
  }

  _appendEvent = eventName => {
    const limit = 6;
    const eventLog = this.state.eventLog.slice(0, limit - 1);
    eventLog.unshift(eventName);
    this.setState({eventLog});
  };
}

class ForceTouchExample extends React.Component<{}, $FlowFixMeState> {
  state = {
    force: 0,
  };

  _renderConsoleText = () => {
    return forceTouchAvailable
      ? 'Force: ' + this.state.force.toFixed(3)
      : '3D Touch is not available on this device';
  };

  render() {
    return (
      <View testID="touchable_3dtouch_event">
        <View style={styles.forceTouchBox} testID="touchable_3dtouch_output">
          <Text>{this._renderConsoleText()}</Text>
        </View>
        <View style={[styles.row, styles.centered]}>
          <View
            style={styles.wrapper}
            testID="touchable_3dtouch_button"
            onStartShouldSetResponder={() => true}
            onResponderMove={event =>
              this.setState({force: event.nativeEvent.force})
            }
            onResponderRelease={event => this.setState({force: 0})}>
            <Text style={styles.button}>Press Me</Text>
          </View>
        </View>
      </View>
    );
  }
}

class TouchableHitSlop extends React.Component<{}, $FlowFixMeState> {
  state = {
    timesPressed: 0,
  };

  onPress = () => {
    this.setState({
      timesPressed: this.state.timesPressed + 1,
    });
  };

  render() {
    let log = '';
    if (this.state.timesPressed > 1) {
      log = this.state.timesPressed + 'x onPress';
    } else if (this.state.timesPressed > 0) {
      log = 'onPress';
    }

    return (
      <View testID="touchable_hit_slop">
        <View style={[styles.row, styles.centered]}>
          <TouchableOpacity
            onPress={this.onPress}
            style={styles.hitSlopWrapper}
            hitSlop={{top: 30, bottom: 30, left: 60, right: 60}}
            testID="touchable_hit_slop_button">
            <Text style={styles.hitSlopButton}>Press Outside This View</Text>
          </TouchableOpacity>
        </View>
        <View style={styles.logBox}>
          <Text>{log}</Text>
        </View>
      </View>
    );
  }
}

class TouchableDisabled extends React.Component<{}> {
  render() {
    return (
      <View>
        <TouchableOpacity disabled={true} style={[styles.row, styles.block]}>
          <Text style={styles.disabledButton}>Disabled TouchableOpacity</Text>
        </TouchableOpacity>

        <TouchableOpacity disabled={false} style={[styles.row, styles.block]}>
          <Text style={styles.button}>Enabled TouchableOpacity</Text>
        </TouchableOpacity>

        <TouchableHighlight
          activeOpacity={1}
          disabled={true}
          underlayColor="rgb(210, 230, 255)"
          style={[styles.row, styles.block]}
          onPress={() => console.log('custom THW text - highlight')}>
          <Text style={styles.disabledButton}>Disabled TouchableHighlight</Text>
        </TouchableHighlight>

        <TouchableHighlight
          activeOpacity={1}
          underlayColor="rgb(210, 230, 255)"
          style={[styles.row, styles.block]}
          onPress={() => console.log('custom THW text - highlight')}>
          <Text style={styles.button}>Enabled TouchableHighlight</Text>
        </TouchableHighlight>

        <TouchableWithoutFeedback
          onPress={() => console.log('TWOF has been clicked')}
          disabled={true}>
          <View style={styles.wrapperCustom}>
            <Text
              style={[
                styles.button,
                styles.nativeFeedbackButton,
                styles.disabledButton,
              ]}>
              Disabled TouchableWithoutFeedback
            </Text>
          </View>
        </TouchableWithoutFeedback>

        <TouchableWithoutFeedback
          onPress={() => console.log('TWOF has been clicked')}
          disabled={false}>
          <View style={styles.wrapperCustom}>
            <Text style={[styles.button, styles.nativeFeedbackButton]}>
              Enabled TouchableWithoutFeedback
            </Text>
          </View>
        </TouchableWithoutFeedback>

        {Platform.OS === 'android' && (
          <TouchableNativeFeedback
            style={[styles.row, styles.block]}
            onPress={() => console.log('custom TNF has been clicked')}
            background={TouchableNativeFeedback.SelectableBackground()}>
            <View>
              <Text style={[styles.button, styles.nativeFeedbackButton]}>
                Enabled TouchableNativeFeedback
              </Text>
            </View>
          </TouchableNativeFeedback>
        )}

        {Platform.OS === 'android' && (
          <TouchableNativeFeedback
            disabled={true}
            style={[styles.row, styles.block]}
            onPress={() => console.log('custom TNF has been clicked')}
            background={TouchableNativeFeedback.SelectableBackground()}>
            <View>
              <Text
                style={[styles.disabledButton, styles.nativeFeedbackButton]}>
                Disabled TouchableNativeFeedback
              </Text>
            </View>
          </TouchableNativeFeedback>
        )}
      </View>
    );
  }
}

const heartImage = {
  uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small',
};

const styles = StyleSheet.create({
  row: {
    justifyContent: 'center',
    flexDirection: 'row',
  },
  centered: {
    justifyContent: 'center',
  },
  image: {
    width: 50,
    height: 50,
  },
  text: {
    fontSize: 16,
  },
  block: {
    padding: 10,
  },
  button: {
    color: '#007AFF',
  },
  disabledButton: {
    color: '#007AFF',
    opacity: 0.5,
  },
  nativeFeedbackButton: {
    textAlign: 'center',
    margin: 10,
  },
  hitSlopButton: {
    color: 'white',
  },
  wrapper: {
    borderRadius: 8,
  },
  wrapperCustom: {
    borderRadius: 8,
    padding: 6,
  },
  hitSlopWrapper: {
    backgroundColor: 'red',
    marginVertical: 30,
  },
  logBox: {
    padding: 20,
    margin: 10,
    borderWidth: StyleSheet.hairlineWidth,
    borderColor: '#f0f0f0',
    backgroundColor: '#f9f9f9',
  },
  eventLogBox: {
    padding: 10,
    margin: 10,
    height: 120,
    borderWidth: StyleSheet.hairlineWidth,
    borderColor: '#f0f0f0',
    backgroundColor: '#f9f9f9',
  },
  forceTouchBox: {
    padding: 10,
    margin: 10,
    borderWidth: StyleSheet.hairlineWidth,
    borderColor: '#f0f0f0',
    backgroundColor: '#f9f9f9',
    alignItems: 'center',
  },
  textBlock: {
    fontWeight: '500',
    color: 'blue',
  },
});

exports.displayName = (undefined: ?string);
exports.description = 'Touchable and onPress examples.';
exports.title = '<Touchable*> and onPress';
exports.examples = [
  {
    title: '<TouchableHighlight>',
    description:
      'TouchableHighlight works by adding an extra view with a ' +
      'black background under the single child view.  This works best when the ' +
      'child view is fully opaque, although it can be made to work as a simple ' +
      'background color change as well with the activeOpacity and ' +
      'underlayColor props.',
    render: function() {
      return <TouchableHighlightBox />;
    },
  },
  {
    title: '<TouchableWithoutFeedback>',
    render: function() {
      return <TouchableWithoutFeedbackBox />;
    },
  },
  {
    title: 'TouchableNativeFeedback with Animated child',
    description:
      'TouchableNativeFeedback can have an AnimatedComponent as a' +
      'direct child.',
    platform: 'android',
    render: function() {
      const mScale = new Animated.Value(1);
      Animated.timing(mScale, {toValue: 0.3, duration: 1000}).start();
      const style = {
        backgroundColor: 'rgb(180, 64, 119)',
        width: 200,
        height: 100,
        transform: [{scale: mScale}],
      };
      return (
        <View>
          <View style={styles.row}>
            <TouchableNativeFeedback>
              <Animated.View style={style} />
            </TouchableNativeFeedback>
          </View>
        </View>
      );
    },
  },
  {
    title: '<Text onPress={fn}> with highlight',
    render: function(): React.Element<any> {
      return <TextOnPressBox />;
    },
  },
  {
    title: 'Touchable feedback events',
    description:
      '<Touchable*> components accept onPress, onPressIn, ' +
      'onPressOut, and onLongPress as props.',
    render: function(): React.Element<any> {
      return <TouchableFeedbackEvents />;
    },
  },
  {
    title: 'Touchable delay for events',
    description:
      '<Touchable*> components also accept delayPressIn, ' +
      'delayPressOut, and delayLongPress as props. These props impact the ' +
      'timing of feedback events.',
    render: function(): React.Element<any> {
      return <TouchableDelayEvents />;
    },
  },
  {
    title: '3D Touch / Force Touch',
    description:
      'iPhone 6s and 6s plus support 3D touch, which adds a force property to touches',
    render: function(): React.Element<any> {
      return <ForceTouchExample />;
    },
    platform: 'ios',
  },
  {
    title: 'Touchable Hit Slop',
    description:
      '<Touchable*> components accept hitSlop prop which extends the touch area ' +
      'without changing the view bounds.',
    render: function(): React.Element<any> {
      return <TouchableHitSlop />;
    },
  },
  {
    title: 'Disabled Touchable*',
    description:
      '<Touchable*> components accept disabled prop which prevents ' +
      'any interaction with component',
    render: function(): React.Element<any> {
      return <TouchableDisabled />;
    },
  },
];