<template>
  <div class="vue-codemirror">
    <textarea :id="id" :name="id" :homeurl="homeurl" cols="100" rows="14" :value="content">
    </textarea>
  </div>
</template>

<script>
import util from '../common/util.js'

const JS_HOME = './lib/codemirror/'
let inc = 0

export default {
  name: 'CodeMirror',
  props: {
    content: {
      type: String
    },
    homeurl: {
      // NEditor js 所在目录的路径
      type: String,
      required: false,
      default: () => JS_HOME
    },
    mode: {
      type: String,
      required: false
    },
    config: {
      // NEditor 配置项
      type: Object,
      required: false,
      default: () => {
        return {
        }
      }
    },
    insertCode: {
      type: String
    }
  },
  data() {
    inc++
    return {
      id: `codemirror_${inc}`,
      instance: null,
      destroyed: false,
      defaultConfig: {
        lineNumbers: true,
        viewportMargin: Infinity
      }
    }
  },
  watch: {
    mode(val) {
      if (this.config.mode !== val) {
        this.config.mode = val
        this.instance.clearHistory()
        this.instance.setOption('mode', val)
      }
    },
    content(val) {
      if (this.instance) {
        let html = this.instance.getValue()
        if (val !== html) {
          this.instance.setValue(val)
        }
      }
    },
    insertCode(val) {
      if (this.instance) {
        if (val !== '') {
          this.instance.deleteH()
          this.instance.replaceRange(val, this.instance.getCursor())
          this.instance.focus()
          this.$emit('update:insertCode', '')
        }
      }
    }
  },
  async created() {
    let self = this

    if (typeof window.CodeMirror === 'undefined') {
      util.loadCSS(this.homeurl + 'codemirror.css')
      util.loadCSS(this.homeurl + 'addon/hint/show-hint.css')
      try {
        await util.loadJS(this.homeurl + 'codemirror.js')
      } catch (e) {
        console.error('codemirror.js is missing', e.toString())
      }

      Promise.all([
        util.loadJS(this.homeurl + 'addon/selection/selection-pointer.js'),
        util.loadJS(this.homeurl + 'addon/edit/continuelist.js'),
        util.loadJS(this.homeurl + 'addon/edit/matchbrackets.js'),
        util.loadJS(this.homeurl + 'addon/comment/continuecomment.js'),
        util.loadJS(this.homeurl + 'addon/comment/comment.js'),
        util.loadJS(this.homeurl + 'mode/xml/xml.js'),
        util.loadJS(this.homeurl + 'mode/xml/xml.js'),
        util.loadJS(this.homeurl + 'addon/hint/show-hint.js'),
        util.loadJS(this.homeurl + 'addon/hint/css-hint.js'),
        util.loadJS(this.homeurl + 'mode/javascript/javascript.js'),
        util.loadJS(this.homeurl + 'mode/htmlmixed/htmlmixed.js'),
        util.loadJS(this.homeurl + 'mode/markdown/markdown.js'),
        util.loadJS(this.homeurl + 'mode/css/css.js')
      ]).then(_ => {
        self.initEditor()
      }).catch(e => {
        console.error('codemirror.js is missing', e.toString())
      })
    } else {
      this.initEditor()
    }
  },
  methods: {
    initEditor() {
      // Vue 异步执行 DOM 更新，这样一来代码执行到这里的时候可能 template 里面的 script 标签还没真正创建
      // 所以，我们只能在 nextTick 里面初始化 CodeMirror
      this.$nextTick(() => {
        if (this.mode) {
          this.config.mode = this.mode
        }
        let opts = Object.assign({}, this.defaultConfig, this.config)
        this.instance = window.CodeMirror.fromTextArea(
          document.getElementById(this.id),
          opts
        )

        this.instance.on('change', () => {
          let html = this.instance.getValue()
          if (html !== this.content) {
            this.$emit('contentChange', html)
            this.$emit('update:content', html)
          }
        })

        //         警告： 该事件的触发非常频繁，不建议在该事件的处理过程中做重量级的处理
        //          this.instance.on('beforeSelectionChange' () => {
        //            this.$emit('beforeSelectionChange' this.instance)
        //         })

        this.instance.on('focus', () => {
          this.$emit('focus', this.instance)
        })

        this.instance.on('blur', () => {
          this.$emit('blur', this.instance)
        })
      })
    }
  },
  beforeDestroy() {
  }
}
</script>
<style>
.CodeMirror::after {
  content: '';
  display: table;
  clear: both;
}
</style>
