<template>
  <div class="input-obj-table-box">
    <el-form
      ref="ruleFormRef"
      class="ruleForm"
      :model="tableRows"
      :size="formSize"
      :rules="formRules"
      status-icon
    >
      <bg-table-pro
        :headers="headers"
        :data="tableRows"
        ref="inputTable"
        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
        row-key="id"
        default-expand-all
        :border="true"
        :stripe="false"
      >
        <template v-slot:paramName="{ row }">
          <el-form-item
            :prop="`${row.$row_path}.paramName`"
            :rules="row.$parent_type === 'array'?formRules.paramName1:formRules.paramName"
          >
            <el-input
              v-model="row.paramName"
              :placeholder="row.$parent_type === 'array' ? `` : `请输入`"
              :disabled="disabled||row.$parent_type === 'array'"
            />
          </el-form-item>
        </template>
        <template v-slot:paramType="{ row }">
          <el-form-item
            :prop="`${row.$row_path}.paramType`"
            :rules="formRules.paramType"
          >
            <el-select
              v-model="row.paramType"
              :disabled="disabled"
              @change="changeInputRowType(row)"
              placeholder="请选择"
            >
              <el-option label="string" value="string" />
              <el-option label="int" value="int" />
              <el-option label="array" value="array" />
              <el-option label="boolean" value="boolean" />
              <el-option label="object" value="object" />
            </el-select>
          </el-form-item>
        </template>
        <template v-slot:paramValue="{ row }">
          <el-form-item
            :prop="`${row.$row_path}.paramValue`"
            :rules="
              row.paramType === 'object' || row.paramType === 'array'
                ? formRules.paramValue1
                : formRules.paramValue
            "
          >
            <el-select
              v-model="row.paramValue"
              :disabled="disabled"
              v-if="row.paramType === 'boolean'"
              :placeholder="
                row.paramType === 'object' || row.paramType === 'array'
                  ? ``
                  : `请选择`
              "
            >
              <el-option label="true" :value="true" />
              <el-option label="false" :value="false" />
            </el-select>
            <el-input
              v-model="row.paramValue"
              :disabled="
                disabled ||
                row.paramType === 'object' ||
                row.paramType === 'array'
              "
              :placeholder="
                row.paramType === 'object' || row.paramType === 'array'
                  ? ``
                  : `请输入`
              "
              v-else
            />
          </el-form-item>
        </template>
        <template v-slot:desc="{ row }">
          <el-form-item :prop="`${row.$row_path}.desc`" :rules="formRules.desc">
            <el-input v-model="row.desc" :disabled="disabled" />
          </el-form-item>
        </template>

        <template v-slot:action="{ row }">
          <bg-table-btn  class="can_click_btn" :disabled="disabled" :click="()=>{addInputConf(row)}"> 增加 </bg-table-btn>
          <bg-table-btn  class="can_click_btn" :disabled="disabled" :click="()=>{removeInputConf(row)}"> 删除 </bg-table-btn>
        </template>
      </bg-table-pro>
    </el-form>

    <el-dialog
      title="编辑结构体"
      v-model="jsonEditorVisible"
      width="920px"
    >
      <div style="width: 100%; height: 400px">
        <bg-code-editor
          v-if="jsonEditorVisible"
          :disabled="disabled"
          class="bg-ace-editor"
          v-model="state.jsonStr"
        ></bg-code-editor>
      </div>
      <template v-slot:footer>
        <el-button @click="jsonEditorVisible = false"> 取消 </el-button>
        <el-button
          v-if="!disabled"
          type="primary"
          @click="confirmJson"
          :loading="confirmLoading"
        >
          确定
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import {
  reactive,
  toRefs,
  onBeforeMount,
  onMounted,
  ref,
  getCurrentInstance,
} from "vue";
import { v4 as uuidv4 } from "uuid";
import { ElMessage } from "element-plus";
const props = defineProps({
  data: {
    type: Array,
    default: () => [],
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  type: {
    type: String,
    default: "json",
  },
});
const formSize = ref("default");
const ruleFormRef = ref(null);
const inputTable = ref(null);

const checkParamName = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("变量名不能为空"));
  } else {
    if (value.length > 30) {
      callback(new Error("最多30个字符"));
    } else {
      callback();
    }
  }
};
const formRules = reactive({
  paramName: [{ validator: checkParamName, trigger: "blur" }],
  paramName1:[],
  paramType: [
    {
      required: true,
      message: "参数类型不能为空",
      trigger: ["change"],
    },
  ],
  paramValue: [
    {
      required: true,
      message: "值不能为空",
      trigger: ["blur", "change"],
    },
    {
      max: 30,
      message: "最多30个字符",
      trigger: ["blur", "change"],
    },
  ],
  paramValue1: [
    {
      max: 30,
      message: "最多30个字符",
      trigger: ["blur", "change"],
    },
  ],
  desc: [
    {
      max: 50,
      message: "最多50个字符",
      trigger: "blur",
    },
  ],
});

const headers = reactive([
  {
    label: "参数名称",
    prop: "paramName",
  },
  {
    label: "参数类型",
    prop: "paramType",
  },
  {
    label: "示例",
    prop: "paramValue",
  },
  {
    label: "说明",
    prop: "desc",
  },
  {
    label: "操作",
    prop: "action",
    width: 120,
  },
]);

const state = reactive({
  ruleFormRef,
  inputTable,
  jsonEditorVisible: false,
  confirmLoading: false,
  tableRows: [],
  jsonStr: "{}",
});

onBeforeMount(() => {
  initTable();
});

var initTable = () => {
  if (props.data && props.data.length >= 1) {
    state.tableRows = props.data;
  } else {
    addInputConf();
  }
};

var createRow = () => {
  return {
    id: uuidv4(),
    paramName: "",
    paramType: "string",
    paramValue: "",
    desc: "",
  };
};

var editInputParamsAsJson = () => {
  //table转成json字符串
  var tableStr = translateToJsonStr(state.tableRows);
  if(props.type=='xml'){
    state.jsonStr = json2xml(JSON.parse(tableStr))
  }else if(props.type=='json'){
    state.jsonStr = tableStr
  }
  state.jsonEditorVisible = true;
}; // 编辑JSON

//json对象转xml字符串
var json2xml=(o, tab)=> {
   var toXml = function(v, name, ind) {
      var xml = "";
      if (v instanceof Array) {
         for (var i=0, n=v.length; i<n; i++)
            xml += ind + toXml(v[i], name, ind+"\t") + "\n";
      }
      else if (typeof(v) == "object") {
         var hasChild = false;
         xml += ind + "<" + name;
         for (var m in v) {
            if (m.charAt(0) == "@")
               xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
            else
               hasChild = true;
         }
         xml += hasChild ? ">" : "/>";
         if (hasChild) {
            for (var m in v) {
               if (m == "#text")
                  xml += v[m];
               else if (m == "#cdata")
                  xml += "<![CDATA[" + v[m] + "]]>";
               else if (m.charAt(0) != "@")
                  xml += toXml(v[m], m, ind+"\t");
            }
            xml += (xml.charAt(xml.length-1)=="\n"?ind:"") + "</" + name + ">";
         }
      }
      else {
         xml += ind + "<" + name + ">" + v.toString() +  "</" + name + ">";
      }
      return xml;
   }, xml="";
   for (var m in o)
      xml += toXml(o[m], m, "");
   return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
}

//数组转json字符串
var translateToJsonStr = (arr) => {
  let recursionItems = (items, isArr = false) => {
    let result = isArr ? [] : {};

    items.forEach((item) => {
      let _prop = item["paramName"];
      let _type = item["paramType"];
      let _value = item["paramValue"];

      if (_prop || isArr) {
        switch (_type) {
          case "int":
            _value = parseInt(_value) || 0;
            break;
          case "array":
            _value = recursionItems(item.children, true);
            break;
          case "boolean":
            _value = _value || false;
            break;
          case "object":
            _value = recursionItems(item.children);
            break;
          default:
            _value = _value + "";
            break;
        }

        if (isArr) {
          result.push(_value);
        } else {
          result[_prop] = _value;
        }
      }
    });

    return result;
  };

  return JSON.stringify(recursionItems(arr), null, "\t");
}; // 对象数组转换为json字符串

// 判断是否是xml
var isXml = (str) => {
  var xmlDoc= null;
  try //Internet Explorer
  {
    xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.async=false;
    xmlDoc.loadXML(str);
  }
  catch(e)
  {
   try //Firefox, Mozilla, Opera, etc.
      {
       xmlDoc=document.implementation.createDocument("","",null);
      xmlDoc.async=false;
      xmlDoc.load(str);
      }
     catch(e) {}
  }
  return(xmlDoc.xml != "");
};

//字符串转为xmldom
var loadXml = (xmlString) => {
  var xmlDoc = null;
  if (window.DOMParser) {
    var parser = new DOMParser();
    xmlDoc = parser.parseFromString(xmlString, "text/xml");
  } else {
    //IE
    xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.async = "false";
    xmlDoc.loadXML(xmlString);
  }
  return xmlDoc;
};

//xmldom转json字符串
var xmlTojson = (xmlDoc) => {
  //准备JSON字符串和缓存（提升性能）
  var jsonStr = "";
  var buffer = new Array();

  buffer.push("{");
  //获取xml文档的所有子节点
  var nodeList = xmlDoc.childNodes;

  generate(nodeList);

  /**
   * 中间函数，用于递归解析xml文档对象，并附加到json字符串中
   * @param node_list xml文档的的nodeList
   */
  function generate(node_list) {
    for (var i = 0; i < node_list.length; i++) {
      var curr_node = node_list[i];
      //忽略子节点中的换行和空格
      if (curr_node.nodeType == 3) {
        continue;
      }
      //如果子节点还包括子节点，则继续进行遍历
      if (curr_node.childNodes.length > 1) {
        buffer.push('"' + curr_node.nodeName + '": {');
        generate(curr_node.childNodes);
      } else {
        var firstChild = curr_node.childNodes[0];

        if (firstChild != null) {
          //nodeValue不为null
          buffer.push(
            '"' + curr_node.nodeName + '":"' + firstChild.nodeValue + '"'
          );
        } else {
          //nodeValue为null
          buffer.push('"' + curr_node.nodeName + '":""');
        }
      }
      if (i < node_list.length - 2) {
        buffer.push(",");
      } else {
        break;
      }
    }
    //添加末尾的"}"
    buffer.push("}");
  }

  jsonStr = buffer.join("");
  return jsonStr;
};

var changeInputRowType = (row) => {
  delete row["children"];

  if (row.paramType === "object" || row.paramType === "array") {
    row.paramValue = "";
    addInputConfChild(row);
  } else if (row.paramType === "int") {
    row.paramValue = 0;
  } else if (row.paramType === "boolean") {
    row.paramValue = true;
  } else {
    row.paramValue = "";
  }
}; //修改参数类型

var addInputConfChild = (row) => {
  if (!row.children) {
    row.children = [];
  }

  row.children.push({
    ...createRow(),
    // paramName: row.paramType === "array" ? 0 : "",
  });

  updataInputConfRows();
}; // 增加子项

var updataInputConfRows = () => {
  let recursionItems = (items, parentType, parentPath) => {
    for (let i = 0; i < items.length; i++) {
      let item = items[i];

      item.$parent_type = parentType;
      item.$row_path = `${parentPath}[${i}]`;

      if (item.children && item.children.length) {
        let basePath = `${item.$row_path}.children.`;

        recursionItems(item.children, item.paramType, basePath);
      }
    }
  };

  recursionItems(state.tableRows, "", "");

  state.ruleFormRef && state.ruleFormRef.clearValidate();
}; // 更新 $parent_type 和 $row_path 字段

var addInputConf = (row) => {
  let tempRow = createRow();

  if (row) {
    let { index, rows } = getInputRowInfo(row);

    rows.splice(index + 1, 0, tempRow);
  } else {
    state.tableRows = [tempRow];
  }

  updataInputConfRows();
}; //增加
var getInputRowInfo = (row) => {
  return state.inputTable && state.inputTable.getRowInfo(row, `id`);
}; //获取当前行的数据信息
var removeInputConf = (row) => {
  let { index, rows } = getInputRowInfo(row);

  rows.splice(index, 1);

  if (rows.length === 0) {
    rows.push(createRow());
  }

  updataInputConfRows();
}; // 删除

var isJSON = (str) => {
  if (typeof str == "string") {
    try {
      var obj = JSON.parse(str);

      if (typeof obj == "object" && obj) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      console.log("error：" + str + "!!!" + e);

      return false;
    }
  }

  console.log("It is not a string!");
}; //判断是否是json

var confirmJson = () => {
  console.log(state.jsonStr);
  ElMessage.warning("数据处理中，请稍后！");
  state.confirmLoading = true;
  setTimeout(() => {
    var transStr = ''
    if (props.type == "xml") {
      if (isXml(state.jsonStr)) {
        var xmlDOM = loadXml(state.jsonStr);
        var xmlJSON = xmlTojson(xmlDOM);
        transStr = xmlJSON
      } else {
        ElMessage.closeAll();
        state.confirmLoading = false;
        ElMessage.error("请输入正确xml字符串");
        return
      }
    }else if(props.type == "json"){
      if (isJSON(state.jsonStr)) {
        transStr = state.jsonStr
      } else {
        ElMessage.closeAll();
        state.confirmLoading = false;
        ElMessage.error("请输入正确的数据格式");
        return
      }
    }
    let rows = translateToArray(transStr);
    state.tableRows = rows;
    if (state.tableRows.length === 0) {
      addInputConf();
    }
    ElMessage.closeAll();
    state.confirmLoading = false;
    state.jsonEditorVisible = false;
  }, 100);
}; //点击确认，转化json

var translateToArray = (jsonStr) => {
  let recursionItems = (items, parentType, parentPath) => {
    let result = [];
    let index = 0;

    for (let prop in items) {
      let temp = {};
      let type = getDataType(items[prop]);

      temp = createRow();

      temp.$parent_type = parentType;
      temp.$row_path = `${parentPath}[${index}]`;
      temp.paramName = parentType === "array" ? "" : prop;
      temp.paramType = type;

      if (type === "object") {
        let path = `${temp.$row_path}.children.`;

        temp.paramType = type;
        temp.children = recursionItems(items[prop], type, path);
      } else if (type === "array") {
        let path = `${temp.$row_path}.children.`;

        temp.paramType = type;
        temp.children = recursionItems([items[prop][0]], type, path); // 数 组只保留第一条数据，用作示例
      } else {
        temp.paramValue = items[prop];
      }

      result.push(temp);

      index++;
    }

    return result;
  };

  return recursionItems(JSON.parse(jsonStr), "", "");
}; //json字符串转换为对象数组

var getDataType = (value) => {
  let type = Object.prototype.toString
    .call(value)
    .replace("[", "")
    .replace("]", "")
    .split(" ")[1];

  // debugger;

  switch (type) {
    case "Number":
      type = "int";
      break;
    case "Array":
      type = "array";
      break;
    case "Boolean":
      type = "boolean";
      break;
    case "Object":
      type = "object";
      break;
    default:
      type = "string";
      break;
  }

  return type;
}; // 获取数据类型

//对数据做去重和填写校验筛选，只有筛选过的数据才会提交到后端
var getInputData = () => {
  //table转成字符串
  let tempStr = translateToJsonStr(state.tableRows);
  let tempArr = translateToArray(tempStr);
  //table转成字符串
  let tableStr = translateToJsonStr(tempArr);
  let jsonStr = ''
  if(props.type=='xml'){
    jsonStr = json2xml(JSON.parse(tableStr))
  }else if(props.type=='json'){
    jsonStr = tableStr
  }
  return {
    table:tempArr,
    json:jsonStr
  }
};

const { tableRows, jsonEditorVisible, confirmLoading } = toRefs(state);

//暴露获取数据方法
defineExpose({
  getInputData,
  editInputParamsAsJson,
});
</script>

<style scoped>
.input-obj-table-box {
  width: 100%;
  height: 100%;
}

.input-obj .el-dialog__body{
  padding: 0;
}
</style>
