<template>
  <div ref="main">
    <div v-if="isRange" class="visual-map">
      <span>{{range.min}}</span>
      <div class="visual-map-box" :style="visualMapStyle()"></div>
      <span>{{range.max}}</span>
    </div>
    <canvas ref="canvas" class="canvas" :width="canvasWidth" :height="canvasHeight"></canvas>
    <!-- 图标加载 -->
    <div style="display: none">
      <svg :ref="item.name" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"
        v-for="(item, index) in svgIcon" :key="`svg${index}`" :width="item.width" :height="item.height"
        :viewBox="`0 0 ${item.width} ${item.height}`" focusable="false" class="fa-icon">
        <path v-for="(pathItem, pathIndex) in item.paths" :key="`path${pathIndex}`" :d="pathItem.d" :style="pathItem.style"></path>
      </svg>
    </div>
  </div>
</template>
<script>
import elementResizeDetectorMaker from 'element-resize-detector'
import {getRatio, hasProperty, isObjInArray, loadImg} from './utils/tools'
import {icons} from './config/index'
import netRackIcons from './Icon/netRackIcon'
import powerIcon from './Icon/powerIcon'
import array from 'lodash/array'

export default {
  name: 'MapPoint',
  data () {
    const hotColor = ['#d64304', '#d66b0b', '#d2b446', '#e2c86c']
    const centerColor = ['#9bd6a4', '#71d685', '#5fd2bc', '#7fc8d2']
    const coolColor = ['#6bd7f1', '#59aae9', '#3294e8', '#0972f2']
    const colors = hotColor.concat(centerColor, coolColor).reverse()
    return {
      ratio: getRatio(),
      ctx: '',
      iconsDefault: icons,
      isLoadedIcon: false,
      colors,
      netRackIcons,
      powerIcon
    }
  },
  props: {
    rangeKey: {
      type: String,
      default: ''
    },
    nodeList: {
      type: Array,
      default: () => []
    },
    currName: {
      type: String,
      default: ''
    },
    highLightNodes: {
      type: Array,
      default: () => []
    },
    nodeIconsSvg: {
      type: Object,
      default: () => {
        return {}
      }
    },
    nodeIconsName: {
      type: Array,
      default: () => []
    },
    mapWidth: {
      type: Number,
      default: 0
    },
    mapHeight: {
      type: Number,
      default: 0
    },
    isRange: {
      type: Boolean,
      default: false
    },
    range: {
      type: Object,
      default: () => {
        return {
          min: 0,
          max: 0
        }
      }
    }
  },
  computed: {
    //  默认的图标和自定义的图标集合在一起，如果type相同，则覆盖默认的
    icons () {
      return array.differenceBy(this.iconsDefault, this.nodeIconsName, 'type').concat(this.nodeIconsName)
    },
    fontSize () {
      return 13 * this.ratio
    },
    canvasWidth () {
      return this.mapWidth * this.ratio
    },
    canvasHeight () {
      return this.mapHeight * this.ratio
    },
    allIconSvg () {
      return Object.assign({}, this.netRackIcons, this.powerIcon, this.nodeIconsSvg)
    },
    svgIcon () {
      const result = []
      this.icons.map((item) => {
        const name = item.name
        let obj = {}
        Object.keys(this.allIconSvg).map((svg) => {
          if (name === svg) obj = Object.assign({}, item, this.allIconSvg[svg])
        })
        result.push(obj)
      })
      return result
    }
  },
  watch: {
    nodeList () {
      this.isLoadedIcon = false
      this.init()
    },
    highLightNodes () {
      this.render()
    },
    currName () {
      this.render()
    },
    isRange () {
      this.render()
    },
    rangeKey () {
      this.render()
    }
  },
  mounted () {
    this.observer = elementResizeDetectorMaker()
    this.observer.listenTo(this.$refs.main, this.init)
  },
  beforeDestroy () {
    this.observer.removeListener(this.$refs.main, this.init)
    clearTimeout(this.animationTimeIndex)
  },
  methods: {
    visualMapStyle () {
      return {
        background: `-webkit-linear-gradient(left,${this.colors.join(',')})`
      }
    },
    async init () {
      if (!this.isLoadedIcon) {
        await this.loadIcon()
        this.isLoadedIcon = true
      }
      const el = this.$refs.canvas
      this.ctx = el.getContext('2d') || ''
      this.render()
    },
    render () {
      // const ANIMATION_TIME = 30
      const ctx = this.ctx
      ctx.font = `${this.fontSize}px '微软雅黑'`
      ctx.textAlign = 'center'
      ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
      this.renderPoints()
      // this.animationTimeIndex = setTimeout(() => {
      //   this.render()
      // }, ANIMATION_TIME)
    },
    renderPoints () {
      // canvas 设置lineWidth 比较耗资源，分开两个循环优化性能
      this.ctx.lineWidth = 6 * this.ratio
      this.nodeList.forEach((node) => {
        if (isObjInArray(this.highLightNodes, 'name', node)) { // 如果节点在要高亮的节点的数组里
          const icon = this.highLightNodes.find(({name = ''}) => {
            return name === node.name
          }).icon
          this.renderPoint(node.name, node, icon)
        } else {
          this.renderPoint(node.name, node)
        }
      })
      this.ctx.lineWidth = 0.5 * this.ratio
      this.nodeList.forEach((node) => {
        this.renderPointName(node)
      })
    },
    // 绘制节点 i:指定绘制图标
    renderPoint (name, node, i = '') {
      // 20200227（自定义图标功能）：调用插件时可自定义nodeValue对象的内容，所以优先在node.nodeValue查找iconType属性，否则使用node.type
      const nodeType = (node.nodeValue && node.nodeValue.iconType) || node.type
      // 绘制图标
      const icon = i || (this.icons.find(({name = '', type = []}) =>
        node.icon ? name === node.icon : type === nodeType)) || false
      let size = icon.size || 0
      if (name === this.currName) {
        size = icon.size * 1.3 || 0
      }
      const {nodeValue = {}} = node
      const value = hasProperty(nodeValue, this.rangeKey) ? nodeValue[this.rangeKey] : ''
      this.isRange && value !== '' && this.renderPointValue(node, size * this.ratio, value)
      icon && this.renderIcon(icon.iconImg, node, size * this.ratio)
    },
    renderPointValue ({x = 0, y = 0}, r = 0, price) {
      const ctx = this.ctx
      ctx.save()
      const lightColor = this.getPriceColor(price)
      const lg = ctx.createRadialGradient(x, y, 0, x, y, r + 5)
      lg.addColorStop(0.3, 'rgba(255,255,255, 0)')
      // 向圆形渐变上添加颜色
      lg.addColorStop(1, lightColor)
      ctx.beginPath()
      ctx.fillStyle = lg
      ctx.arc(x, y, r + 5, 0, 2 * Math.PI, false)
      ctx.fill()
      ctx.closePath()
      ctx.restore()
    },
    getPriceColor (price) {
      const {min, max} = this.range
      const value = max - min
      const len = this.colors.length - 1
      const colorIndex = parseInt(len * (price - min) / value) || 0
      return this.colors[colorIndex]
    },
    // 绘制图标
    renderIcon (icon, {x, y, angle = 0}, r) {
      const ctx = this.ctx
      ctx.save()
      ctx.translate(x, y)
      ctx.rotate(this.getRads(angle))
      ctx.drawImage(icon, -r, -r, r * 2, r * 2)
      ctx.restore()
    },
    // 角度 转换 值
    getRads (degrees) {
      return (Math.PI * degrees) / 180
    },
    // 异步加载图标
    async loadIcon () {
      return new Promise((resolve) => {
        const loadedList = []
        const icons = this.icons

        function onloaded (image, index) {
          loadedList.push(true)
          icons[index].iconImg = image
          if (loadedList.length === icons.length) {
            resolve()
          }
        }
        icons.forEach((item, index) => {
          loadImg(this.$refs[item.name][0]).then((image) => {
            onloaded(image, index)
          }).catch((e) => {
            onloaded(e, index)
          })
        })
      })
    },
    // 绘制节点名称 anotherName是管理后台地图编辑配置的别名，nodeValue.nameAlias是管理后台基础数据的节点别名，优先使用nodeValue.nameAlias
    renderPointName ({name = '', anotherName = '', type, x, y, dx = 0, dy = 0, val = '', nodeValue = {}}) {
      let fontSize = this.fontSize
      if (name === this.currName) {
        fontSize = fontSize * 1.3
      }
      const ctx = this.ctx
      const color = this.colorJudge(type)
      ctx.save()
      ctx.font = `${fontSize}px '微软雅黑'`
      ctx.fillStyle = color
      ctx.strokeStyle = color
      const textX = x + dx
      const textY = y + fontSize * 2 / 5 + dy
      const valY = y + fontSize * 5 / 3 + dy
      // 节点/电厂名
      const alias = nodeValue.nameAlias || anotherName || name
      this.renderText(ctx, alias, textX, textY)
      // 电厂底部的值
      this.renderText(ctx, val, textX, valY)
      ctx.restore()
    },
    // 绘制文字
    renderText (ctx, text, textX, textY) {
      ctx.beginPath()
      ctx.strokeText(text, textX, textY)
      ctx.fillText(text, textX, textY)
      ctx.closePath()
    },
    colorJudge (type) { // 字体颜色判断
      let color = '#fff'
      switch (type) {
        case 1:
        case 9:
          color = '#10ecf9'
          break
        case 11:
          color = '#ffa412'
          break
        case 4:
        case 3:
          color = '#2ce4b2'
          break
        case 24:
          color = '#f4ef4a'
          break
        default:
          color = '#fff'
          break
      }
      return color
    }
  }
}
</script>
<style scoped lang="less">
.visual-map {
  position: absolute;
  top: 10px;
  left: 10px;
  line-height: 20px;
  height: 20px;

  span {
    display: inline-block;
    padding: 0 10px;
  }

  .visual-map-box {
    width: 150px;
    height: 100%;
    display: inline-block;
    vertical-align: middle;
  }
}

.canvas {
  width: 100%;
  height: 100%;
}
</style>
