/**
 * 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 {
  StyleSheet,
  Text,
  TextInput,
  TouchableWithoutFeedback,
  TouchableOpacity,
  View,
  WebView,
} = ReactNative;

const HEADER = '#3b5998';
const BGWASH = 'rgba(255,255,255,0.8)';
const DISABLED_WASH = 'rgba(255,255,255,0.25)';

const TEXT_INPUT_REF = 'urlInput';
const WEBVIEW_REF = 'webview';
const DEFAULT_URL = 'https://m.facebook.com';
const FILE_SYSTEM_ORIGIN_WHITE_LIST = ['file://*', 'http://*', 'https://*'];

class WebViewExample extends React.Component<{}, $FlowFixMeState> {
  state = {
    url: DEFAULT_URL,
    status: 'No Page Loaded',
    backButtonEnabled: false,
    forwardButtonEnabled: false,
    loading: true,
    scalesPageToFit: true,
  };

  inputText = '';

  handleTextInputChange = event => {
    let url = event.nativeEvent.text;
    if (!/^[a-zA-Z-_]+:/.test(url)) {
      url = 'http://' + url;
    }
    this.inputText = url;
  };

  render() {
    this.inputText = this.state.url;

    return (
      <View style={[styles.container]}>
        <View style={[styles.addressBarRow]}>
          <TouchableOpacity
            onPress={this.goBack}
            style={
              this.state.backButtonEnabled
                ? styles.navButton
                : styles.disabledButton
            }>
            <Text>{'<'}</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={this.goForward}
            style={
              this.state.forwardButtonEnabled
                ? styles.navButton
                : styles.disabledButton
            }>
            <Text>{'>'}</Text>
          </TouchableOpacity>
          <TextInput
            ref={TEXT_INPUT_REF}
            autoCapitalize="none"
            defaultValue={this.state.url}
            onSubmitEditing={this.onSubmitEditing}
            onChange={this.handleTextInputChange}
            clearButtonMode="while-editing"
            style={styles.addressBarTextInput}
          />
          <TouchableOpacity onPress={this.pressGoButton}>
            <View style={styles.goButton}>
              <Text>Go!</Text>
            </View>
          </TouchableOpacity>
        </View>
        <WebView
          ref={WEBVIEW_REF}
          automaticallyAdjustContentInsets={false}
          style={styles.webView}
          source={{uri: this.state.url}}
          javaScriptEnabled={true}
          domStorageEnabled={true}
          decelerationRate="normal"
          onNavigationStateChange={this.onNavigationStateChange}
          onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
          startInLoadingState={true}
          scalesPageToFit={this.state.scalesPageToFit}
        />
        <View style={styles.statusBar}>
          <Text style={styles.statusBarText}>{this.state.status}</Text>
        </View>
      </View>
    );
  }

  goBack = () => {
    this.refs[WEBVIEW_REF].goBack();
  };

  goForward = () => {
    this.refs[WEBVIEW_REF].goForward();
  };

  reload = () => {
    this.refs[WEBVIEW_REF].reload();
  };

  onShouldStartLoadWithRequest = event => {
    // Implement any custom loading logic here, don't forget to return!
    return true;
  };

  onNavigationStateChange = navState => {
    this.setState({
      backButtonEnabled: navState.canGoBack,
      forwardButtonEnabled: navState.canGoForward,
      url: navState.url,
      status: navState.title,
      loading: navState.loading,
      scalesPageToFit: true,
    });
  };

  onSubmitEditing = event => {
    this.pressGoButton();
  };

  pressGoButton = () => {
    const url = this.inputText.toLowerCase();
    if (url === this.state.url) {
      this.reload();
    } else {
      this.setState({
        url: url,
      });
    }
    // dismiss keyboard
    this.refs[TEXT_INPUT_REF].blur();
  };
}

class Button extends React.Component<$FlowFixMeProps> {
  _handlePress = () => {
    if (this.props.enabled !== false && this.props.onPress) {
      this.props.onPress();
    }
  };

  render() {
    return (
      <TouchableWithoutFeedback onPress={this._handlePress}>
        <View style={styles.button}>
          <Text>{this.props.text}</Text>
        </View>
      </TouchableWithoutFeedback>
    );
  }
}

class ScaledWebView extends React.Component<{}, $FlowFixMeState> {
  state = {
    scalingEnabled: true,
  };

  render() {
    return (
      <View>
        <WebView
          style={{
            backgroundColor: BGWASH,
            height: 200,
          }}
          source={{uri: 'https://facebook.github.io/react/'}}
          scalesPageToFit={this.state.scalingEnabled}
        />
        <View style={styles.buttons}>
          {this.state.scalingEnabled ? (
            <Button
              text="Scaling:ON"
              enabled={true}
              onPress={() => this.setState({scalingEnabled: false})}
            />
          ) : (
            <Button
              text="Scaling:OFF"
              enabled={true}
              onPress={() => this.setState({scalingEnabled: true})}
            />
          )}
        </View>
      </View>
    );
  }
}

class MessagingTest extends React.Component<{}, $FlowFixMeState> {
  webview = null;

  state = {
    messagesReceivedFromWebView: 0,
    message: '',
  };

  onMessage = e =>
    this.setState({
      messagesReceivedFromWebView: this.state.messagesReceivedFromWebView + 1,
      message: e.nativeEvent.data,
    });

  postMessage = () => {
    if (this.webview) {
      this.webview.postMessage('"Hello" from React Native!');
    }
  };

  render(): React.Node {
    const {messagesReceivedFromWebView, message} = this.state;

    return (
      <View style={[styles.container, {height: 200}]}>
        <View style={styles.container}>
          <Text>
            Messages received from web view: {messagesReceivedFromWebView}
          </Text>
          <Text>{message || '(No message)'}</Text>
          <View style={styles.buttons}>
            <Button
              text="Send Message to Web View"
              enabled
              onPress={this.postMessage}
            />
          </View>
        </View>
        <View style={styles.container}>
          <WebView
            ref={webview => {
              this.webview = webview;
            }}
            style={{
              backgroundColor: BGWASH,
              height: 100,
            }}
            originWhitelist={FILE_SYSTEM_ORIGIN_WHITE_LIST}
            source={require('./messagingtest.html')}
            onMessage={this.onMessage}
          />
        </View>
      </View>
    );
  }
}

class InjectJS extends React.Component<{}> {
  webview = null;
  injectJS = () => {
    const script = 'document.write("Injected JS ")';
    if (this.webview) {
      this.webview.injectJavaScript(script);
    }
  };
  render() {
    return (
      <View>
        <WebView
          ref={webview => {
            this.webview = webview;
          }}
          style={{
            backgroundColor: BGWASH,
            height: 300,
          }}
          source={{uri: 'https://www.facebook.com'}}
          scalesPageToFit={true}
        />
        <View style={styles.buttons}>
          <Button text="Inject JS" enabled onPress={this.injectJS} />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: HEADER,
  },
  addressBarRow: {
    flexDirection: 'row',
    padding: 8,
  },
  webView: {
    backgroundColor: BGWASH,
    height: 350,
  },
  addressBarTextInput: {
    backgroundColor: BGWASH,
    borderColor: 'transparent',
    borderRadius: 3,
    borderWidth: 1,
    height: 24,
    paddingLeft: 10,
    paddingTop: 3,
    paddingBottom: 3,
    flex: 1,
    fontSize: 14,
  },
  navButton: {
    width: 20,
    padding: 3,
    marginRight: 3,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: BGWASH,
    borderColor: 'transparent',
    borderRadius: 3,
  },
  disabledButton: {
    width: 20,
    padding: 3,
    marginRight: 3,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: DISABLED_WASH,
    borderColor: 'transparent',
    borderRadius: 3,
  },
  goButton: {
    height: 24,
    padding: 3,
    marginLeft: 8,
    alignItems: 'center',
    backgroundColor: BGWASH,
    borderColor: 'transparent',
    borderRadius: 3,
    alignSelf: 'stretch',
  },
  statusBar: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingLeft: 5,
    height: 22,
  },
  statusBarText: {
    color: 'white',
    fontSize: 13,
  },
  buttons: {
    flexDirection: 'row',
    height: 30,
    backgroundColor: 'black',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  button: {
    flex: 0.5,
    width: 0,
    margin: 5,
    borderColor: 'gray',
    borderWidth: 1,
    backgroundColor: 'gray',
  },
});

const HTML = `
<!DOCTYPE html>\n
<html>
  <head>
    <title>Hello Static World</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=320, user-scalable=no">
    <style type="text/css">
      body {
        margin: 0;
        padding: 0;
        font: 62.5% arial, sans-serif;
        background: #ccc;
      }
      h1 {
        padding: 45px;
        margin: 0;
        text-align: center;
        color: #33f;
      }
    </style>
  </head>
  <body>
    <h1>Hello Static World</h1>
  </body>
</html>
`;

exports.displayName = (undefined: ?string);
exports.title = '<WebView>';
exports.description = 'Base component to display web content';
exports.examples = [
  {
    title: 'Simple Browser',
    render(): React.Element<any> {
      return <WebViewExample />;
    },
  },
  {
    title: 'Scale Page to Fit',
    render(): React.Element<any> {
      return <ScaledWebView />;
    },
  },
  {
    title: 'Bundled HTML',
    render(): React.Element<any> {
      return (
        <WebView
          style={{
            backgroundColor: BGWASH,
            height: 100,
          }}
          allowFileAccess={true}
          originWhitelist={FILE_SYSTEM_ORIGIN_WHITE_LIST}
          source={require('./helloworld.html')}
          scalesPageToFit={true}
        />
      );
    },
  },
  {
    title: 'Static HTML',
    render(): React.Element<any> {
      return (
        <WebView
          style={{
            backgroundColor: BGWASH,
            height: 100,
          }}
          source={{html: HTML}}
          scalesPageToFit={true}
        />
      );
    },
  },
  {
    title: 'POST Test',
    render(): React.Element<any> {
      return (
        <WebView
          style={{
            backgroundColor: BGWASH,
            height: 100,
          }}
          source={{
            uri: 'http://www.posttestserver.com/post.php',
            method: 'POST',
            body: 'foo=bar&bar=foo',
          }}
          scalesPageToFit={false}
        />
      );
    },
  },
  {
    title: 'Messaging Test',
    render(): React.Element<any> {
      return <MessagingTest />;
    },
  },
  {
    title: 'Inject JavaScript',
    render(): React.Element<any> {
      return <InjectJS />;
    },
  },
];