<!--
 * @Description:
 * @Author: 梁平贤
 * @LastEditors: 梁平贤
 * @Date: 2019-10-14 16:25:32
 * @LastEditTime: 2019-10-28 14:40:47
 -->
<template>
  <div class="en-text-config" @click="editor.focus()">
    <textarea ref="mycode" v-model="code" class="code"></textarea>
  </div>
</template>

<script>
import "codemirror/theme/ambiance.css";
import "codemirror/lib/codemirror.css";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/edit/closebrackets.js";
import "./calculator/formula.js";
import "./text/text.js";
import deepCopy from "deepcopy";
// import generateUUID from "../../../methods/uuid.js";
import { v4 as uuidv4 } from "uuid";

const CodeMirror = require("codemirror/lib/codemirror");
require("codemirror/addon/edit/matchbrackets");
require("codemirror/addon/selection/active-line");
require("codemirror/addon/hint/show-hint");

const FiledClassName = "cm-field-only";

export default {
  name: "EnTextConfig",
  props: {
    // 获取字段名称的key,默认为name,需要包含在传入的attribute中
    fieldNameKey: {
      type: String,
      default: "name"
    },
    // 初始化的代码 需要转换展示为用户能看懂的
    initialCode: {
      type: String,
      default: ""
    },
    // 初始化的字段信息
    initialFields: {
      type: Object,
      default: function() {
        return {};
      }
    },
    // 编辑器的高度 默认300高 .. 暂时先不用
    editorHeight: {
      type: [Number, String],
      default: "300px"
    },
    // 模式,暂时提供两种,一种是计算公式,一种是普通的文本配置
    mode: {
      type: String,
      default: "normal",
      invalidator: function(val) {
        return ["normal", "calculator"].indexOf(val) > -1;
      }
    }
  },
  data() {
    return {
      code: "",
      editor: null,
      fields: {}
    };
  },
  mounted() {
    // 初始化代码编辑器
    this.init();
    // 获取初始化的字段,复制一份,可能会修改
    this.fields = deepCopy(this.initialFields);
    // 将初始化的代码写入编辑器
    this.writeContentToCodeMirror(this.initialCode);
  },
  methods: {
    // 在光标位置加入函数
    addMethod(method) {
      var that = this;
      that.editor.replaceSelection(method + "(");
      var d = that.editor.getCursor();
      that.editor.replaceSelection(")");
      that.editor.setCursor(d);
      that.editor.focus();
    },
    // 在光标位置添加字段并标记为块
    addField(attribute) {
      // 获取光标位置
      const c = this.editor.getCursor();
      // 记录插入的字段信息
      // let uuid = generateUUID();
      let uuid = uuidv4()
      // 在uuid前插入$,在后插入#
      uuid = `$${uuid}#`;
      // 插入字段
      this.editor.replaceSelection("" + attribute[this.fieldNameKey] + "");
      // 获取当前光标位置
      var d = this.editor.getCursor();
      this.editor.focus();
      this.fields[uuid] = attribute;
      const dom = document.createElement("span");
      dom.setAttribute("data-id", uuid);
      dom.className = FiledClassName;
      dom.innerText = attribute[this.fieldNameKey];
      this.editor.markText(c, d, {
        handleMouseEvents: true,
        atomic: true,
        replacedWith: dom
      });
    },
    // 数据填充进编辑器
    writeContentToCodeMirror(code) {
      // 需要被替换成块的内容
      const convertObject = this.convertToShow(code);
      this.code = convertObject.code;
      this.editor.setValue(this.code);
      // 循环把块标记出来并且把属性挂载在标签上
      convertObject.markTexts.forEach(ele => {
        const c = CodeMirror.Pos(ele.lineCount, ele.location);
        const d = CodeMirror.Pos(ele.lineCount, ele.location + ele.length);
        const dom = document.createElement("span");
        dom.setAttribute("data-id", ele.attribute["data-id"]);
        dom.className = FiledClassName;
        dom.innerText = ele.attribute.name;
        this.editor.markText(c, d, {
          handleMouseEvents: true,
          atomic: true,
          replacedWith: dom
        });
      });
    },
    // 将全部字符串转换为展示需要的样式
    convertToShow(convertCode) {
      const codeArray = convertCode.split("\n");
      // 最终展示的代码数组,被\n分割的
      const showCodes = [];
      // 待标记的代码
      const markTexts = [];
      for (let index = 0; index < codeArray.length; index++) {
        const lineCode = codeArray[index];
        const array = [...lineCode];
        const resultArray = [...lineCode];
        // 记录查找$的位置
        let leftIndex = -1;
        // 记录查找到的#的位置
        let rightIndex = -1;
        // 记录当前替换字符串的整体位置偏移量
        let offset = 0;
        for (let i = 0; i < array.length; i++) {
          if (array[i] === "$") {
            leftIndex = i;
          }
          if (array[i] === "#") {
            // 如果$#都存在 那就认为这应该是一个字段
            if (leftIndex === -1) {
              continue;
            }
            rightIndex = i;
            // 获取该替换成的名称
            const fieldKey = lineCode.substring(leftIndex, rightIndex + 1);
            const fieldName = this.fields[fieldKey][this.fieldNameKey];
            // result实时处理
            resultArray.splice(
              leftIndex + offset,
              rightIndex - leftIndex + 1,
              ...fieldName
            );
            // 将当前位置的替换以及位置信息记录下来
            markTexts.push({
              location: leftIndex + offset,
              length: fieldName.length,
              lineCount: index,
              attribute: {
                "data-id": fieldKey,
                name: fieldName
              }
            });
            // 计算偏移量
            offset += fieldName.length - fieldKey.length;
            // 重置
            leftIndex = -1;
            rightIndex = -1;
          }
        }
        const lastCode = resultArray.join("");
        showCodes.push(lastCode);
      }
      return {
        code: showCodes.join("\n"),
        markTexts: markTexts
      };
    },
    // 解析输入框里面的内容 将返回固定格式的信息
    parsingEditor() {
      // CodeMirror-code 有多行 一行里面内嵌了多个span 所以两次嵌套循环处理
      const lines = this.editor.display.lineDiv;
      let textContent = "";
      Array.prototype.forEach.call(lines.childNodes, ele => {
        console.log(1111);
        if (textContent.length > 0) {
          // 到了2+以上的行需要添加换行符 用以回显
          textContent += "\n";
        }
        const doms = ele.querySelector("span").childNodes;
        Array.prototype.forEach.call(doms, ele1 => {
          console.log(2222);
          if (ele1.classList && ele1.classList.contains("CodeMirror-widget")) {
            if (
              ele1.childNodes &&
              ele1.childNodes[0].classList.contains(FiledClassName)
            ) {
              // 是字段的话拿到真实的字段信息放到字符串中
              const dataId = ele1.childNodes[0].getAttribute("data-id");
              textContent += dataId;
            }
          } else {
            // 会有莫名其妙的字符&#8203;
            textContent += ele1.textContent.replace(/\u200B/g, "");
          }
        });
      });
      // 循环fields,把不在textContent里面的内容给干掉....(因为可能手动删除了一些字段,但是字段信息是没有清理掉的)
      const tmpFields = { ...this.fields };
      Object.keys(tmpFields).forEach(key => {
        if (textContent.indexOf(key) === -1) {
          delete this.fields[key];
        }
      });
      // 返回存储,下次可以用它回显
      return {
        code: textContent,
        fields: this.fields
      };
    },
    // 清空所有输入
    clear() {
      this.editor.setValue("");
      this.editor.clearHistory();
    },
    /**
     * 自动插入（）
     * @return {[type]} [description]
     */
    insertBarcket() {
      var that = this;
      that.editor.replaceSelection("(");
      var d = that.editor.getCursor();
      that.editor.replaceSelection(")");
      that.editor.setCursor(d);
      that.editor.focus();
    },
    init() {
      this.editor = CodeMirror.fromTextArea(this.$refs.mycode, {
        mode:
          this.mode === "calculator" ? "text/x-formula" : "text/x-enfrytext", // 编辑器模式
        indentWithTabs: true,
        smartIndent: true,
        lineNumbers: false,
        dragDrop: false,
        matchBrackets: this.mode === "calculator",
        // 自动补全括号
        autoCloseBrackets: this.mode === "calculator",
        // autofocus: true,
        extraKeys: { Ctrl: "autocomplete" } // 自定义快捷键
      });
      // this.editor.setSize("auto","auto");
      const that = this;
      if (this.mode === "calculator") {
        // 计算公式的自动提示,编辑时提示
        this.editor.on("change", function(cm, event) {
          if (event.origin !== "complete") {
            // 未选中
            cm.showHint({
              hint: CodeMirror.hint.formula,
              completeSingle: false,
              shown: function() {
                console.log("显示了");
              },
              select: function(cpt, ele) {
                console.log(cpt, ele);
              },
              pick: function(item) {
                console.log(item);
              }
            });
          } else {
            that.insertBarcket();
          }
        });
      }
      this.editor.addKeyMap({
        Backspace: function(a) {
          var b = a.getTokenAt(a.getCursor());
          if (b.type === "field") {
            var c = a.getCursor().line;
            a.setSelection(
              new CodeMirror.Pos(c, b.start),
              new CodeMirror.Pos(c, b.end)
            );
            a.replaceSelection("", null, "+delete");
          } else {
            a.execCommand("delCharBefore");
          }
        }
      });
    }
  },
  destroy() {
    // garbage cleanup
    const element = this.editor.doc.cm.getWrapperElement();
    element && element.remove && element.remove();
  }
};
</script>
<style>
.cm-s-default {
  line-height: 25px !important;
  font-size: 12px;
}
</style>
<style scoped lang="scss">
.en-text-config {
  width: 100%;
  height: 100%;
  overflow-y: auto;

  & /deep/ .cm-field-only {
    background-color: #e1f2fa;
    padding: 2px 6px;
    border-radius: 3px;
    margin-left: 2px;
    margin-right: 2px;
    font-size: 12px;
    color: #4694df;
    height: 22px;
    display: inline-block;
    line-height: 19px;
  }
}
</style>
<style>
.CodeMirror-hints {
  z-index: 999999 !important;
}
</style>
