import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { Link } from 'react-router';
import classNames from 'classnames';
import * as IDEActions from '../modules/IDE/actions/ide';
import * as toastActions from '../modules/IDE/actions/toast';
import * as projectActions from '../modules/IDE/actions/project';
import { setAllAccessibleOutput } from '../modules/IDE/actions/preferences';
import { logoutUser } from '../modules/User/actions';

import getConfig from '../utils/getConfig';
import { metaKeyName, } from '../utils/metaKey';

import CaretLeftIcon from '../images/left-arrow.svg';
import TriangleIcon from '../images/down-filled-triangle.svg';
import LogoIcon from '../images/p5js-logo-small.svg';

class Nav extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      dropdownOpen: 'none'
    };
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this.clearHideTimeout = this.clearHideTimeout.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleClickOutside = this.handleClickOutside.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleNew = this.handleNew.bind(this);
    this.handleDuplicate = this.handleDuplicate.bind(this);
    this.handleShare = this.handleShare.bind(this);
    this.handleDownload = this.handleDownload.bind(this);
    this.handleFind = this.handleFind.bind(this);
    this.handleAddFile = this.handleAddFile.bind(this);
    this.handleAddFolder = this.handleAddFolder.bind(this);
    this.handleFindNext = this.handleFindNext.bind(this);
    this.handleRun = this.handleRun.bind(this);
    this.handleFindPrevious = this.handleFindPrevious.bind(this);
    this.handleStop = this.handleStop.bind(this);
    this.handleStartAccessible = this.handleStartAccessible.bind(this);
    this.handleStopAccessible = this.handleStopAccessible.bind(this);
    this.handleKeyboardShortcuts = this.handleKeyboardShortcuts.bind(this);
    this.handleLogout = this.handleLogout.bind(this);
    this.toggleDropdownForFile = this.toggleDropdown.bind(this, 'file');
    this.handleFocusForFile = this.handleFocus.bind(this, 'file');
    this.setDropdownForNone = this.setDropdown.bind(this, 'none');
    this.toggleDropdownForEdit = this.toggleDropdown.bind(this, 'edit');
    this.handleFocusForEdit = this.handleFocus.bind(this, 'edit');
    this.toggleDropdownForSketch = this.toggleDropdown.bind(this, 'sketch');
    this.handleFocusForSketch = this.handleFocus.bind(this, 'sketch');
    this.toggleDropdownForHelp = this.toggleDropdown.bind(this, 'help');
    this.handleFocusForHelp = this.handleFocus.bind(this, 'help');
    this.toggleDropdownForAccount = this.toggleDropdown.bind(this, 'account');
    this.handleFocusForAccount = this.handleFocus.bind(this, 'account');
    this.closeDropDown = this.closeDropDown.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClick, false);
    document.addEventListener('keydown', this.closeDropDown, false);
  }
  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClick, false);
    document.removeEventListener('keydown', this.closeDropDown, false);
  }

  setDropdown(dropdown) {
    this.setState({
      dropdownOpen: dropdown
    });
  }

  closeDropDown(e) {
    if (e.keyCode === 27) {
      this.setDropdown('none');
    }
  }

  handleClick(e) {
    if (!this.node) {
      return;
    }
    if (this.node && this.node.contains(e.target)) {
      return;
    }

    this.handleClickOutside();
  }

  handleNew() {
    const { unsavedChanges, warnIfUnsavedChanges } = this.props;
    if (!unsavedChanges) {
      this.props.showToast(1500);
      this.props.setToastText('Opened new sketch.');
      this.props.newProject();
    } else if (warnIfUnsavedChanges && warnIfUnsavedChanges()) {
      this.props.showToast(1500);
      this.props.setToastText('Opened new sketch.');
      this.props.newProject();
    }
    this.setDropdown('none');
  }

  handleSave() {
    if (this.props.user.authenticated) {
      this.props.saveProject(this.props.cmController.getContent());
    } else {
      this.props.showErrorModal('forceAuthentication');
    }
    this.setDropdown('none');
  }

  handleFind() {
    this.props.cmController.showFind();
    this.setDropdown('none');
  }

  handleFindNext() {
    this.props.cmController.findNext();
    this.setDropdown('none');
  }

  handleFindPrevious() {
    this.props.cmController.findPrev();
    this.setDropdown('none');
  }

  handleAddFile() {
    this.props.newFile(this.props.rootFile.id);
    this.setDropdown('none');
  }

  handleAddFolder() {
    this.props.newFolder(this.props.rootFile.id);
    this.setDropdown('none');
  }

  handleRun() {
    this.props.startSketch();
    this.setDropdown('none');
  }

  handleStop() {
    this.props.stopSketch();
    this.setDropdown('none');
  }

  handleStartAccessible() {
    this.props.setAllAccessibleOutput(true);
    this.setDropdown('none');
  }

  handleStopAccessible() {
    this.props.setAllAccessibleOutput(false);
    this.setDropdown('none');
  }

  handleKeyboardShortcuts() {
    this.props.showKeyboardShortcutModal();
    this.setDropdown('none');
  }

  handleLogout() {
    this.props.logoutUser();
    this.setDropdown('none');
  }

  handleDownload() {
    this.props.autosaveProject();
    projectActions.exportProjectAsZip(this.props.project.id);
    this.setDropdown('none');
  }

  handleDuplicate() {
    this.props.cloneProject();
    this.setDropdown('none');
  }

  handleShare() {
    const { username } = this.props.params;
    this.props.showShareModal(this.props.project.id, this.props.project.name, username);
    this.setDropdown('none');
  }

  handleClickOutside() {
    this.setState({
      dropdownOpen: 'none'
    });
  }

  toggleDropdown(dropdown) {
    if (this.state.dropdownOpen === 'none') {
      this.setState({
        dropdownOpen: dropdown
      });
    } else {
      this.setState({
        dropdownOpen: 'none'
      });
    }
  }

  isUserOwner() {
    return this.props.project.owner && this.props.project.owner.id === this.props.user.id;
  }

  handleFocus(dropdown) {
    this.clearHideTimeout();
    this.setDropdown(dropdown);
  }

  clearHideTimeout() {
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }

  handleBlur() {
    this.timer = setTimeout(this.setDropdown.bind(this, 'none'), 10);
  }

  renderDashboardMenu(navDropdownState) {
    return (
      <ul className="nav__items-left">
        <li className="nav__item-logo">
          <LogoIcon role="img" aria-label="p5.js Logo" focusable="false" className="svg__logo" />
        </li>
        <li className="nav__item nav__item--no-icon">
          <Link to="/" className="nav__back-link">
            <CaretLeftIcon className="nav__back-icon" focusable="false" aria-hidden="true" />
            <span className="nav__item-header">
              Back to Editor
            </span>
          </Link>
        </li>
      </ul>
    );
  }

  renderProjectMenu(navDropdownState) {
    return (
      <ul className="nav__items-left">
        <li className="nav__item-logo">
          <LogoIcon role="img" aria-label="p5.js Logo" focusable="false" className="svg__logo" />
        </li>
        <li className={navDropdownState.file}>
          <button
            onClick={this.toggleDropdownForFile}
            onBlur={this.handleBlur}
            onFocus={this.clearHideTimeout}
            onMouseOver={() => {
              if (this.state.dropdownOpen !== 'none') {
                this.setDropdown('file');
              }
            }}
          >
            <span className="nav__item-header">File</span>
            <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
          </button>
          <ul className="nav__dropdown">
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleNew}
                onFocus={this.handleFocusForFile}
                onBlur={this.handleBlur}
              >
                New
              </button>
            </li>
            { getConfig('LOGIN_ENABLED') && (!this.props.project.owner || this.isUserOwner()) &&
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleSave}
                onFocus={this.handleFocusForFile}
                onBlur={this.handleBlur}
              >
                Save
                <span className="nav__keyboard-shortcut">{metaKeyName}+S</span>
              </button>
            </li> }
            { this.props.project.id && this.props.user.authenticated &&
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleDuplicate}
                onFocus={this.handleFocusForFile}
                onBlur={this.handleBlur}
              >
                Duplicate
              </button>
            </li> }
            { this.props.project.id &&
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleShare}
                onFocus={this.handleFocusForFile}
                onBlur={this.handleBlur}
              >
                Share
              </button>
            </li> }
            { this.props.project.id &&
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleDownload}
                onFocus={this.handleFocusForFile}
                onBlur={this.handleBlur}
              >
                Download
              </button>
            </li> }
            { this.props.user.authenticated &&
            <li className="nav__dropdown-item">
              <Link
                to={`/${this.props.user.username}/sketches`}
                onFocus={this.handleFocusForFile}
                onBlur={this.handleBlur}
                onClick={this.setDropdownForNone}
              >
                Open
              </Link>
            </li> }
            {getConfig('UI_COLLECTIONS_ENABLED') &&
              this.props.user.authenticated &&
              this.props.project.id &&
              <li className="nav__dropdown-item">
                <Link
                  to={`/${this.props.user.username}/sketches/${this.props.project.id}/add-to-collection`}
                  onFocus={this.handleFocusForFile}
                  onBlur={this.handleBlur}
                  onClick={this.setDropdownForNone}
                >
                  Add to Collection
                </Link>
              </li>}
            { getConfig('EXAMPLES_ENABLED') &&
            <li className="nav__dropdown-item">
              <Link
                to="/p5/sketches"
                onFocus={this.handleFocusForFile}
                onBlur={this.handleBlur}
                onClick={this.setDropdownForNone}
              >
                Examples
              </Link>
            </li> }
          </ul>
        </li>
        <li className={navDropdownState.edit}>
          <button
            onClick={this.toggleDropdownForEdit}
            onBlur={this.handleBlur}
            onFocus={this.clearHideTimeout}
            onMouseOver={() => {
              if (this.state.dropdownOpen !== 'none') {
                this.setDropdown('edit');
              }
            }}
          >
            <span className="nav__item-header">Edit</span>
            <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
          </button>
          <ul className="nav__dropdown" >
            <li className="nav__dropdown-item">
              <button
                onClick={() => {
                  this.props.cmController.tidyCode();
                  this.setDropdown('none');
                }}
                onFocus={this.handleFocusForEdit}
                onBlur={this.handleBlur}
              >
                Tidy Code
                <span className="nav__keyboard-shortcut">{'\u21E7'}+Tab</span>
              </button>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleFind}
                onFocus={this.handleFocusForEdit}
                onBlur={this.handleBlur}
              >
                Find
                <span className="nav__keyboard-shortcut">{metaKeyName}+F</span>
              </button>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleFindNext}
                onFocus={this.handleFocusForEdit}
                onBlur={this.handleBlur}
              >
                Find Next
                <span className="nav__keyboard-shortcut">{metaKeyName}+G</span>
              </button>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleFindPrevious}
                onFocus={this.handleFocusForEdit}
                onBlur={this.handleBlur}
              >
                Find Previous
                <span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+G</span>
              </button>
            </li>
          </ul>
        </li>
        <li className={navDropdownState.sketch}>
          <button
            onClick={this.toggleDropdownForSketch}
            onBlur={this.handleBlur}
            onFocus={this.clearHideTimeout}
            onMouseOver={() => {
              if (this.state.dropdownOpen !== 'none') {
                this.setDropdown('sketch');
              }
            }}
          >
            <span className="nav__item-header">Sketch</span>
            <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
          </button>
          <ul className="nav__dropdown">
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleAddFile}
                onFocus={this.handleFocusForSketch}
                onBlur={this.handleBlur}
              >
                Add File
              </button>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleAddFolder}
                onFocus={this.handleFocusForSketch}
                onBlur={this.handleBlur}
              >
                Add Folder
              </button>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleRun}
                onFocus={this.handleFocusForSketch}
                onBlur={this.handleBlur}
              >
                Run
                <span className="nav__keyboard-shortcut">{metaKeyName}+Enter</span>
              </button>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleStop}
                onFocus={this.handleFocusForSketch}
                onBlur={this.handleBlur}
              >
                Stop
                <span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+Enter</span>
              </button>
            </li>
            {/* <li className="nav__dropdown-item">
              <button
                onClick={this.handleStartAccessible}
                onFocus={this.handleFocusForSketch}
                onBlur={this.handleBlur}
              >
                Start Accessible
                <span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+1</span>
              </button>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleStopAccessible}
                onFocus={this.handleFocusForSketch}
                onBlur={this.handleBlur}
              >
                Stop Accessible
                <span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+2</span>
              </button>
            </li> */}
          </ul>
        </li>
        <li className={navDropdownState.help}>
          <button
            onClick={this.toggleDropdownForHelp}
            onBlur={this.handleBlur}
            onFocus={this.clearHideTimeout}
            onMouseOver={() => {
              if (this.state.dropdownOpen !== 'none') {
                this.setDropdown('help');
              }
            }}
          >
            <span className="nav__item-header">Help</span>
            <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
          </button>
          <ul className="nav__dropdown">
            <li className="nav__dropdown-item">
              <button
                onFocus={this.handleFocusForHelp}
                onBlur={this.handleBlur}
                onClick={this.handleKeyboardShortcuts}
              >
                Keyboard Shortcuts
              </button>
            </li>
            <li className="nav__dropdown-item">
              <a
                href="https://p5js.org/reference/"
                target="_blank"
                rel="noopener noreferrer"
                onFocus={this.handleFocusForHelp}
                onBlur={this.handleBlur}
                onClick={this.setDropdownForNone}
              >Reference
              </a>
            </li>
            <li className="nav__dropdown-item">
              <Link
                to="/about"
                onFocus={this.handleFocusForHelp}
                onBlur={this.handleBlur}
                onClick={this.setDropdownForNone}
              >
                About
              </Link>
            </li>
          </ul>
        </li>
      </ul>
    );
  }

  renderUnauthenticatedUserMenu(navDropdownState) {
    return (
      <ul className="nav__items-right" title="user-menu">
        <li className="nav__item">
          <Link to="/login" className="nav__auth-button">
            <span className="nav__item-header">Log in</span>
          </Link>
        </li>
        <span className="nav__item-or">or</span>
        <li className="nav__item">
          <Link to="/signup" className="nav__auth-button">
            <span className="nav__item-header">Sign up</span>
          </Link>
        </li>
      </ul>
    );
  }

  renderAuthenticatedUserMenu(navDropdownState) {
    return (
      <ul className="nav__items-right" title="user-menu">
        <li className="nav__item">
          <span>Hello, {this.props.user.username}!</span>
        </li>
        <span className="nav__item-spacer">|</span>
        <li className={navDropdownState.account}>
          <button
            className="nav__item-header"
            onClick={this.toggleDropdownForAccount}
            onBlur={this.handleBlur}
            onFocus={this.clearHideTimeout}
            onMouseOver={() => {
              if (this.state.dropdownOpen !== 'none') {
                this.setDropdown('account');
              }
            }}
          >
            My Account
            <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
          </button>
          <ul className="nav__dropdown">
            <li className="nav__dropdown-item">
              <Link
                to={`/${this.props.user.username}/sketches`}
                onFocus={this.handleFocusForAccount}
                onBlur={this.handleBlur}
                onClick={this.setDropdownForNone}
              >
                My sketches
              </Link>
            </li>
            {getConfig('UI_COLLECTIONS_ENABLED') &&
              <li className="nav__dropdown-item">
                <Link
                  to={`/${this.props.user.username}/collections`}
                  onFocus={this.handleFocusForAccount}
                  onBlur={this.handleBlur}
                  onClick={this.setDropdownForNone}
                >
                  My collections
                </Link>
              </li>
            }
            <li className="nav__dropdown-item">
              <Link
                to={`/${this.props.user.username}/assets`}
                onFocus={this.handleFocusForAccount}
                onBlur={this.handleBlur}
                onClick={this.setDropdownForNone}
              >
                My assets
              </Link>
            </li>
            <li className="nav__dropdown-item">
              <Link
                to="/account"
                onFocus={this.handleFocusForAccount}
                onBlur={this.handleBlur}
                onClick={this.setDropdownForNone}
              >
                Settings
              </Link>
            </li>
            <li className="nav__dropdown-item">
              <button
                onClick={this.handleLogout}
                onFocus={this.handleFocusForAccount}
                onBlur={this.handleBlur}
              >
                Log out
              </button>
            </li>
          </ul>
        </li>
      </ul>
    );
  }

  renderUserMenu(navDropdownState) {
    const isLoginEnabled = getConfig('LOGIN_ENABLED');
    const isAuthenticated = this.props.user.authenticated;

    if (isLoginEnabled && isAuthenticated) {
      return this.renderAuthenticatedUserMenu(navDropdownState);
    } else if (isLoginEnabled && !isAuthenticated) {
      return this.renderUnauthenticatedUserMenu(navDropdownState);
    }

    return null;
  }

  renderLeftLayout(navDropdownState) {
    switch (this.props.layout) {
      case 'dashboard':
        return this.renderDashboardMenu(navDropdownState);
      case 'project':
      default:
        return this.renderProjectMenu(navDropdownState);
    }
  }

  render() {
    const navDropdownState = {
      file: classNames({
        'nav__item': true,
        'nav__item--open': this.state.dropdownOpen === 'file'
      }),
      edit: classNames({
        'nav__item': true,
        'nav__item--open': this.state.dropdownOpen === 'edit'
      }),
      sketch: classNames({
        'nav__item': true,
        'nav__item--open': this.state.dropdownOpen === 'sketch'
      }),
      help: classNames({
        'nav__item': true,
        'nav__item--open': this.state.dropdownOpen === 'help'
      }),
      account: classNames({
        'nav__item': true,
        'nav__item--open': this.state.dropdownOpen === 'account'
      })
    };

    return (
      <header>
        <nav className="nav" title="main-navigation" ref={(node) => { this.node = node; }}>
          {this.renderLeftLayout(navDropdownState)}
          {this.renderUserMenu(navDropdownState)}
        </nav>
      </header>
    );
  }
}

Nav.propTypes = {
  newProject: PropTypes.func.isRequired,
  showToast: PropTypes.func.isRequired,
  setToastText: PropTypes.func.isRequired,
  saveProject: PropTypes.func.isRequired,
  autosaveProject: PropTypes.func.isRequired,
  cloneProject: PropTypes.func.isRequired,
  user: PropTypes.shape({
    authenticated: PropTypes.bool.isRequired,
    username: PropTypes.string,
    id: PropTypes.string
  }).isRequired,
  project: PropTypes.shape({
    id: PropTypes.string,
    name: PropTypes.string,
    owner: PropTypes.shape({
      id: PropTypes.string
    })
  }),
  logoutUser: PropTypes.func.isRequired,
  showShareModal: PropTypes.func.isRequired,
  showErrorModal: PropTypes.func.isRequired,
  unsavedChanges: PropTypes.bool.isRequired,
  warnIfUnsavedChanges: PropTypes.func,
  showKeyboardShortcutModal: PropTypes.func.isRequired,
  cmController: PropTypes.shape({
    tidyCode: PropTypes.func,
    showFind: PropTypes.func,
    findNext: PropTypes.func,
    findPrev: PropTypes.func,
    getContent: PropTypes.func
  }),
  startSketch: PropTypes.func.isRequired,
  stopSketch: PropTypes.func.isRequired,
  setAllAccessibleOutput: PropTypes.func.isRequired,
  newFile: PropTypes.func.isRequired,
  newFolder: PropTypes.func.isRequired,
  layout: PropTypes.oneOf(['dashboard', 'project']),
  rootFile: PropTypes.shape({
    id: PropTypes.string.isRequired
  }).isRequired,
  params: PropTypes.shape({
    username: PropTypes.string
  })
};

Nav.defaultProps = {
  project: {
    id: undefined,
    owner: undefined
  },
  cmController: {},
  layout: 'project',
  warnIfUnsavedChanges: undefined,
  params: {
    username: undefined
  }
};

function mapStateToProps(state) {
  return {
    project: state.project,
    user: state.user,
    unsavedChanges: state.ide.unsavedChanges,
    rootFile: state.files.filter(file => file.name === 'root')[0]
  };
}

const mapDispatchToProps = {
  ...IDEActions,
  ...projectActions,
  ...toastActions,
  logoutUser,
  setAllAccessibleOutput
};

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Nav));
export { Nav as NavComponent };