<template>
  <div class="json-schema-editor">
    <a-row class="row" :gutter="10">
      <a-col :span="8" class="ant-col-name">
        <div :style="{marginLeft:`${20*deep}px`}" class="ant-col-name-c">
          <a-button v-if="pickValue.type==='object'" type="link" :icon="hidden?'caret-right':'caret-down'" style="color:rgba(0,0,0,.65)" @click="hidden = !hidden" />
          <span v-else style="width:32px;display:inline-block"></span>
          <input :disabled="disabled || root" :value="pickKey" class="root-input" @blur="onInputName" />
        </div>
      </a-col>
      <a-col>
        <a-tooltip v-if="root">
          <span slot="title">全选</span>
          <a-checkbox :disabled="!isObject && !isArray" class="ant-col-name-required" @change="onRootCheck" />
        </a-tooltip>
        <a-tooltip v-else>
          <span slot="title">是否必填</span>
          <a-checkbox :disabled="isItem" :checked="checked" class="ant-col-name-required" @change="onCheck" />
        </a-tooltip>
      </a-col>
      <a-col :span="4">
        <a-select v-model="pickValue.type" :disabled="disabledType" class="ant-col-type" @change="onChangeType" :getPopupContainer="
          triggerNode => {
            return triggerNode.parentNode || document.body;
          }">
          <a-select-option :key="t" v-for="t in TYPE_NAME">
            {{t}}
          </a-select-option>
        </a-select>
      </a-col>
      <a-col :span="6">
        <a-input v-model="pickValue.description" class="ant-col-title" :placeholder="local['description']" />
      </a-col>
      <a-col :span="6" class="ant-col-setting">
        <a-tooltip v-if="isObject">
          <span slot="title">添加子节点</span>
          <a-button type="link" icon="plus" class="plus-icon" @click="addChild" />
        </a-tooltip>
        <a-tooltip v-if="!root && !isItem">
          <span slot="title">删除节点</span>
          <a-button type="link" class="close-icon ant-btn-icon-only" @click="removeNode">
            <i aria-label="icon: plus" class="anticon anticon-plus" style="color:#F56C6C">
              <svg viewBox="64 64 896 896" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
                <path d="M810.666667 273.493333L750.506667 213.333333 512 451.84 273.493333 213.333333 213.333333 273.493333 451.84 512 213.333333 750.506667 273.493333 810.666667 512 572.16 750.506667 810.666667 810.666667 750.506667 572.16 512z" p-id="1142"></path>
              </svg>
            </i>
          </a-button>
        </a-tooltip>
      </a-col>
    </a-row>
    <div v-if="!isArray&&isShow" class="choose-box">
      <a-input v-model="inputValue" size="small" style="width: 240px" ref="a" @change="handleChange($event)"></a-input>
      <p style="font-size:14px;margin-bottom:0;margin-left:10px">或
        <span style="font-size:12px;color:#8e74e7;cursor: pointer;" @click="dataDialog">引用数据模型</span>
      </p>
      <a-button type="link" class="close-icon ant-btn-icon-only" @click="isShow=false" style="margin-left:20px">
        <i aria-label="icon: plus" class="anticon anticon-plus" style="color:#F56C6C">
          <svg viewBox="64 64 896 896" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
            <path d="M810.666667 273.493333L750.506667 213.333333 512 451.84 273.493333 213.333333 213.333333 273.493333 451.84 512 213.333333 750.506667 273.493333 810.666667 512 572.16 750.506667 810.666667 810.666667 750.506667 572.16 512z" p-id="1142"></path>
          </svg>
        </i>
      </a-button>
    </div>
    <!-- 引入数据模型弹框 -->
    <a-modal title="模拟引用数据模型" :visible="visible" @ok="handleOk" @cancel="visible=false">
      <p>模拟引用数据模型</p>
    </a-modal>
    <template v-if="!hidden&&pickValue.properties && !isArray">
      <json-schema-editor v-for="(item,key,index) in pickValue.properties" :value="{[key]:item}" :parent="pickValue" :key="index" :deep="deep+1" :root="false" class="children" :lang="lang" :custom="custom" v-on="$listeners" />
    </template>
    <template v-if="isArray">
      <json-schema-editor :value="{items:pickValue.items}" :deep="deep+1" disabled isItem :root="false" class="children" :lang="lang" :custom="custom" v-on="$listeners" />
    </template>
  </div>
</template>
<script>
import Vue from 'vue'
import { isNull, debounce } from './util'
import { TYPE_NAME, TYPE } from './type/type'
import { Row, Col, Button, Input, Icon, Checkbox, Select, Tooltip, Modal } from 'ant-design-vue'
import LocalProvider from './LocalProvider'
Modal.install(Vue)
export default {
  name: 'JsonSchemaEditor',
  components: {
    ARow: Row, ACol: Col,
    AButton: Button,
    // eslint-disable-next-line vue/no-unused-components
    AIcon: Icon,
    AInput: Input,
    ACheckbox: Checkbox,
    ASelect: Select,
    ASelectOption: Select.Option,
    ATooltip: Tooltip,
  },
  props: {
    value: {
      type: Object,
      required: true
    },
    disabled: { //name不可编辑，根节点name不可编辑,数组元素name不可编辑
      type: Boolean,
      default: false
    },
    disabledType: { //禁用类型选择
      type: Boolean,
      default: false
    },
    isItem: { //是否数组元素
      type: Boolean,
      default: false
    },
    deep: { // 节点深度，根节点deep=0
      type: Number,
      default: 0
    },
    root: { //是否root节点
      type: Boolean,
      default: true
    },
    parent: { //父节点
      type: Object,
      default: null
    },
    custom: { //enable custom properties
      type: Boolean,
      default: false
    },
    lang: { // i18n language
      type: String,
      default: 'zh_CN'
    }
  },
  computed: {
    // 输入框防抖函数
    getData () {
      return debounce(this.addRow, 1000)
    },
    pickValue () {
      return Object.values(this.value)[0]
    },
    pickKey () {
      return Object.keys(this.value)[0]
    },
    isObject () {
      return this.pickValue.type === 'object'
    },
    isArray () {
      return this.pickValue.type === 'array'
    },
    checked () {
      return this.parent && this.parent.required && this.parent.required.indexOf(this.pickKey) >= 0
    },
    advanced () {
      return TYPE[this.pickValue.type]
    },
    advancedAttr () {
      return TYPE[this.pickValue.type].attr
    },
    ownProps () {
      return ['type', 'description', 'properties', 'items', 'required', ...Object.keys(this.advancedAttr)]
    },
    advancedNotEmptyValue () {
      const jsonNode = Object.assign({}, this.advancedValue);
      for (let key in jsonNode) {
        isNull(jsonNode[key]) && delete jsonNode[key]
      }
      return jsonNode
    },
    enumText () {
      const t = this.advancedValue['enum']
      if (!t) return ''
      if (!t.length) return ''
      return t.join('\n')
    }
  },
  data () {
    return {
      visible: false,
      inputValue: '',
      isShow: false,
      TYPE_NAME,
      hidden: false,
      countAdd: 1,
      addProp: {},
      customProps: [],
      customing: false,
      local: LocalProvider(this.lang)
    }
  },
  methods: {
    handleChange (e) {
      this.getData(e.target.value)
    },
    addRow (e) {
      if (e) {
        this.inputValue = ''
        this.isShow = false
        this.pickValue.properties || this.$set(this.pickValue, 'properties', {})
        const props = this.pickValue.properties
        this.$set(props, e, { type: 'string' })
      }
    },
    handleOk () {
      this.dnode()
      this.isShow = false
      this.visible = false
    },
    dataDialog () {
      this.visible = true
    },
    onInputName (e) {
      const oldKey = this.pickKey
      const newKey = e.target.value
      if (oldKey === newKey) return
      const nodeValue = this.parent.properties[oldKey]
      // 替换key名
      this.$delete(this.parent.properties, oldKey)
      this.$set(this.parent.properties, newKey, nodeValue)
      // required重新设置
      const requireds = this.parent.required || []
      const oldIndex = requireds.indexOf(oldKey)
      if (requireds.length > 0 && oldIndex > -1) {
        requireds.splice(oldIndex, 1)
        requireds.push(newKey)
        this.$set(this.parent, 'required', [...new Set(requireds)])
      }
    },
    onChangeType () {
      this.parseCustomProps()
      // 删除自定义属性
      this.customProps.forEach(item => {
        this.$delete(this.pickValue, item.key)
      });
      this.customProps = [];
      this.$delete(this.pickValue, 'properties')
      this.$delete(this.pickValue, 'items')
      this.$delete(this.pickValue, 'required')
      this.$delete(this.pickValue, 'format')
      this.$delete(this.pickValue, 'enum')

      if (this.isArray) {
        this.$set(this.pickValue, 'items', { type: 'string' })
      }
    },
    onCheck (e) {
      this._checked(e.target.checked, this.parent)
    },
    onRootCheck (e) {
      this._deepCheck(e.target.checked, this.pickValue)
    },
    changeEnumValue (e) {
      const pickType = this.pickValue.type
      const value = e.target.value
      var arr = value.split('\n')
      if (pickType === 'string') {
        this.advancedValue.enum = arr.map(item => item);
      } else {
        if (arr.length === 0 || (arr.length === 1 && arr[0] == '')) {
          this.advancedValue.enum = null
        } else {
          this.advancedValue.enum = arr.map(item => +item);
        }
      }
    },
    _deepCheck (checked, node) {
      if (node.type === 'object' && node.properties) {
        checked ? this.$set(node, 'required', Object.keys(node.properties)) : this.$delete(node, 'required')
        Object.keys(node.properties).forEach(key => this._deepCheck(checked, node.properties[key]))
      } else if (node.type === 'array' && node.items.type === 'object') {
        console.log(123);
        checked ? this.$set(node.items, 'required', Object.keys(node.items.properties)) : this.$delete(node.items, 'required')
        Object.keys(node.items.properties).forEach(key => this._deepCheck(checked, node.items.properties[key]))
      }
    },
    _checked (checked, parent) {
      let required = parent.required
      if (checked) {
        required || this.$set(this.parent, 'required', [])

        required = this.parent.required
        required.indexOf(this.pickKey) === -1 && required.push(this.pickKey)
      } else {
        const pos = required.indexOf(this.pickKey)
        pos >= 0 && required.splice(pos, 1)
      }
      required.length === 0 && this.$delete(parent, 'required')
    },
    dnode () {
      const arr = [{ item: '2123', type: 'string', description: '2222' }, { item: '12111', type: 'string', description: '123123' }]
      if (!this.pickValue.properties) {
        this.$set(this.pickValue, 'properties', {})
      }
      arr.forEach((el) => {
        this.$set(this.pickValue.properties, el.item, { type: el.type, description: el.description })
      })
    },
    addChild () {
      this.isShow = true
      this.$nextTick(() => {
        this.$refs['a'].focus()
      })
    },
    parseCustomProps () {
      const ownProps = this.ownProps
      Object.keys(this.pickValue).forEach(key => {
        if (ownProps.indexOf(key) === -1) {
          this.confirmAddCustomNode({ key: key, value: this.pickValue[key] })
          this.$delete(this.pickValue, key)
        }
      })
    },
    confirmAddCustomNode (prop) {
      const p = prop || this.addProp
      let existKey = false
      this.customProps.forEach(item => {
        if (item.key === p.key) {
          existKey = true
        }
      })
      if (existKey) return
      this.customProps.push(p)
      this.addProp = {}
      this.customing = false
    },
    removeNode () {
      const { properties, required } = this.parent
      this.$delete(properties, this.pickKey)
      if (required) {
        const pos = required.indexOf(this.pickKey)
        pos >= 0 && required.splice(pos, 1)
        required.length === 0 && this.$delete(this.parent, 'required')
      }
    },
    _joinName () {
      return `field_${this.deep}_${this.countAdd++}`
    }
  }
}
</script>
<style scoped>
.root-input {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-variant: tabular-nums;
  list-style: none;
  font-feature-settings: 'tnum';
  position: relative;
  display: inline-block;
  width: 100%;
  height: 32px;
  padding: 4px 11px;
  color: rgba(0, 0, 0, 0.65);
  font-size: 14px;
  line-height: 1.5;
  background-color: #fff;
  background-image: none;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  transition: all 0.3s;
}
.root-input:focus {
  border-color: #409eff;
  outline: 0;
}
.root-input:disabled {
  background-color: #f5f7fa;
  border-color: #e4e7ed;
  color: #c0c4cc;
  cursor: not-allowed;
}
.choose-box {
  display: flex;
  justify-content: center;
  align-items: center;
}
.json-schema-editor .row {
  display: flex;
  margin: 12px;
}
.json-schema-editor .row {
  display: flex;
  align-items: center;
}
.json-schema-editor .row .ant-col-name .ant-col-name-c {
  display: flex;
  align-items: center;
}
.json-schema-editor .row .ant-col-name .ant-col-name-required {
  flex: 0 0 24px;
  text-align: center;
}
.json-schema-editor .row .ant-col-type {
  width: 100%;
}
.json-schema-editor .row .ant-col-setting {
  display: inline-block;
}
.json-schema-editor .row .setting-icon {
  color: rgba(0, 0, 0, 0.45);
  border: none;
}
.json-schema-editor .row .plus-icon {
  border: none;
}
.json-schema-editor .row .close-icon {
  color: #888;
  border: none;
}
</style>
