import PropTypes from 'prop-types'
import React from 'react'
import FormField from 'part:@sanity/components/formfields/default'
import Fieldset from 'part:@sanity/components/fieldsets/default'
import DefaultSelect from 'part:@sanity/components/selects/default'
import PatchEvent, {set, unset, setIfMissing} from '@sanity/form-builder/PatchEvent'
import AceEditor from 'react-ace'
import {get, has, xor} from 'lodash'
import fieldsetStyles from './Fieldset.css'
import styles from './Styles.css'

import 'brace/mode/text'
import 'brace/mode/javascript'
import 'brace/mode/json'
import 'brace/mode/jsx'
import 'brace/mode/markdown'
import 'brace/mode/css'
import 'brace/mode/html'

import 'brace/theme/tomorrow'

function compareNumbers(a, b) { // eslint-disable-line id-length
  return a - b
}

const SUPPORTED_LANGUAGES = [
  {title: 'JSX', value: 'jsx'},
  {title: 'JavaScript', value: 'javascript'},
  {title: 'JSON', value: 'json'},
  {title: 'Markdown', value: 'markdown'},
  {title: 'CSS', value: 'css'},
  {title: 'HTML', value: 'html'},
  {title: 'text', value: 'text'}
]

const ACE_SET_OPTIONS = {
  useSoftTabs: true,
  navigateWithinSoftTabs: true /* note only supported by ace v1.2.7 or higher */
}
const ACE_EDITOR_PROPS = {$blockScrolling: true}

export default class CodeInput extends React.Component {

  static propTypes = {
    type: PropTypes.object,
    level: PropTypes.number.isRequired,
    value: PropTypes.shape({
      _type: PropTypes.string,
      code: PropTypes.string,
      language: PropTypes.string,
      highlightedLines: PropTypes.array
    }),
    onChange: PropTypes.func
  }

  static defaultProps = {
    value: '',
    onChange() {}
  }

  state = {
    hasFocus: false
  }

  componentWillUnmount() {
    this.editor.removeListener('guttermousedown', this.handleGutterMouseDown)
  }


  handleCodeChange = code => {
    const {type, onChange} = this.props
    const path = ['code']

    const fixedLanguage = get(type, 'options.language')

    onChange(PatchEvent.from([
      setIfMissing({_type: type.name, language: fixedLanguage}),
      code ? set(code, path) : unset(path)
    ]))
  }

  handleToggleSelectLine = lineNumber => {
    const {type, onChange} = this.props
    const path = ['highlightedLines']
    const {highlightedLines} = this.props.value

    const highlightedLinesCleaned = xor(highlightedLines, [lineNumber]).sort(compareNumbers)

    onChange(PatchEvent.from([
      setIfMissing({_type: type.name, highlightedLines: []}),
      lineNumber ? set(highlightedLinesCleaned, path) : unset(path)
    ]))
  }

  handleGutterMouseDown = event => {
    const target = event.domEvent.target
    if (target.classList.contains('ace_gutter-cell')) {
      const row = event.getDocumentPosition().row
      this.handleToggleSelectLine(`${row}`)
    }
  }

  handleEditorLoad = editor => {
    this.editor = editor
    this.editor.focus()
    this.editor.on('guttermousedown', this.handleGutterMouseDown)
  }

  handleLanguageChange = item => {
    const {type, onChange} = this.props
    const path = ['language']
    onChange(PatchEvent.from([
      setIfMissing({_type: type.name}),
      item ? set(item.value, path) : unset(path)
    ]))
  }

  createMarkers = rows => {
    return rows.map(row => {
      return {
        startRow: Number(row),
        startCol: 0,
        endRow: Number(row),
        endCol: 500,
        className: styles.highlight,
        type: 'background',
        inFront: true
      }
    })
  }

  renderEditor = () => {
    const {value, type} = this.props
    const fixedLanguage = get(type, 'options.language')
    return (
      <AceEditor
        mode={value.language || fixedLanguage || 'text'}
        theme="tomorrow"
        width="100%"
        onChange={this.handleCodeChange}
        name={`${this._inputId}__aceEditor`}
        value={value.code || ''}
        markers={value.highlightedLines ? this.createMarkers(value.highlightedLines) : null}
        onLoad={this.handleEditorLoad}
        tabSize={2}
        setOptions={ACE_SET_OPTIONS}
        editorProps={ACE_EDITOR_PROPS}
      />

    )
  }

  render() {
    const {value, type, level} = this.props

    if (has(type, 'options.language')) {
      return (
        <Fieldset styles={fieldsetStyles} legend={type.title} description={type.description}>
          {
            this.renderEditor()
          }
        </Fieldset>
      )
    }

    const currentLanguage = (value && value.language) ? SUPPORTED_LANGUAGES.find(item => item.value === value.language) : null

    const languageField = type.fields.find(field => field.name === 'language')
    const languages = currentLanguage ? SUPPORTED_LANGUAGES : [{title: 'Select language'}].concat(SUPPORTED_LANGUAGES)

    return (
      <Fieldset legend={type.title} description={type.description}>
        <DefaultSelect
          label={languageField.type.title}
          onChange={this.handleLanguageChange}
          value={currentLanguage}
          items={languages}
          level={level + 1}
        />
        <FormField label={type.title} level={level + 1}>
          {
            this.renderEditor()
          }
        </FormField>
      </Fieldset>
    )
  }
}

@@ -1,15 +1,58 @@
 import 
+React, %7BPureComponent%7D from 'react'%0Aimport 
 PropType
@@ -73,34 +73,8 @@
 ypes
-'%0Aimport React from 'react
 '%0Aim
@@ -1313,14 +1313,12 @@
 nds 
-React.
+Pure
 Comp

Make code-input a pure component