<template>
  <div
    v-loading="loading"
    class="l-flex--col u-color--black u-font-size--sm u-relative"
    :class="{ shrink: shrinkState }"
  >
    <el-tabs
      v-if="exact"
      v-model="active"
      class="c-tabs has-bottom-padding"
    >
      <el-tab-pane
        label="按部门"
        name="group"
      />
      <el-tab-pane
        label="按分辨率"
        name="ratio"
      />
      <el-tab-pane
        label="按宽高比"
        name="productType"
      />
    </el-tabs>
    <warning
      v-if="error"
      @click="getDevices"
    />
    <template v-else>
      <div class="l-flex__none l-flex--row c-sibling-item--v u-overflow--hidden">
        <div class="l-flex__auto" />
        <search-input
          v-model.trim="deviceName"
          class="l-flex__none"
          placeholder="设备名称"
          @search="onSearch"
        />
      </div>
      <c-tree
        class="l-flex__fill c-sibling-item--v"
        :root-option="rootOption"
      />
    </template>
    <i
      v-if="shrink"
      class="o-shrink-icon u-color--black u-font-size--lg has-active"
      :class="iconClass"
      @click="onToggleShrink"
    />
  </div>
</template>

<script>
import {
  getDepartmentDeviceTree,
  getProduct
} from '@/api/device'
import treeMixin from '../tree.js'

export default {
  name: 'DeviceTree',
  mixins: [treeMixin],
  props: {
    shrink: {
      type: [Boolean, String],
      default: false
    },
    exact: {
      type: [Boolean, String],
      default: false
    },
    filter: {
      type: Function,
      default: null
    }
  },
  data () {
    return {
      shrinkState: false,
      loading: false,
      error: false,
      active: 'group',
      deviceName: ''
    }
  },
  computed: {
    isGroupTab () {
      return this.active === 'group'
    },
    iconClass () {
      return this.shrinkState ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'
    }
  },
  watch: {
    active: {
      handler () {
        this.getDevices()
      },
      immediate: true
    }
  },
  methods: {
    onToggleShrink () {
      this.shrinkState = !this.shrinkState
    },
    getDevices () {
      this.loading = true
      this.error = false
      this.getDevicesByActive().then(
        data => {
          this.$nodes = data
          this.onSearch()
        },
        () => {
          this.error = true
        }
      ).finally(() => {
        this.loading = false
      })
    },
    getDevicesByActive () {
      if (this.$productCache) {
        return this.initTabCache()
      }
      const cache = this[`$${this.active}Cache`]
      if (cache) {
        return Promise.resolve(cache)
      }
      return getDepartmentDeviceTree().then(({ data }) => {
        const productCacheOptions = {
          key: 'productId',
          ids: {},
          map: {},
          cache: []
        }
        this.$groupCache = this.iterateNode(data, productCacheOptions).children
        if (this.exact) {
          this.$productCache = productCacheOptions.cache
        }
        return this.getDevicesByActive()
      })
    },
    async initTabCache () {
      for (let i = 0; i < this.$productCache.length; i++) {
        if (!this.$productCache[i].resolutionRatio) {
          const { data: { resolutionRatio, productTypeName } } = await getProduct(this.$productCache[i].id)
          this.$productCache[i].resolutionRatio = resolutionRatio
          this.$productCache[i].productTypeName = productTypeName
          this.$productCache[i].children.forEach(device => {
            device.resolutionRatio = resolutionRatio
          })
        }
      }
      const ratioMap = {}
      const productTypeMap = {}
      this.$productCache.forEach(({ resolutionRatio, productTypeName, children }) => {
        if (!ratioMap[resolutionRatio]) {
          ratioMap[resolutionRatio] = []
        }
        ratioMap[resolutionRatio] = [...ratioMap[resolutionRatio], ...children]
        if (!productTypeMap[productTypeName]) {
          productTypeMap[productTypeName] = []
        }
        productTypeMap[productTypeName] = [...productTypeMap[productTypeName], ...children]
      })
      this.$ratioCache = Object.keys(ratioMap).map(key => {
        return {
          id: key,
          name: key,
          children: ratioMap[key]
        }
      })
      this.$productTypeCache = Object.keys(productTypeMap).map(key => {
        return {
          id: key,
          name: key,
          children: productTypeMap[key]
        }
      })
      this.$productCache = null
      return this.getDevicesByActive()
    },
    iterateNode ({ id = 'root', name = '我的部门', children, devices }, productCacheOptions) {
      const deviceArr = []
      devices.forEach(device => {
        const item = this.transform(device)
        this.setCache(item, productCacheOptions)
        deviceArr.push(item)
      })
      return {
        id: `dir_${id}`,
        name,
        children: [
          ...children.map(child => this.iterateNode(child, productCacheOptions)),
          ...deviceArr
        ]
      }
    },
    transform ({ id, name, productId }) {
      return {
        id, name, productId
      }
    },
    setCache (device, { key, ids, map, cache }) {
      const id = device.id
      if (!ids[id]) {
        const value = device[key]
        if (!map[value]) {
          map[value] = []
          cache.push({
            id: value,
            children: map[value]
          })
        }
        ids[id] = 1
        map[value].push(device)
      }
    },
    onSearch () {
      const regx = this.deviceName ? new RegExp(this.deviceName) : null
      const rootOption = this.createNode()
      this.setNodes(rootOption, this.onFilter(rootOption, this.$nodes, regx))
      this.rootOption = rootOption
      this.customExpand = !!this.deviceName
    },
    onFilter (parent, nodes, regx) {
      if (regx) {
        nodes = nodes.filter(({ children, name }) => {
          if (children) {
            return true
          }
          return regx.test(name)
        })
      }
      const childNodes = nodes.map(({ children, ...node }) => {
        if (!children) {
          const leafNode = this.createLeafNode(parent, node)
          if (this.filter) {
            leafNode.disabled = !this.filter(node)
          }
          return leafNode
        }
        const subNode = this.createNode(node, parent)
        this.setNodes(subNode, this.onFilter(subNode, children, regx))
        return subNode
      })
      if (this.hideDisabled) {
        return childNodes.filter(({ disabled }) => !disabled)
      }
      return childNodes
    },
    reset () {
      this.onSearch()
    }
  }
}
</script>

<style lang="scss" scoped>
.shrink {
  box-sizing: border-box;
  width: $spacing !important;
}

.o-shrink-icon {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
}
</style>
