import React, { useEffect, useRef, useState } from 'react'
import ResizeObserver from 'resize-observer-polyfill';
import './index.css'

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

const DICT = {
  "Python3.6": 'python',
  "Python2.7": 'python',
  "Dynamips": 'cpp',
  "Java": 'java',
  "Web": 'php',
  "Html": 'html',
  "Hive": 'sql',
  "Hadoop": 'java',
  "SDL": 'cpp',
  "PHP": 'php',
  "Matlab": 'python',
  "Git": 'python',
  "Python": 'python',
  "C/C++": 'cpp',
  "C++": 'cpp',
  "C": 'cpp',
  "Ruby": 'ruby',
  "Shell": 'shell',
  "JavaScript": 'javascript',
  "Perl6": 'perl',
  "Kotlin": 'kotlin',
  "Elixir": 'elixir',
  "Android": 'java',
  "JavaWeb": 'java',
  "Go": 'go',
  "Spark": 'sql',
  "MachineLearning": 'python',
  "Verilog": 'xml',
  "Verilog/VNC": 'xml',
  "Docker": 'dockerfile',
  "C#": 'csharp',
  "SQLite3": 'sql',
  "Oracle": 'sql',
  "Vhdl": 'vhdl',
  "R": 'r',
  "Swift": 'swift',
  "SQLServer": 'mysql',
  "MySQL": 'mysql',
  "Mongo": 'sql',
  "PostgreSql": 'pgsql',
  "Hbase": 'powershell',
  "Sqoop": "sql", "Nasm": 'cpp', "Kafka": 'java', "Flink": 'java', "Sml": 'javascript', "OpenGL": 'cpp', "Perl5": 'perl', "Orange": 'python'
}
export function getLanguageByMirrorName(mirror_name = []) {
  let lang = mirror_name
  if (Array.isArray(mirror_name)) {
    for (let i = 0; i < mirror_name.length; i++) {
      let languageVal = DICT[mirror_name[i]]
      if (languageVal) {
        return languageVal
      }
    }
    return lang[0]
  }
  return DICT[lang] || lang
}

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

  function onLayout() {
    let ro
    if (editorEl.current) {
      ro = new ResizeObserver(entries => {
        for (let entry of entries) {
          if (entry.target.offsetHeight > 0 || entry.target.offsetWidth > 0) {
            editor.current.instance.layout()
          }
        }
      })
      ro.observe(editorEl.current)
    }
    return ro
  } 

  useEffect(() => {
    let instance = editor.current.instance
    if (value != null && instance && init) {
      const model = instance.getModel()
      if (model && value !== model.getValue()) {
        __prevent_trigger_change_event = true
        model.setValue(value)
        instance.layout()
        __prevent_trigger_change_event = false;
      }
    }
  }, [value, init, editor.current])

  function onPaste() {
    let instance = editor.current.instance
    if (instance) {
      const selection = instance.getSelection()
      const pastePos = editor.current.pastePos || {}
      const range = new monaco.Range(pastePos.startLineNumber || selection.endLineNumber, pastePos.startColumn || selection.endColumn, pastePos.endLineNumber || selection.endLineNumber, pastePos.endColumn || selection.endColumn)
      instance.executeEdits('', [{ range, text: '' }])
    }
  }

  useEffect(() => {
    import(/* webpackChunkName: "monaco-editor" */ 'monaco-editor/esm/vs/editor/editor.api.js').then((mod) => {
      monaco = mod
      editor.current.instance = monaco.editor.create(
        editorEl.current, {
        value,
        language: getLanguageByMirrorName(language),
        theme,
        ...options
      },
        overrideServices
      )
      const instance = editor.current.instance
      editorDidMount(instance, monaco)

      editor.current.subscription = instance.onDidChangeModelContent(event => {
        if (!__prevent_trigger_change_event) {
          onChange(instance.getValue(), event);
        }
      })

      if (onEditBlur) {
        instance.onDidBlurEditorWidget(() => { onEditBlur(instance.getValue()) })
      }
      if (onFocus) {
        instance.onDidFocusEditorText(() => { onFocus(instance.getValue()) })
      }
      if (forbidCopy) {
        instance.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_V, () => null)
        instance.onDidPaste((pos) => { editor.current.pastePos = pos })
        window.addEventListener('paste', onPaste)
      }

      let ro = onLayout()
      setInit(true)

      return () => {
        const el = editor.current.instance
        el.dispose()
        const model = el.getModel()
        if (model) {
          model.dispose()
        }
        if (editor.current.subscription) {
          editor.current.subscription.dispose()
        }
        if (forbidCopy) {
          window.removeEventListener('paste', onPaste)
        }
        ro.unobserve(editorEl.current)
      }
    })
  }, [])

  useEffect(() => {
    let instance = editor.current.instance
    if (instance && init) {
      let lang = getLanguageByMirrorName(language)
      monaco.editor.setModelLanguage(instance.getModel(), lang)
    }
  }, [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 id='extend-challenge-file-edit' ref={editorEl} style={style}  ></div>
  )
}

export function DiffEditor({ width, height, original, modified, language, options = {} }) {
  const editorEl = useRef()

  useEffect(() => {
    if (editorEl.current) {
      import(/* webpackChunkName: "monaco-editor" */ 'monaco-editor/esm/vs/editor/editor.api.js').then((mod) => {
        monaco = mod
        editorEl.current.instance = monaco.editor.createDiffEditor(editorEl.current, {
          readOnly: true,
          ...options,
        })
      })
    }
  }, [editorEl.current])

  useEffect(() => {
    if (editorEl.current) {
      let instance = editorEl.current.instance
      instance.setModel({
        original: monaco.editor.createModel(original, language),
        modified: monaco.editor.createModel(modified, language)
      })
    }

  }, [original, modified, language, editorEl.current])

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

  return (
    <div ref={editorEl} style={style}  ></div>
  )
}