/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * The examples provided by Facebook are for non-commercial testing and
 * evaluation purposes only.
 *
 * Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * @flow
 */
'use strict';

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

class Tester extends React.Component {
  state = {
    native: new Animated.Value(0),
    js: new Animated.Value(0),
  };

  current = 0;

  onPress = () => {
    const animConfig = (
      this.current && this.props.reverseConfig ? this.props.reverseConfig : this.props.config
    );
    this.current = this.current ? 0 : 1;
    const config = {
      ...animConfig,
      toValue: this.current,
    };

    Animated[this.props.type](this.state.native, { ...config, useNativeDriver: true }).start();
    Animated[this.props.type](this.state.js, { ...config, useNativeDriver: false }).start();
  };

  render() {
    return (
      <TouchableWithoutFeedback onPress={this.onPress}>
        <View>
          <View>
            <Text>Native:</Text>
          </View>
          <View style={styles.row}>
            {this.props.children(this.state.native)}
          </View>
          <View>
            <Text>JavaScript:</Text>
          </View>
          <View style={styles.row}>
            {this.props.children(this.state.js)}
          </View>
        </View>
      </TouchableWithoutFeedback>
    );
  }
}

class ValueListenerExample extends React.Component {
  state = {
    anim: new Animated.Value(0),
    progress: 0,
  };
  _current = 0;

  componentDidMount() {
    this.state.anim.addListener((e) => this.setState({ progress: e.value }));
  }

  componentWillUnmount() {
    this.state.anim.removeAllListeners();
  }

  _onPress = () => {
    this._current = this._current ? 0 : 1;
    const config = {
      duration: 1000,
      toValue: this._current,
    };

    Animated.timing(this.state.anim, { ...config, useNativeDriver: true }).start();
  };

  render() {
    return (
      <TouchableWithoutFeedback onPress={this._onPress}>
        <View>
          <View style={styles.row}>
            <Animated.View
              style={[
                styles.block,
                {
                  opacity: this.state.anim,
                }
              ]}
            />
          </View>
          <Text>Value: {this.state.progress}</Text>
        </View>
      </TouchableWithoutFeedback>
    );
  }
}

const UIExplorerSettingSwitchRow = require('UIExplorerSettingSwitchRow');
class InternalSettings extends React.Component {
  _stallInterval: ?number;
  state: {busyTime: number | string, filteredStall: number};
  render() {
    return (
      <View>
        <UIExplorerSettingSwitchRow
          initialValue={false}
          label="Force JS Stalls"
          onEnable={() => {
            this._stallInterval = setInterval(() => {
              const start = Date.now();
              console.warn('burn CPU');
              while ((Date.now() - start) < 100) {}
            }, 300);
          }}
          onDisable={() => {
            clearInterval(this._stallInterval || 0);
          }}
        />
        <UIExplorerSettingSwitchRow
          initialValue={false}
          label="Track JS Stalls"
          onEnable={() => {
            require('JSEventLoopWatchdog').install({thresholdMS: 25});
            this.setState({busyTime: '<none>'});
            require('JSEventLoopWatchdog').addHandler({
              onStall: ({busyTime}) => this.setState((state) => ({
                busyTime,
                filteredStall: (state.filteredStall || 0) * 0.97 + busyTime * 0.03,
              })),
            });
          }}
          onDisable={() => {
            console.warn('Cannot disable yet....');
          }}
        />
        {this.state && <Text>
          JS Stall filtered: {Math.round(this.state.filteredStall)}, last: {this.state.busyTime}
        </Text>}
      </View>
    );
  }
}

class EventExample extends React.Component {
  state = {
    scrollX: new Animated.Value(0),
  };

  render() {
    const opacity = this.state.scrollX.interpolate({
      inputRange: [0, 200],
      outputRange: [1, 0],
    });
    return (
      <View>
        <Animated.View
          style={[
            styles.block,
            {
              opacity,
            }
          ]}
        />
        <Animated.ScrollView
          horizontal
          style={{ height: 100, marginTop: 16 }}
          onScroll={
            Animated.event([{
              nativeEvent: { contentOffset: { x: this.state.scrollX } }
            }], {
              useNativeDriver: true,
            })
          }
        >
          <View style={{ width: 600, backgroundColor: '#eee', justifyContent: 'center' }}>
            <Text>Scroll me!</Text>
          </View>
        </Animated.ScrollView>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  row: {
    padding: 10,
    zIndex: 1,
  },
  block: {
    width: 50,
    height: 50,
    backgroundColor: 'blue',
  },
});

exports.framework = 'React';
exports.title = 'Native Animated Example';
exports.description = 'Test out Native Animations';

exports.examples = [
{
    title: 'Multistage With Multiply and rotation',
    description: 'description',
    render: function() {
      return (
          <Tester
            type="timing"
            config={{ duration: 1000 }}>
            {anim => (
              <Animated.View
                style={[
                  styles.block,
                  {
                    transform: [
                      {
                        translateX: anim.interpolate({
                          inputRange: [0, 1],
                          outputRange: [0, 200],
                        })
                      },
                      {
                        translateY: anim.interpolate({
                          inputRange: [0, 0.5, 1],
                          outputRange: [0, 50, 0],
                        })
                      },
                      {
                        rotate: anim.interpolate({
                          inputRange: [0, 0.5, 1],
                          outputRange: ['0deg', '90deg', '0deg'],
                        })
                      }
                    ],
                    opacity: Animated.multiply(
                      anim.interpolate({
                        inputRange: [0,1],
                        outputRange: [1,0]
                      }), anim.interpolate({
                        inputRange: [0,1],
                        outputRange: [0.25,1]
                    })
                    )
                  }
                ]}
              />
            )}
          </Tester>
      );
    },
  },
  {
    title: 'Multistage With Multiply',
    description: 'description',
    render: function() {
      return (
          <Tester
            type="timing"
            config={{ duration: 1000 }}>
            {anim => (
              <Animated.View
                style={[
                  styles.block,
                  {
                    transform: [
                      {
                        translateX: anim.interpolate({
                          inputRange: [0, 1],
                          outputRange: [0, 200],
                        })
                      },
                      {
                        translateY: anim.interpolate({
                          inputRange: [0, 0.5, 1],
                          outputRange: [0, 50, 0],
                        })
                      }
                    ],
                    opacity: Animated.multiply(
                      anim.interpolate({
                        inputRange: [0,1],
                        outputRange: [1,0]
                      }), anim.interpolate({
                        inputRange: [0,1],
                        outputRange: [0.25,1]
                    })
                    )
                  }
                ]}
              />
            )}
          </Tester>
      );
    },
  },
  {
    title: 'Scale interpolation',
    description: 'description',
    render: function() {
      return (
        <Tester
          type="timing"
          config={{ duration: 1000 }}>
          {anim => (
            <Animated.View
              style={[
                styles.block,
                {
                  transform: [
                    {
                      scale: anim.interpolate({
                        inputRange: [0, 1],
                        outputRange: [1, 1.4],
                      })
                    }
                  ],
                }
              ]}
            />
          )}
        </Tester>
      );
    },
  },
  {
    title: 'Opacity without interpolation',
    description: 'description',
    render: function() {
      return (
        <Tester
          type="timing"
          config={{ duration: 1000 }}>
          {anim => (
            <Animated.View
              style={[
                styles.block,
                {
                  opacity: anim
                }
              ]}
            />
          )}
        </Tester>
      );
    },
  },
  {
    title: 'Rotate interpolation',
    description: 'description',
    render: function() {
      return (
        <Tester
          type="timing"
          config={{ duration: 1000 }}>
          {anim => (
            <Animated.View
              style={[
                styles.block,
                {
                  transform: [
                    {
                      rotate: anim.interpolate({
                        inputRange: [0, 1],
                        outputRange: ['0deg', '90deg'],
                      })
                    }
                  ],
                }
              ]}
            />
          )}
        </Tester>
      );
    },
  },
  {
    title: 'translateX => Animated.spring',
    description: 'description',
    render: function() {
      return (
        <Tester
          type="spring"
          config={{ bounciness: 0 }}>
          {anim => (
            <Animated.View
              style={[
                styles.block,
                {
                  transform: [
                    {
                      translateX: anim.interpolate({
                        inputRange: [0, 1],
                        outputRange: [0, 100],
                      })
                    },
                  ],
                }
              ]}
            />
          )}
        </Tester>
      );
    },
  },{
    title: 'translateX => Animated.decay',
    render: function() {
      return (
        <Tester
          type="decay"
          config={{ velocity: 0.5 }}
          reverseConfig={{ velocity: -0.5 }}>
          {anim => (
            <Animated.View
              style={[
                styles.block,
                {
                  transform: [
                    {
                      translateX: anim
                    },
                  ],
                }
              ]}
            />
          )}
        </Tester>
      );
    },
  },
  {
    title: 'Animated value listener',
    render: function() {
      return (
        <ValueListenerExample />
      );
    },
  },
  {
    title: 'Internal Settings',
    render: function() {
      return (
        <InternalSettings />
      );
    },
  },
  {
    title: 'Animated events',
    platform: 'android',
    render: function() {
      return (
        <EventExample />
      );
    },
  },
];