/* eslint-disable react/button-has-type */
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Icon from './Icon';

import styles from './FloorControl.styl';

const iconNames = {
  selected: 'doka-icon-edit',
  new: 'doka-icon-default',
  default: 'doka-icon-drag'
};

export default class FloorButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      renaming: false
    };
    this.isListening = false;

    this.renamingElement = null;
    this.setRef = this.setRef.bind(this);
    this.setRenamingRef = this.setRenamingRef.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.onTouchMove = this.onTouchMove.bind(this);
    this.onDragEnd = this.onDragEnd.bind(this);
    this.onDragStart = this.onDragStart.bind(this);
    this.addMouseListeners = this.addMouseListeners.bind(this);
    this.changeFloorName = this.changeFloorName.bind(this);
    this.changeToFloorName = this.changeToFloorName.bind(this);
    this.maybeChangeToFloorname = this.maybeChangeToFloorname.bind(this);
    this.toggleRenaming = this.toggleRenaming.bind(this);
  }

  componentDidMount() {
    if (!this.isListening) this.addMouseListeners();
  }

  componentDidUpdate() {
    const { renaming } = this.state;
    if (renaming) this.renamingElement.focus();
  }

  componentWillUnmount() {
    if (this.isListening) this.removeMouseListeners();
  }

  onMouseMove(e) {
    const cursorPosition = {
      x: e.clientX,
      y: e.clientY
    };
    const { onDragMove, floor } = this.props;
    if (onDragMove) onDragMove(floor, cursorPosition, this.element, this.buttonActive);
  }

  onTouchMove(e) {
    if (e.touches.length !== 1) return;
    const cursorPosition = {
      x: e.touches[0].clientX,
      y: e.touches[0].clientY
    };
    const { onDragMove, floor } = this.props;
    if (onDragMove) onDragMove(floor, cursorPosition, this.element, this.buttonActive);
  }

  onDragStart() {
    // Usually we are interested in coordinates relative to the floorplan viewport,
    // but in this case we want them relative to the browser viewport.
    // Because of this we don't rely on the default 'trackCursor' function here.
    if (!this.isListening) this.addMouseListeners();
    this.buttonActive = true;
  }

  onDragEnd() {
    this.buttonActive = false;
  }

  setRef(element) {
    this.element = element;
  }

  setRenamingRef(element) {
    this.renamingElement = element;
  }

  addMouseListeners() {
    document.addEventListener('mousemove', this.onMouseMove, true);
    document.addEventListener('touchmove', this.onTouchMove, true);
    document.addEventListener('mouseup', this.onDragEnd, true);
    document.addEventListener('touchend', this.onDragEnd, true);
    this.isListening = true;
  }

  removeMouseListeners() {
    document.removeEventListener('mousemove', this.onMouseMove, true);
    document.removeEventListener('touchmove', this.onTouchMove, true);
    document.removeEventListener('mouseup', this.onDragEnd, true);
    document.removeEventListener('touchend', this.onDragEnd, true);
    this.isListening = false;
  }

  changeFloorName(newName) {
    const { floor, onFloorNameChanged } = this.props;
    const newNameTrimmed = newName.trim();
    if (onFloorNameChanged && newNameTrimmed !== '' && newNameTrimmed !== floor.name) onFloorNameChanged(floor.id, newNameTrimmed);
  }

  changeToFloorName(newName) {
    this.changeFloorName(newName);
    this.setState({ renaming: false });
  }

  maybeChangeToFloorname(e) {
    const ENNTER_KEY = 13;
    if (e.keyCode === ENNTER_KEY) {
      this.changeToFloorName(e.target.innerText);
    }
  }

  toggleRenaming(e) {
    this.setState({ renaming: true });
    e.target.classList.toggle('active');
  }

  render() {
    const { floor, offset, isMarkedForDeletion, hasFloorImage, isFloorSelected, onButtonClick } = this.props;
    const { renaming } = this.state;

    const getIconForState = () => {
      const state = !hasFloorImage ? iconNames.new : iconNames.default;
      return isFloorSelected ? iconNames.selected : state;
    };

    const classes = classNames({
      [styles.floorControlItem]: true,
      [styles.floorControlItemSelected]: !!isFloorSelected,
      [styles.floorControlItemDelete]: !!isMarkedForDeletion
    });
    const classesIconButton = classNames({
      [styles.floorControlItemIconButton]: true,
      [styles.floorControlItemIconButtonRenaming]: !!renaming
    });

    // By keeping the li-container in place and moving only the button, the bounding box of the container stays in place.
    return (
      <li
        className={classes}
        onMouseDown={this.onDragStart}
        onTouchStart={this.onDragStart}
        ref={this.setRef}
      >
        <button
          onClick={isFloorSelected ? (e) => this.toggleRenaming(e) : () => onButtonClick()}
          className={classesIconButton}
          style={{
            top: offset * 50
          }}
        >
          <Icon name={getIconForState()} />
        </button>

        {!renaming && (
          <button
            // By passing this key we ensure the button is rerendered when its original order changes, resetting its animation.
            key={floor.order}
            onClick={onButtonClick}
            style={{
              top: offset * 50
            }}
          >
            {floor.name}
          </button>
        )}
        {renaming && (
          <span
            ref={this.setRenamingRef}
            style={{ top: offset * 50 }}
            onBlur={(e) => this.changeToFloorName(e.target.innerText)}
            onKeyDown={this.maybeChangeToFloorname}
            autoFocus
            contentEditable
          />
        )}
      </li>
    );
  }
}

FloorButton.propTypes = {
  onButtonClick: PropTypes.func.isRequired,
  // eslint-disable-next-line react/forbid-prop-types
  floor: PropTypes.object.isRequired,
  hasFloorImage: PropTypes.bool.isRequired,
  onFloorNameChanged: PropTypes.func.isRequired,
  isFloorSelected: PropTypes.bool.isRequired,
  isMarkedForDeletion: PropTypes.bool.isRequired,
  offset: PropTypes.number.isRequired,
  onDragMove: PropTypes.func
};

FloorButton.defaultProps = {
  onDragMove: undefined
};
