/* 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 InstructionsPanel from '../InstructionsPanel/InstructionsPanel';
import CodeTesterHeader from '../CodeTesterHeader/CodeTesterHeader';

import EditModeTabs from '../EditModeTabs/EditModeTabs';
import { renderIf } from '../../helpers';

class CodeTester extends Component {
  constructor(props) {
    super(props);
    this.state = {
      code: '',
      lintedCode: '',
      verticalPanes: null,
      verticalPaneSizes: [],
      horizontalPanes: null,
      horizontalPaneSizes: [],
      expanded: null,
      consoleLogs: [],
      consoleErrors: [],
      consoleVisible: props.showConsole,
      consoleStyle: {
        right: '-40%'
      },
      testsVisible: false,
      selectedTabIndex: 0,
    };
    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.renderEditorPanel = this.renderEditorPanel.bind(this);
    this.toggleInstructions = this.toggleInstructions.bind(this);
    this.handleRunClick = this.handleRunClick.bind(this);
    this.onTabSelect = this.onTabSelect.bind(this);
  }

  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);
    }
  }

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

  toggleInstructions() {
    this.setState({ testsVisible: !this.state.testsVisible });
  }

  handleToggleConsole() {
    const { consoleVisible } = this.state;
    const consoleStyle = {};
    if (consoleVisible) {
      consoleStyle.right = '-40%';
    } else {
      consoleStyle.right = '0';
    }
    this.setState({ consoleVisible: !this.state.consoleVisible, consoleStyle });
  }

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

  handleRunClick() {
    this.onTabSelect(1, null, null, () => {
      this.TestRunnerRef.handleNewCode();
    });
  }

  onTabSelect(selectedTabIndex, prevIndex, event, cb = () => {}) {
    if (selectedTabIndex === 2) {
      this.setState(({ editMode }) => ({ editMode: true, selectedTabIndex }), cb);
    } else {
      this.setState(({ editMode }) => ({ editMode: false, selectedTabIndex }), cb);
    }
  }

  renderEditorPanel() {
    const {
      initialCode,
      instructions,
      handleInstructionsChange,
      handleTestSpecChange,
      handleBoilerplateChange,
      handleReset,
      showLints,
      testSpec,
      hideEditorButtons,
      headerText,
      subHeaderText,
    } = this.props;
    const { editMode } = this.state;
    return editMode ? (
      <EditModeTabs
        initialCode={initialCode}
        showLints={showLints}
        instructions={instructions}
        testSpec={testSpec}
        handleInstructionsChange={handleInstructionsChange}
        handleTestSpecChange={handleTestSpecChange}
        handleBoilerplateChange={this.updateCode}
        handleLintedBoilerplateCode={this.handleLintedCode}
        handleReset={handleReset}
      />
    ) : (
      <div>
        <CodeTesterHeader headerText={headerText} subHeaderText={subHeaderText}/>
        <Editor
          code={initialCode}
          onValidCode={this.handleLintedCode}
          onAnyCode={this.updateCode}
          showLints={showLints}
          handleReset={handleReset}
          hideButtons={hideEditorButtons}
        />
      </div>
    );
  }

  render() {
    const { 
      lintedCode, 
      verticalPanes, 
      consoleStyle, 
      testVisible,
      selectedTabIndex,
    } = this.state;
    const {
      onTestResults,
      testSpec,
      instructions,
      autoRun,
      iFrameUrl,
      testHandlerUrl,
      onError,
      infiniteLoopTimeout,
      submitEnabled,
      onTestRunnerSubmitClick,
      hideEditorButtons,
      markdownOptions,
      className,
      toggleEditMode,
      handleReset,
      editMode,
      handleUrlInput,
      handleGithubSubmit,
      committing
    } = this.props;
    return (
      <div className={`CodeTester ${className}`}>
        <InstructionsPanel
          handleUrlInput={handleUrlInput}
          handleGithubSubmit={handleGithubSubmit}
          editMode={editMode}
          tabIndex={selectedTabIndex}
          onTabSelect={this.onTabSelect}
          instructions={instructions}
          markdownOptions={markdownOptions}
          testSpec={testSpec}
          lintedCode={lintedCode}
          onTestResults={onTestResults}
          handleConsoleLog={this.handleConsoleLog}
          autoRun={autoRun}
          iFrameUrl={iFrameUrl}
          testHandlerUrl={testHandlerUrl}
          onError={onError}
          infiniteLoopTimeout={infiniteLoopTimeout}
          submitEnabled={submitEnabled}
          onTestRunnerSubmitClick={onTestRunnerSubmitClick}
          toggleEditMode={toggleEditMode}
          handleReset={handleReset}
          onTestRunnerRef={ref => this.TestRunnerRef = ref}
          committing={committing}
        />
        <div className="right-code">
          <div className="console-panel">
            <Console
              style={consoleStyle}
              logs={this.state.consoleLogs}
              onToggleConsole={this.handleToggleConsole}
              show={this.state.consoleVisible}
            />
          </div>
          <div className="right-panel-main">
            {this.renderEditorPanel()}
          </div>
          <div className="test-panel">
            <button className="btn btn-lg btn-info" onClick={this.handleRunClick}>Run</button>
            <button className="btn btn-lg btn-success" onClick={onTestRunnerSubmitClick}>Submit</button>
          </div>
        </div>
      </div>
    );
  }
}

CodeTester.propTypes = {
  autoRun: PropTypes.bool,
  editMode: PropTypes.bool,
  toggleEditMode: PropTypes.func,
  handleUrlInput: PropTypes.func,
  handleGithubSubmit: PropTypes.func,
  handleBoilerplateChange: PropTypes.func,
  handleInstructionsChange: PropTypes.func,
  handleReset: PropTypes.func,
  handleTestSpecChange: PropTypes.func,
  infiniteLoopTimeout: PropTypes.number,
  initialCode: PropTypes.string,
  initialHorizontalPaneSizes: PropTypes.arrayOf(PropTypes.number),
  instructions: PropTypes.string,
  iFrameUrl: PropTypes.string.isRequired,
  onAnyCode: PropTypes.func,
  onError: PropTypes.func,
  onLintedCode: PropTypes.func,
  onTestResults: PropTypes.func,
  onTestRunnerSubmitClick: PropTypes.func,
  showConsole: PropTypes.bool,
  showLints: PropTypes.bool,
  submitEnabled: PropTypes.bool,
  testSpec: PropTypes.string.isRequired,
  testHandlerUrl: PropTypes.string,
  className: PropTypes.string,
  hideEditorButtons: PropTypes.bool,
  markdownOptions: PropTypes.object,
  headerText: PropTypes.string,
  subHeaderText: PropTypes.string,
  committing: PropTypes.bool
};


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

export default CodeTester;
