<template>
  <div
    v-loading="loading"
    class="l-flex--col c-device-tree u-color--black u-font-size--sm u-relative"
    :class="{ 'support-shrink': shrink, shrink: shrinkState }"
  >
    <div class="l-flex__fill l-flex--col c-device-tree__content">
      <el-tabs
        v-if="exact"
        v-model="active"
        class="c-tabs sm has-bottom-padding--sm"
      >
        <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">
            <div
              v-if="canReset"
              class="o-button"
              @click="onReset"
            >
              重置
            </div>
          </div>
          <search-input
            v-model.trim="deviceName"
            class="l-flex__none"
            size="small"
            placeholder="设备名称"
            @search="onSearch"
          />
        </div>
        <c-tree
          class="l-flex__fill c-sibling-item--v"
          :root-option="rootOption"
        />
      </template>
    </div>
    <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
    },
    customTransform: {
      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'
    },
    canReset () {
      return this.checkbox && !this.rootOption.disabled
    }
  },
  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.initData()
        },
        () => {
          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
        }
        this.$emit('loaded', data)
        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(({ children, resolutionRatio/* , productTypeName */ }) => {
        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 (device) {
      const { id, name, productId } = device
      if (this.customTransform) {
        return {
          id, name, productId,
          ...this.customTransform(device)
        }
      }
      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)
      }
    },
    initData () {
      const rootOption = this.createRootNode()
      this.setNodes(rootOption, this.onFilter(rootOption, this.$nodes, this.deviceName ? new RegExp(this.deviceName) : null))
      this.rootOption = rootOption
      this.customExpand = false
      const diff = this.$presetNodeMap ? Object.keys(this.$presetNodeMap) : []
      this.$presetNodeMap = null
      return diff.length
    },
    onSearch () {
      this.filterNode(this.rootOption, this.deviceName ? new RegExp(this.deviceName) : null)
      this.customExpand = !!this.deviceName
    },
    filterNode (node, regx) {
      const { disbaled, checked, indeterminate } = node
      let change = false
      node.children.forEach(subnode => {
        if (subnode.children) {
          change = this.filterNode(subnode, regx) || change
        } else {
          const isDisabled = this.isDisabledNode(subnode, regx)
          if (subnode.disabled !== isDisabled) {
            change = true
            subnode.disabled = isDisabled
            this.addOrRemoveNodeCache(subnode)
          }
        }
      })
      if (change) {
        this.checkNode(node)
        return disbaled !== node.disbaled || checked !== node.checked || indeterminate !== node.indeterminate
      }
      return change
    },
    onFilter (parent, nodes, regx) {
      const childNodes = nodes.map(({ children, ...node }) => {
        if (!children) {
          const checked = this.$presetNodeMap ? this.$presetNodeMap[node.id] : false
          if (checked) {
            delete this.$presetNodeMap[node.id]
          }
          return this.createLeafNode(parent, {
            ...node,
            checked,
            disabled: this.isDisabledNode(node)
          })
        }
        const subNode = this.createNode(node, parent)
        this.setNodes(subNode, this.onFilter(subNode, children, regx))
        return subNode
      })
      return childNodes
    },
    isDisabledNode (node, regx) {
      return (regx ? !regx.test(node.name) : false) || (this.filter ? !this.filter(node) : false)
    },
    onReset () {
      this.deviceName = ''
      this.initData()
    },
    reset (deviceIds) {
      if (deviceIds?.length) {
        const map = {}
        deviceIds.forEach(id => {
          map[id] = true
        })
        this.$presetNodeMap = map
        this.deviceName = ''
        if (this.$nodes) {
          return this.initData()
        }
        return -1
      }
      return this.initData()
    }
  }
}
</script>

<style lang="scss" scoped>
.c-device-tree {
  &.support-shrink {
    padding-right: $spacing--lg !important;

    &.shrink {
      box-sizing: border-box;
      width: $spacing--lg !important;

      .c-device-tree__content {
        width: 0;
        overflow: hidden;
      }
    }
  }
}

.o-shrink-icon {
  position: absolute;
  top: 50%;
  right: calc(($spacing--lg - $spacing) / 2);
  transform: translateY(-50%);
}
</style>
