<template>
  <!-- item template -->
  <div class="tree-node">
    <div
      class="tree-item row no-wrap items-center"
      :class="(isLastNode()?'':'line')+(treeSelectedItem&&treeSelectedItem.item===item?' selected':'')"
    >
      <q-icon
        :class="(isFolder()?'is-folder ':' ')+(isOpen?'extend rotate-90':'')"
        v-if="isFolder()"
        name="play_arrow"
        @click.stop="toggle(!isOpen)"
        class="e-icon text-grey-6"
      ></q-icon>
      <div
        v-if="!item.$isEdit"
        class="tree-title q-pa-xs"
        :class="(treeSelectedItem&&treeSelectedItem.item===item?'selected':'')"
        @mouseenter="mouseAction(true)"
        @mouseleave="mouseAction(false)"
        @click="selectItem"
      >
        <code
          class="json-key"
          v-if="!(parent&&parent.type==='array')&&(item.k||item.k==0)"
          v-text="item.k"
        ></code>
        <code class="json-key" v-if="parent&&parent.type==='array'" v-text="index"></code>
        <code
          class="json-value"
          :class="item.type+(item.v&&item.type==='boolean'?' true':'')"
          v-text="showValue()"
        ></code>
        <q-btn
          v-if="showItemEdit(item)"
          icon="edit"
          color="primary"
          flat
          dense
          size="0.60em"
          @click.stop="updateItem()"
        >
          <q-tooltip>修改</q-tooltip>
        </q-btn>
        <q-btn
          v-if="isEdit&&item.$mouseover&&parent"
          icon="clear"
          color="negative"
          flat
          dense
          size="0.60em"
          @click.stop="delItem()"
        >
          <q-tooltip>删除</q-tooltip>
        </q-btn>
      </div>
      <input
        class="json-key edit-key"
        :class="item.$err_k?' error':''"
        placeholder="key"
        v-focus="item.$isEdit"
        v-if="item.$isEdit&&parent&&parent.type!=='array'"
        v-model="item.k"
        @keyup.stop.prevent.esc="cancelSave(item)"
        @keyup.stop.prevent.enter="save(item)"
      />
      <code class="json-key" v-if="item.$isEdit&&parent&&parent.type==='array'" v-text="index"></code>
      <span v-if="item.$isEdit" class="q-pa-xs" :class="item.type">:</span>
      <input
        class="json-value"
        placeholder="value"
        :class="item.type+(item.$err_v?' error':'')"
        :style="{width:(item.type==='number'?60:100)+'px'}"
        v-if="item.$isEdit&&(item.type==='string'||item.type==='number')"
        @keyup.esc.prevent="cancelSave(item)"
        @keyup.enter.prevent="save(item)"
        v-model="item.v"
      />
      <q-btn
        v-if="item.$isEdit&&item.type==='boolean'"
        :icon="item.v?'check_circle_outline':'clear'"
        :color="item.v?'primary':'grey-8'"
        :label="item.v?'true':'false'"
        flat
        dense
        size="0.80em"
        @click="changeBoolean(item)"
      ></q-btn>
      <code
        v-text="item.v"
        class="q-pa-xs"
        v-if="item.$isEdit&&(item.type==='array'||item.type==='object')"
      ></code>

      <select
        v-model="item.type"
        @change="changeType(item)"
        v-if="item.$isEdit&&(!item.children||item.children.length==0)"
        class="q-ma-xs"
      >
        <option :value="s" v-text="s" v-for="(s,index) in typeList" :key="index"></option>
      </select>
      <q-btn
        v-if="item.$isEdit"
        icon="check"
        color="primary"
        flat
        round
        dense
        size="0.80em"
        @click="save(item)"
      >
        <q-tooltip>确定修改</q-tooltip>
      </q-btn>
      <q-btn
        v-if="item.$isEdit"
        icon="clear"
        color="negative"
        flat
        round
        dense
        size="0.80em"
        @click="cancelSave(item)"
      >
        <q-tooltip>取消修改</q-tooltip>
      </q-btn>
    </div>
    <transition name="fade">
      <div class="tree-children" :class="isLastNode()?'':'line'" v-show="isOpen" v-if="isFolder()">
        <kyb-json-item
          :ref="'item_'+index"
          v-for="(child, index) in item.children"
          :is-edit="isEdit"
          :index="index"
          :key="itemKeyIn?child[itemKeyIn]:index"
          :selectMore="selectMore"
          :item="child"
          :parent="item"
          :module="module"
          :entity="entity"
          :item-key="itemKey"
          :tree-selected-item="treeSelectedItem"
          @add-item="$emit('add-item', $event)"
          @select-item="$emit('select-item', $event)"
          @item-change="itemChange($event)"
          @del-item="delItem"
          @del-action="delAction"
        ></kyb-json-item>
        <!-- <div class="add" @click="$emit('add-item', item)">+</div> -->
      </div>
    </transition>
  </div>
</template>

<script>
// define the kyb-tree2 component
export default {
  name: 'kyb-json-item',
  props: {
    isEdit: Boolean,
    index: Number,
    itemKey: String,
    item: Object,
    selectMore: Boolean,
    parent: Object,
    treeSelectedItem: Object,
    module: String,
    entity: String
  },
  data: function() {
    return {
      itemKeyIn: this.itemKey,
      isOpen: false,
      typeList: ['string', 'number', 'boolean', 'object', 'array']
    }
  },
  created: function() {
    if (this.parent) {
      this.item.selectMore = this.parent.selectMore
    } else {
      this.item.type || (this.igem.type = 'object')
    }
    this.item.$selected =
      this.item.$selected === undefined ? false : this.item.$selected
    this.item.$changeName = this.item && this.item.name ? this.item.name : ''
    // this.updataV();
    //
    // console.log("created", this.item);
    // this.$emit("item-change", this.item);
  },
  watch: {
    'item.name': function(old) {
      // console.log("item.name", old);
      this.item.$changeName = old
    },
    'item.$isEdit': function(old) {
      // console.log("item.$isEdit", old);
      // this.$emit("item-change", this.item);
    }
  },
  computed: {},
  methods: {
    showValue() {
      let showTitle = null
      if (this.item && this.item.v && this.item.type === 'string') {
        showTitle = '"' + this.item.v + '"'
      } else if (this.item.type === 'object') {
        let str = JSON.stringify(this.item.v)
        showTitle = str.length > 40 ? str.substring(0, 40) + '...}' : str
      } else if (this.item.type === 'array') {
        let len =
          this.item.children && this.item.children.length
            ? this.item.children.length
            : 0
        showTitle = '[' + (len || '') + ']'
      } else {
        showTitle = this.item.v
      }
      return showTitle
    },
    isFolder: function() {
      return (
        this.item &&
        (this.item.lazy || (this.item.children && this.item.children.length))
      )
    },
    isLastNode: function() {
      return (
        this.parent &&
        this.parent.children &&
        this.parent.children.indexOf(this.item) ===
          this.parent.children.length - 1
      )
    },
    findByKey(keyword, keyField) {
      keyField || (keyField = '_id')
      if (
        this.item &&
        this.item[keyField] &&
        keyword === this.item[[keyField]]
      ) {
        return this
      }
      if (this.item.children && this.item.children.length) {
        for (let i = 0; i < this.item.children.length; i++) {
          // console.log(this.$refs["item_" + i][0].findByKey);
          let re = this.$refs['item_' + i][0].findByKey(keyword)
          if (re != null) {
            return re
          }
        }
      }
      return null
    },
    showItemEdit(item) {
      let isShow = this.isEdit && item.$mouseover && this.parent
      if (this.parent && this.parent.type === 'array') {
        if (item.type === 'object' || item.type === 'array') {
          isShow = isShow && (!item.children || item.children.length === 0)
        }
      }
      return isShow
    },

    getJSON() {
      if (this.item.type === 'object' || this.item.type === 'array') {
        let v = {}
        if (this.item.type === 'array') {
          v = []
        }
        this.item.children || (this.item.children = [])
        for (let i = 0; i < this.item.children.length; i++) {
          let index = i >= this.item.$delIndexOf ? i + 1 : i
          let re =
            this.$refs['item_' + index] && this.$refs['item_' + index].length
              ? this.$refs['item_' + index][0]
              : null
          if (re != null && re.item) {
            if (this.item.type === 'object') {
              Object.assign(v, re.getJSON())
            } else {
              v.push(re.getJSON())
            }
          }
        }
        delete this.item.$delIndexOf

        if (this.parent && this.parent.type !== 'array') {
          let r = {}
          r[this.item.k] = v
          return r
        } else {
          return v
        }
      } else {
        if (this.parent.type === 'array') {
          return this.item.v
        } else {
          let v = {}
          v[this.item.k] = this.item.v
          return v
        }
      }
    },
    itemChange(obj) {
      // this.updataV();

      this.$emit('item-change', obj)
    },
    toggle: function(isOpen, changeChild) {
      // console.log("-toggle", this.item);
      // this.$set(this.item, "$isOpen", !this.isOpen);
      // console.log("---- tree-item-:", this.$refs);
      // 服务器懒加载时，直接发送消息给父对象即可
      this.isOpen = isOpen

      if (changeChild && this.item.children && this.item.children.length) {
        this.item.children.forEach((v, k) => {
          this.$refs['item_' + k][0].toggle(isOpen, changeChild)
        })
      }
    },
    focus: function(event) {
      event.currentTarget.select()
    },
    selectItem: function() {
      // console.log(this.item);
      // let data = {};
      // this.genJSON(this.item, data);
      // console.log("gson: ", data);
      this.isOpen = !this.isOpen
      this.$emit('select-item', this)
    },
    selectAction: function() {
      this.$set(this.item, '$selected', !this.item.$selected)
      this.$forceUpdate()
    },
    cancelSave: function() {
      // this.save();
      // 取消添加的情况
      this.item.k = this.item.$old_k
      this.item.v = this.item.$old_v
      this.item.$isEdit = false
      this.$forceUpdate()
      if (this.item.$isAdd) {
        this.delItem()
      }
    },
    save: async function() {
      // 验证
      this.item.$err_k = false
      this.item.$err_v = false
      if (this.item.type === 'number') {
        if (isNaN(this.item.v)) {
          this.$q.showNotifi('必须填写数字')
          this.item.$err_v = true
        }
      }
      if (this.parent.type !== 'array') {
        if (this.item.k === '' || (!this.item.k && this.item.k !== 0)) {
          this.$q.showNotifi('属性名不能为空')
          this.item.$err_k = true
        }
        let v =
          this.parent && this.parent.children
            ? this.parent.children.findIndex(
              item => item.k === this.item.k && item !== this.item
            )
            : -1
        if (v > -1) {
          this.$q.showNotifi('重复的属性名')
          this.item.$err_k = true
        }
      }
      if (this.item.$err_k || this.item.$err_v) {
        this.$forceUpdate()
        return
      }
      this.item.$isEdit = false
      delete this.item.$isAdd
      this.$forceUpdate()
      this.itemChange(this)
      // this.$emit("item-change", this);
    },
    addItem() {
      if (!(this.item.type === 'object' || this.item.type === 'array')) {
        this.$q.showNotifi('对象或数组才能添加子对象')
        this.$forceUpdate()
        return
      }
      this.item.children || (this.item.children = [])
      let n = {
        k: '',
        v: '',
        type: 'string',
        $isEdit: true,
        $isAdd: true
      }

      this.item.children.push(n)
      this.$set(this.item, 'children', this.item.children)
      this.isOpen = true
      this.$forceUpdate()

      if (this.item.children.length > 1) {
        this.$refs['item_' + (this.item.children.length - 2)][0].$forceUpdate()
      }

      this.$emit('add-item', this)
      this.$emit('item-change', this)
    },
    updateItem() {
      this.item.$old_k = this.item.k
      this.item.$old_v = this.item.v
      this.item.$isEdit = true
      this.$forceUpdate()
    },
    delAction(obj) {
      let isSelectSelf = this.treeSelectedItem === obj
      let indexOf = this.item.children.indexOf(obj.item)
      this.item.children.splice(indexOf, 1)
      this.item.$delIndexOf = indexOf
      let last = null
      if (this.item.children.length > 0) {
        let next = indexOf
        next =
          next === this.item.children.length
            ? this.item.children.length - 1
            : next
        last = this.$refs['item_' + next][0]
        last.$forceUpdate()
      } else {
        last = this
      }

      if (isSelectSelf) {
        // 目前选中的是当前删除的item
        last.selectItem()
      }
      this.itemChange(this)
      this.$forceUpdate()
    },
    delItem() {
      // 告诉父亲做具体的删除动作
      this.$emit('del-action', this)
    },
    changeType(item) {
      if (item.type === 'object') {
        item.v = '{}'
      } else if (item.type === 'array') {
        item.v = '[]'
      } else if (item.type === 'boolean') {
        item.v = false
      } else if (item.type === 'string') {
        item.v = ''
      } else if (item.type === 'number') {
        item.v = 0
      }
      this.$forceUpdate()
    },
    changeBoolean(item) {
      item.v = !item.v
      this.$forceUpdate()
    },
    mouseAction(v) {
      this.item.$mouseover = v
      this.$forceUpdate()
    }
  }
}
</script>

<style lang="stylus">
.tree-node:hover, .tree-node.selected {
  // background $grey-8
}

.tree-children {
  padding-left: 30px;
  position: relative;
  transition: all 0.5s;
}

.tree-item {
  position: relative;
  padding: 2px;
}

.tree-title {
  margin: 1px;
  min-height: 30px;
  min-width: 50px;
  cursor: pointer;
}

.tree-title:hover, .tree-title.selected {
  background: $grey-3;
  border-radius: 3px;
}

.tree-title.selected {
  color: $primary;
}

.tree-item.line::before, .tree-children.line::before {
  content: '';
  position: absolute;
  left: -18px;
  top: 0;
  width: 16px;
  bottom: 0;
  border-left: 1px solid $grey-5;
}

.tree-item::after {
  content: '';
  position: absolute;
  left: -18px;
  top: -10%;
  width: 18px;
  height: 60%;
  border-left: 1px solid $grey-5;
  border-bottom: 1px solid $grey-5;
}

.e-icon {
  transition: all 0.5s;
  font-size: 20px;
  width: 20px;
  height: 20px;
  border-radius: 3px;
  cursor: pointer;
}

.e-icon.is-folder:hover {
  background: $grey-4;
}

.node-icon {
  padding: 6px;
  font-size: 18px;
}

.checkbox-icon {
  font-size: 22px;
}

.error {
  background: $red-2;
}

.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s;
}

.fade-enter, .fade-leave-to { /* .fade-leave-active below version 2.1.8 */
  opacity: 0;
}
</style>
