/* eslint-disable import/no-extraneous-dependencies */
import React, { Component } from 'react';
import CodeMirror from 'react-codemirror2';
import PropTypes from 'prop-types';
import _ from 'lodash';

import { getOptions } from './codeMirror/helpers';

/******** File extensions necessary *********/
import 'react-codemirror2/node_modules/codemirror/lib/codemirror.css';
import 'react-codemirror2/node_modules/codemirror/mode/javascript/javascript.js';
import './Editor.css'; // Order Important
import 'react-codemirror2/node_modules/codemirror/mode/xml/xml.js';
import 'react-codemirror2/node_modules/codemirror/theme/material.css';
import 'react-codemirror2/node_modules/codemirror/mode/markdown/markdown.js';
import 'react-codemirror2/node_modules/codemirror/addon/lint/lint.js';
import './codeMirror/addon/lint/eslint/eslint-lint.js';
import 'react-codemirror2/node_modules/codemirror/addon/comment/comment.js';
import 'react-codemirror2/node_modules/codemirror/addon/edit/closebrackets.js';
import 'react-codemirror2/node_modules/codemirror/keymap/sublime.js';

class Editor extends Component {
  constructor(props) {
    super(props);
    this.state = {
      code: this.props.code,
    };
    this.handleReset = this.handleReset.bind(this);
    this.updateCode = this.updateCode.bind(this);
  }

  // Using CWM because options must be defined before component mounts
  componentWillMount() {
    const { onValidCode } = this.props;
    const handleValidCode = (annotationsNotSorted, annotations, cmInstance) => {
      const cleanCode = cmInstance.getValue();
      onValidCode(cleanCode);
    };
    this.options = getOptions({}, _.debounce(handleValidCode, 300));
  }

  componentWillReceiveProps({ code: newCode }) {
    const { code: prevCode } = this.props;
    if (prevCode !== newCode) {
      this.setState({ code: newCode });
    }
  }

  handleReset() {
    this.props.handleReset();
    this.setState({ code: this.props.code });
  }

  // Wrapper to minimize breaking onChange API change on move to react-codemirror2
  updateCode(editor, metadata, newCode) {
    const { code: currCode } = this.state;
    if (newCode !== currCode) {
      this.props.onAnyCode(newCode);
    }
    this.setState({ code: newCode });
  }

  render() {
    const { showLints } = this.props;
    const { code } = this.state;
    if (showLints) {
      require('react-codemirror2/node_modules/codemirror/addon/lint/lint.css');
    }

    return (
      <div id="code-tester-editor">
        <CodeMirror
          value={code}
          className="ReactCodeMirror"
          onChange={this.updateCode}
          options={this.options}
        />
      </div>
    );
  }
}

Editor.propTypes = {
  code: PropTypes.string,
  handleReset: PropTypes.func,
  onValidCode: PropTypes.func,
  onAnyCode: PropTypes.func,
  showLints: PropTypes.bool,
  hideButtons: PropTypes.bool,
};

Editor.defaultProps = {
  code: '// Your code here:',
  hideButtons: false,
  showLints: true,
  onValidCode: (lintedCode) => { },  // eslint-disable-line no-unused-vars
  onAnyCode: (code) => { }, // eslint-disable-line no-unused-vars
  handleReset: () => { console.warn('No handleRest function provided.'); },
};

export default Editor;
