import React, { useEffect, useRef, useState } from 'react'
import * as monaco from 'monaco-editor'
import './TPIMonacoConfig'
import './monaco-editor.css'

function processSize(size) {
  return !/^\d+$/.test(size) ? size : `${size}px`
}
function noop() { }
let __prevent_trigger_change_event = false

function debounce(func, wait, immediate) {
  var timeout
  return function () {
    var context = this, args = arguments
    var later = function () {
      timeout = null
      if (!immediate) func.apply(context, args)
    };
    var callNow = immediate && !timeout
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)
    if (callNow) func.apply(context, args)
  }
}



const mirror2LanguageMap = {
  'JFinal': 'java',
  'Java': 'java',
  'JavaWeb': 'java',
  'Kotlin': 'java',

  'Html': 'html',
  'Css': 'css',
  'Javascript': 'javascript',
  'JavaScript': 'javascript',

  'C/C++': 'cpp',
  'MachineLearning': 'python',
  'Python2.7': 'python',
  'Python3.6': 'python',

  'C#': 'csharp',
  'R': 'r'

}
function getLanguageByMirrorName(mirror_name) {
  let lang = 'javascript'
  if (mirror_name && mirror_name.length) {
    // 需要倒着遍历， html、css的实训，主评测语言环境是python，小类别是html或css
    for (let i = mirror_name.length - 1; i >= 0; i--) {
      let languageVal = mirror2LanguageMap[mirror_name[i]]
      if (languageVal) {
        lang = languageVal
        break
      }
    }
  }
  return lang
}


export default ({
  width = '100%',
  height = '100%',
  value,
  language = 'javascript',
  options = {},
  overrideServices = {},
  theme = 'vs-dark',
  onChange = noop,
  editorDidMount = noop
}) => {
  const editorEl = useRef()
  const editor = useRef({})
  const [init, setInit] = useState(false)

  function onLayout() {
    if (window.ResizeObserver) {
      const ro = new window.ResizeObserver(entries => {
        for (let entry of entries) {
          if (entry.target.offsetHeight > 0) {
            editor.current.instance.layout()
          }
        }
      })
      ro.observe(editorEl.current.parentElement)
    } else {
      setTimeout(() => {
        editor.current.instance.layout()
      }, 100);
    }
  }

  useEffect(() => {
    editor.current.instance = monaco.editor.create(
      editorEl.current, {
      value,
      language: getLanguageByMirrorName(language),
      theme,
      ...options
    },
      overrideServices
    )
    editorDidMount(editor.current.instance, monaco)
    editor.current.subscription = editor.current.instance.onDidChangeModelContent(event => {
      if (!__prevent_trigger_change_event) {
        onChange(editor.current.instance.getValue(), event);
      }
    })
    setInit(true)

    const resizeHandler = debounce(() => { editor.current.instance.layout() }, 100)
    window.addEventListener('resize', resizeHandler)
    onLayout()
    return () => {
      const el = editor.current.instance
      el.dispose()
      const model = el.getModel()
      if (model) {
        model.dispose()
      }
      if (editor.current.subscription) {
        editor.current.subscription.dispose()
      }
      window.removeEventListener('resize', resizeHandler)
    }
  }, [editorEl.current])

  useEffect(() => {
    let instance = editor.current.instance
    if (instance && init) {
      __prevent_trigger_change_event = true
      const model = instance.getModel()
      instance.pushUndoStop()
      model.pushEditOperations(
        [],
        [
          {
            range: model.getFullModelRange(),
            text: value
          }
        ]
      )
      instance.pushUndoStop();
      __prevent_trigger_change_event = false;
    }
  }, [value, init])

  useEffect(() => {
    let instance = editor.current.instance
    if (instance && init) {
      monaco.editor.setModelLanguage(instance.getModel(), getLanguageByMirrorName(language))
    }
  }, [language, init])

  useEffect(() => {
    let instance = editor.current.instance
    if (instance && init) {
      monaco.editor.setTheme(theme)
    }
  }, [theme, init])

  useEffect(() => {
    let instance = editor.current.instance
    if (instance && init) {
      instance.updateOptions(options)
    }
  }, [options, init])

  useEffect(() => {
    let instance = editor.current.instance
    if (instance && init) {
      instance.layout()
    }
  }, [width, height, init])

  const fixedWidth = processSize(width)
  const fixedHeight = processSize(height)
  const style = {
    width: fixedWidth,
    height: fixedHeight
  }

  return (
    <div ref={editorEl} style={style} id="extend-challenge-file-edit" name="content" ></div>
  )
} 