/* eslint no-unused-vars: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

import './CodeTester.css';
import Editor from '../Editor/Editor';
import TestRunner from '../TestRunner/TestRunner';
import { initSplit } from './helpers';
import ExpandPaneIcon from '../ExpandPaneIcon/ExpandPaneIcon';
import Markdown from '../Markdown/Markdown';
import Console from '../Console/Console';

import EditModeTabs from '../EditModeTabs/EditModeTabs';

class CodeTester extends Component {
  constructor(props) {
    super(props);
    this.state = {
      code: '',
      lintedCode: '',
      verticalPanes: null,
      verticalPaneSizes: [],
      horizontalPanes: null,
      horizontalPaneSizes: [],
      expanded: null,
      consoleLogs: [],
      consoleErrors: [],
      consoleVisible: true,
    };
    this.handleLintedCode = this.handleLintedCode.bind(this);
    this.handleConsoleLog = this.handleConsoleLog.bind(this);
    this.handleToggleConsole = this.handleToggleConsole.bind(this);
    this.updateCode = this.updateCode.bind(this);
    this.renderCenterPane = this.renderCenterPane.bind(this);
  }

  componentDidMount() {
    const verticalPanes = initSplit(
      ['#code-tester-instructions', '#code-tester-editor', '#code-tester-test-runner'],
      { sizes: [33, 33, 34], minSize: [0, 0, 0] }
    );
    const horizontalPanes = initSplit(
      ['.code-tester-split-top', '.code-tester-split-bottom'],
      { sizes: [70, 30], direction: 'vertical', minSize: [400, 50] }
    );
    this.setState({ verticalPanes, horizontalPanes })
  }

  handleLintedCode(lintedCode) {
    // This function is only called if code passes linting rules
    const prevCode = this.state.lintedCode;
    // setState causes a re-render, so we need to make sure the code is different
    // from the code we already have.
    if (lintedCode !== prevCode) {
      this.setState({ lintedCode });
      this.props.onLintedCode(lintedCode);
    }
    // send clean code to test runner
    // save to localStorage
  }

  updateCode(code) {
    /* This function is called when any code is entered,
      whether it passes linting rules or not.
    */
    const prevCode = this.state.code;
    if (prevCode !== code) {
      this.setState({ code });
      this.props.onAnyCode(code);
    }
  }

  toggleFullWidth(targetPaneNum) {
    const { verticalPanes } = this.state;
    if (this.state.expanded === null) {
      const verticalPaneSizes = verticalPanes.getSizes();
      this.setState({ expanded: targetPaneNum, verticalPaneSizes });
      _.range(0, verticalPaneSizes.length)
        .filter(paneId => paneId !== targetPaneNum)
        .forEach(paneId => {
        verticalPanes.collapse(paneId);
      });
    } else {
      verticalPanes.setSizes(this.state.verticalPaneSizes);
      this.setState({ expanded: null });
    }
  }

  handleToggleConsole() {
    const { consoleVisible, horizontalPanes, horizontalPaneSizes: lastSizes } = this.state;
    let horizontalPaneSizes;
    if (consoleVisible) {
      horizontalPaneSizes = horizontalPanes.getSizes();
      horizontalPanes.setSizes([93, 7]);
    } else {
      horizontalPanes.setSizes(lastSizes);
    }
    this.setState({ consoleVisible: !this.state.consoleVisible, horizontalPaneSizes });
  }

  handleConsoleLog(logs) {
    this.setState({ consoleLogs: logs });
  }

  renderCenterPane() {
    const {
      editMode,
      initialCode,
      instructions,
      handleInstructionsChange,
      handleTestSpecChange,
      showLints,
      testSpec
    } = this.props;
    return editMode ?
    (
      <EditModeTabs
        initialCode={initialCode}
        showLints={showLints}
        instructions={instructions}
        testSpec={testSpec}
        handleInstructionsChange={handleInstructionsChange}
        handleTestSpecChange={handleTestSpecChange}
      />
    ) :
    (
      <Editor
        code={initialCode}
        onValidCode={this.handleLintedCode}
        onAnyCode={this.updateCode}
        showLints={showLints}
      />
    );
  }

  render() {
    const { lintedCode, verticalPanes } = this.state;
    const {
      onTestResults,
      testSpec,
      instructions,
      autoRun,
      iFrameUrl,
      testHandlerUrl,
      onError,
      infiniteLoopTimeout,
    } = this.props;
    const handleTestResults = this.props.onTestResults;
    return (
      <div className="CodeTester">
        <div className="outer-flex-container">
          <div className="inner-flex-container code-tester-split-top outer-split-item">
            <div className="inner-flex-item split" id="code-tester-instructions">
              <Markdown markdown={instructions} />
            </div>
            <div className="inner-flex-item split" id="code-tester-editor">
              <ExpandPaneIcon paneExpander={this.toggleFullWidth.bind(this, 1)} />
              {this.renderCenterPane()}
            </div>
            <div className="inner-flex-item split test-runner-container" id="code-tester-test-runner">
              <TestRunner
                testSpec={testSpec}
                codeToTest={lintedCode}
                onTestResults={handleTestResults}
                onConsoleLog={this.handleConsoleLog}
                autoRun={autoRun}
                iFrameUrl={iFrameUrl}
                testHandlerUrl={testHandlerUrl}
                onError={onError}
                infiniteLoopTimeout={infiniteLoopTimeout}
              />
            </div>
          </div>{/* End of inner flex container*/}

          <div className="code-tester-split-bottom outer-split-item">
            <Console logs={this.state.consoleLogs} onToggleConsole={this.handleToggleConsole} show={this.state.consoleVisible} />
          </div>
        </div>{/* End of outer flex container*/}
      </div>
    );
  }
}

CodeTester.propTypes = {
  autoRun: PropTypes.bool,
  iFrameUrl: PropTypes.string.isRequired,
  initialCode: PropTypes.string,
  instructions: PropTypes.string,
  onAnyCode: PropTypes.func,
  onLintedCode: PropTypes.func,
  onTestResults: PropTypes.func,
  showLints: PropTypes.bool,
  testSpec: PropTypes.string.isRequired,
  handleInstructionsChange: PropTypes.func,
  handleTestSpecChange: PropTypes.func,
  handleBoilerplateChange: PropTypes.func,
  editMode: PropTypes.bool,
  testHandlerUrl: PropTypes.string,
  onError: PropTypes.func,
  infiniteLoopTimeout: PropTypes.number,
};


CodeTester.defaultProps = {
  onTestResults: (passCount, failCount, testDuration) => {},
  onLintedCode: (code) => {},
  onAnyCode: (code) => {},
  initialCode: '',
  instructions: '',
  autoRun: false,
  showLints: true,
  editMode: false,
  onError: (err) => {
    const message = _.get(err, 'message') ? err.message : 'Unknown';
    console.error('Code Tester Error: ', message);
  },
  infiniteLoopTimeout: 3000,
};

export default CodeTester;
