import React, { Component, Fragment } from 'react';
import propTypes from 'prop-types';

import { JoystickSettings } from './JoystickSettings';
import ReactNipple from 'react-nipple';

import { withStyles } from '@material-ui/core/styles';
import { Grid, Typography } from '@material-ui/core';
import { SpeedDial, SpeedDialAction, SpeedDialIcon } from '@material-ui/lab';

import SyncIcon from '@material-ui/icons/Sync';
import SyncDisabledIcon from '@material-ui/icons/SyncDisabled';
import SettingsIcon from '@material-ui/icons/Settings';

const styles = theme => ({
  joystick: {
    //background: '#222',
    //color: '#efefef',
    height: '80vh',
  },
  speedDial: {
    position: 'fixed',
    bottom: theme.spacing(1),
    right: theme.spacing(1),
  },
});


class Joystick extends Component {
  constructor(props) {
    super(props);
    this.state = {
      show_settings: false,
      rateHz: 10,
      revision: 0,
      speeddial: false,
      enabled: false,
    }
  }

  joystick_msg = {
    testJoystick: {
      axes: [0, 0, 0, 0], // Axis 0, 1, 2, 3
      buttons: [],
      enabled: true,
      axesMode: ['', '', '', ''] // Axis 0, 1, 2, 3
    }
  };

  joy_finetune = {
    axes_scale: [
      [1, 1], // axis_0
      [1, 1], // axis_1
      [1, 1], // axis_2
      [1, 1], // axis_3
    ],
    axes_deadzone: [0.01, 0.01, 0.01, 0.01], // Axis 0, 1, 2, 3
    restJoystick_0: true,
    restJoystick_1: true,
    axesMode: ['interceptor', 'interceptor', 'interceptor', 'interceptor'],
  }

  last_timestamp = 0;
  data_revision = 0;
  renderJoystick_0 = true;
  renderJoystick_1 = true;

  componentDidMount() {
    this.render_delay = setInterval(this.renderDelay, 1000 / this.state.rateHz);
    this.checkJoy = setInterval(this.realGamepad, 50);
  }

  componentWillUnmount() {
    clearInterval(this.render_delay);
    clearInterval(this.checkJoy);
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (!this.props.active) return false;
    return nextState.revision !== this.state.revision || nextState.speeddial !== this.state.speeddial || nextState !== this.state.show_settings;
  }

  renderDelay = () => {
    clearInterval(this.render_delay);
    if (this.state.enabled) {
      this.joystick_msg.testJoystick.axesMode = this.joy_finetune.axesMode;
      this.props.procData(this.joystick_msg);
    }
    if (this.state.revision !== this.data_revision) this.setState({ revision: this.data_revision });
    this.render_delay = setInterval(this.renderDelay, 1000 / this.state.rateHz);
  };

  resetMessage = () => {
    this.joystick_msg.testJoystick.enabled = this.state.enabled;
    if (this.state.enabled) return;
    this.props.procData(this.joystick_msg);
  }

  realGamepad = () => {
    const gamepads = navigator.getGamepads();
    Object.values(gamepads).forEach(gamepad => {
      if (!gamepad ||
        gamepad.timestamp === this.last_timestamp ||
        gamepad.id.startsWith("Surface")) return; //"surface" - device that is reported but not considered as a gamepad for our purpose!

      gamepad.axes.forEach((value, index) => {
        let axis = value;
        if (index < this.joy_finetune.axes_scale.length) { // Apply finetune options only for limited amount of axes(4)
          const deadzone = parseFloat(this.joy_finetune.axes_deadzone[index]);
          const scale_neg = parseFloat(this.joy_finetune.axes_scale[index][0]);
          const scale_pos = parseFloat(this.joy_finetune.axes_scale[index][1]);
          if (-deadzone < axis && axis < deadzone) axis = 0;
          if (axis < 0) axis *= scale_neg;
          else axis *= scale_pos;
        }
        this.joystick_msg.testJoystick.axes[index] = axis;
      })

      const buttons = [];
      Object.values(gamepad.buttons).forEach(button => {
        buttons.push(button.value);
      });
      this.joystick_msg.testJoystick.buttons = buttons;
      this.data_revision += 1;
      this.last_timestamp = gamepad.timestamp;
    })
  }

  virtualJoystick = (joy_index, data) => {
    if (data === 0) {
      this.joystick_msg.testJoystick.axes[joy_index] = 0;
      this.joystick_msg.testJoystick.axes[joy_index + 1] = 0;
      this.data_revision += 1;
      return;
    }
    const size_scale = data.instance.options.size / 2;
    let axis_x = (data.position.x - data.instance.position.x) / size_scale * !data.lockX;
    let axis_y = (data.position.y - data.instance.position.y) / size_scale * !data.lockY;
    const axes_scale = this.joy_finetune.axes_scale;
    if (axis_x < 0) axis_x *= parseFloat(axes_scale[joy_index][0]);
    else axis_x *= parseFloat(axes_scale[joy_index][1]);
    if (axis_y < 0) parseFloat(axis_y *= axes_scale[joy_index + 1][0]);
    else axis_y *= parseFloat(axes_scale[joy_index + 1][1]);
    this.joystick_msg.testJoystick.axes[joy_index] = axis_x;
    this.joystick_msg.testJoystick.axes[joy_index + 1] = axis_y;
    this.data_revision += 1;
  }

  render() {
    //console.log("Rendering Joystick");
    const { classes } = this.props;
    return (
      <Fragment>
        <Grid
          className={classes.joystick}
          container
          spacing={0}
          direction="row"
          justify="center"
          alignItems="center"
        >
          <Grid item xs={6} align="center">
            <Typography variant="subtitle2">
              X:{this.joystick_msg.testJoystick.axes[0].toFixed(3)} /
              Y:{this.joystick_msg.testJoystick.axes[1].toFixed(3)}
            </Typography>
            {this.renderJoystick_0 ? (
              <ReactNipple
                options={{
                  mode: 'semi',
                  position: { top: '50%', left: '50%' },
                  color: 'red',
                  size: 200,
                  dynamicPage: true,
                  lockX: false,
                  lockY: false,
                  restJoystick: this.joy_finetune.restJoystick_0,
                  threshold: 0.06
                }}
                style={{
                  outline: '1px dashed grey',
                  height: '75vh',
                  position: 'relative'
                }}
                onMove={(evt, data) => this.virtualJoystick(0, data)}
                onEnd={() => { if (this.joy_finetune.restJoystick_0) this.virtualJoystick(0, 0) }} // Reseting to 0 on joystick rest (only when restJoystick: true)
              />) : (this.renderJoystick_0 = true, null)}
          </Grid>
          <Grid item xs={6} align="center">
            <Typography variant="subtitle2">
              X:{this.joystick_msg.testJoystick.axes[2].toFixed(3)} /
              Y:{this.joystick_msg.testJoystick.axes[3].toFixed(3)}
            </Typography>
            {this.renderJoystick_1 ? (
              <ReactNipple
                options={{
                  mode: 'semi',
                  position: { top: '50%', left: '50%' },
                  color: 'red',
                  size: 200,
                  dynamicPage: true,
                  lockX: false,
                  lockY: false,
                  restJoystick: this.joy_finetune.restJoystick_1,
                  threshold: 0.06
                }}
                style={{
                  outline: '1px dashed grey',
                  height: '75vh',
                  position: 'relative'
                }}
                onMove={(evt, data) => this.virtualJoystick(2, data)}
                onEnd={() => { if (this.joy_finetune.restJoystick_1) this.virtualJoystick(2, 0) }}
              />) : (this.renderJoystick_1 = true, null)}
          </Grid>
        </Grid>
        <SpeedDial
          ariaLabel="SpeedDial tooltip example"
          className={classes.speedDial}
          icon={<SpeedDialIcon />}
          FabProps={{ size: 'small', color: "secondary" }}
          onClick={() => this.setState({ speeddial: !this.state.speeddial })}
          open={this.state.speeddial}
        >
          <SpeedDialAction
            icon={this.state.enabled ? <SyncDisabledIcon /> : <SyncIcon />}
            tooltipTitle={this.state.enabled ? 'Disable' : 'Enable'}
            tooltipOpen
            onClick={() => this.setState({ enabled: !this.state.enabled }, this.resetMessage)}
          />
          <SpeedDialAction
            icon={<SettingsIcon />}
            tooltipTitle="Settings"
            tooltipOpen
            onClick={() => { this.setState({ show_settings: true }) }}
          />
        </SpeedDial>
        {this.state.show_settings ? (
          <JoystickSettings
            setSettings={
              (joy_finetune) => {
                this.setState({ show_settings: false });
                this.joy_finetune = joy_finetune;
                this.renderJoystick_0 = false;
                this.renderJoystick_1 = false;
              }
            }
            show={this.state.show_settings}
            joy_finetune={this.joy_finetune}
          />
        ) : (null)}
      </Fragment>
    );
  }
}

Joystick.propTypes = {
  classes: propTypes.object.isRequired,
  procData: propTypes.func.isRequired,
  active: propTypes.bool.isRequired,
}

export default withStyles(styles)(Joystick);