<template>
  <div ref="parent"
       class="canvas-parent-box">
    <div ref="main" class="canvas-box"
         v-drag="{parentBox: $refs.parent, isEnable: canvasDragIsEnable, spill: true, spillValue: 0.8}">
      <!-- 标尺 -->
      <map-ruler class="map-main" :map-width="mapWidth" :map-height="mapHeight"
                 :interval="interval" v-if="showRuler"></map-ruler>
      <!-- 线路 -->
      <map-line class="map-main"
                :line-list="lineList"
                :line-value="lineValue"
                :high-light-lines="highLightLines"
                :animation="showLineAnimation"
                :curr-name="canvasHoverFlag"
                :map-width="mapWidth"
                :map-height="mapHeight"></map-line>
      <!-- 节点 -->
      <map-point class="map-main"
                 :node-list="nodeList"
                 :high-light-nodes="highLightNodes"
                 :curr-name="canvasHoverFlag"
                 :map-width="mapWidth"
                 :map-height="mapHeight"
                 :range="range"
                 :range-key="rangeKey"
                 :is-range="isRange"
                 :node-icons-svg="nodeIconsSvg"
                 :node-icons-name="nodeIconsName"
      ></map-point>
      <!-- 鼠标事件处理层 -->
      <map-hover class="map-main"
                 :node-list="nodeList"
                 :line-list="lineList"
                 @on-hover="handleHover"
                 :map-width="mapWidth"
                 :map-height="mapHeight"
                 :node-dragable="nodeDragable"
                 :scale-value="scaleValue"
                 @on-mouse-event="handleMouseEvent"
                 @on-node-drag="handleDrag"
                 @on-node-drag-end="handleDragEnd"
      ></map-hover>
    </div>
  </div>
</template>
<script>
import elementResizeDetectorMaker from 'element-resize-detector'
import {icons, CANVAS_WIDTH, CANVAS_HEIGHT} from './config/index'
import {getRatio} from './utils/tools'
import MapRuler from './MapRuler'
import MapPoint from './MapPoint'
import MapLine from './MapLine'
import MapHover from './MapHover'
import {drag} from './directives/drag'

export default {
  name: 'Canvas',
  data () {
    return {
      icons,
      ratio: getRatio(),
      r: 15,
      time: 0,
      canvasHoverFlag: ''
    }
  },
  props: {
    rangeKey: {
      type: String,
      default: ''
    },
    isRange: {
      type: Boolean,
      default: false
    },
    range: {
      type: Object,
      default: () => {
        return {
          min: 0,
          max: 0
        }
      }
    },
    mapWidth: {
      type: Number,
      default: CANVAS_WIDTH
    },
    mapHeight: {
      type: Number,
      default: CANVAS_HEIGHT
    },
    // 节点数据源对象
    node: {
      type: Object,
      default: () => {
        return {}
      }
    },
    // 线路数据源对象
    line: {
      type: Object,
      default: () => {
        return {}
      }
    },
    // 线路潮流数据源数组
    lineValue: {
      type: Array,
      default: () => []
    },
    // 节点详情数据源数组
    nodeValue: {
      type: Array,
      default: () => []
    },
    // 断面相关线路高亮
    highLightLines: {
      type: Array,
      default: () => []
    },
    // 高亮节点（更换节点图标）数据格式：[{key: value, icon: iconName}]
    highLightNodes: {
      type: Array,
      default: () => []
    },
    nodeIconsSvg: {
      type: Object,
      default: () => {
        return {}
      }
    },
    nodeIconsName: {
      type: Array,
      default: () => []
    },
    // 标尺的间距
    interval: {
      type: Number,
      default: 1
    },
    // 是否显示标尺
    showRuler: {
      type: Boolean,
      default: false
    },
    // 是否显示线路动画潮流
    showLineAnimation: {
      type: Boolean,
      default: false
    },
    // 是否节点可拖拽
    nodeDragable: {
      type: Boolean,
      default: false
    },
    // 缩放
    scaleValue: {
      type: Number,
      default: 50
    },
    // 是否可整个拖动
    mapDragable: {
      type: Boolean,
      default: true
    }
  },
  components: {
    MapRuler,
    MapPoint,
    MapLine,
    MapHover
  },
  watch: {
    mapWidth () {
      this.setHeight()
    },
    mapHeight () {
      this.setHeight()
    },
    scaleValue (val) {
      this.setScale(val)
    }
  },
  computed: {
    wr () {
      const el = this.$refs.main
      const bbox = el.getBoundingClientRect()
      return (this.mapWidth / bbox.width) * this.ratio
    },
    hr () {
      const el = this.$refs.main
      const bbox = el.getBoundingClientRect()
      return (this.mapHeight / bbox.height) * this.ratio
    },
    canvasDragIsEnable () {
      return this.mapDragable && !this.canvasHoverFlag
    },
    lineList () {
      let result = []
      const lines = this.line
      // 将线路对象转化为线路数组
      for (const name in lines) {
        const lineAllValue = this.lineValue.find((line) => {
          return line.name === name
        }) || {}
        const lineListByline = this.getLineListByline(name, lines[name], this.node, this.lineValue, lineAllValue)
        result = result.concat(lineListByline)
      }
      return result
    },
    nodeList () {
      return this.nodeNameList.map((name) => {
        const node = this.node[name]
        const nodeValue = this.nodeValue.find((node) => {
          return node.name === name
        }) || {}
        return {
          name,
          ...node,
          ...this.getNodePos(node),
          nodeValue: nodeValue
        }
      })
    },
    nodeNameList () {
      return Object.keys(this.node) || []
    }
  },
  mounted () {
    this.observer = elementResizeDetectorMaker()
    this.observer.listenTo(this.$refs.main, this.setHeight)
  },
  beforeDestroy () {
    this.observer.removeListener(this.$refs.main, this.render)
  },
  methods: {
    getLineListByline (name, line, node, lineValue, lineAllValue) {
      const start = node[line.start]
      const end = node[line.end]
      const {lineNumber = 0} = line
      const lineInfo = lineValue.find((item) => item.name === name) || {}
      const {values = [], nameAlias = ''} = lineInfo
      const result = []
      const interval = this.r / lineNumber
      const angle = this.getAngle(start, end)
      // 如果线路数量是单数，则先设置单数线为中线
      if (lineNumber % 2 === 1) {
        result.push(this.getPos(start, end, 0, angle))
      }
      for (let j = 1; j <= parseInt(lineNumber / 2 + ''); j++) {
        let intervalTod = interval * j
        if (j === 1 && lineNumber % 2 === 0) {
          intervalTod = intervalTod * 2 / 3
        }
        result.push(this.getPos(start, end, -1 * intervalTod, angle))
        result.unshift(this.getPos(start, end, intervalTod, angle))
      }
      return result.map((item, index) => {
        const {value = 0} = values[index] || {}
        return {
          ...line,
          ...item,
          name,
          index,
          nameAlias,
          value,
          lineValue: lineAllValue // 线路对象整个返回
        }
      })
    },
    handleDrag ({x, y}, name) {
      this.setNodePos(name, {x, y})
      this.$emit('node-drag', {x, y}, name)
    },
    handleDragEnd ({x, y}, name) {
      this.setNodePos(name, {x, y})
      this.$emit('node-drag-end', {x, y}, name)
    },
    setNodePos (name, {x, y}) {
      this.node[name].x = x
      this.node[name].y = y
    },
    handleMouseEvent ({currNode, event: {type, layerX, layerY}}) {
      const types = {
        mousedown: 'down-shape',
        mousemove: 'hover-shape',
        mouseup: 'click-shape',
        dblclick: 'dblclick-shape'
      }
      types[type] && this.$emit(types[type], currNode, {
        layerX,
        layerY
      })
    },
    handleHover (name) {
      this.$emit('out-shape')
      this.canvasHoverFlag = name
    },
    setScale (val) {
      this.$refs.main.style.transition = 'transform 0.15s'
      this.$refs.main.style.transform = `scale(${val / 50}, ${val / 50})`
      setTimeout(() => {
        this.$refs.main.style.transition = ''
      }, 200)
    },
    setHeight () {
      const el = this.$refs.main || {}
      const width = el.clientWidth || this.mapWidth
      const ratio = this.mapWidth / width
      if (el.style) {
        this.$refs.main.style.height = Math.round(this.mapHeight / ratio) + 'px'
      }
    },
    // 获取节点坐标
    getNodePos ({x = 0, y = 0, dx = 0, dy = 0}) {
      return {
        originalx: x, // 原始的x坐标
        originaly: y, // 原始的y坐标
        x: x * this.ratio,
        y: y * this.ratio,
        dx: dx * this.ratio,
        dy: dy * this.ratio
      }
    },
    // 计算角度
    getAngle (start, end) {
      const x = start.x - end.x
      const y = start.y - end.y
      if (!start && !end) {
        return 0
      }
      return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360
    },
    // 获取平行线
    getPos (start, end, interval, angle) {
      const PI = Math.PI
      const anglePI = 2 * PI / 360 * angle
      const sin = interval * Math.sin(anglePI)
      const cos = interval * Math.cos(anglePI)
      return {
        x1: (start.x + sin) * this.ratio || 0,
        y1: (start.y - cos) * this.ratio || 0,
        x2: (end.x + sin) * this.ratio || 0,
        y2: (end.y - cos) * this.ratio || 0
      }
    }
  },
  directives: {
    drag: {
      insert (el, binding) {
        drag(el, binding)
      },
      update (el, binding) {
        drag(el, binding)
      }
    }
  }
}
</script>
<style scoped lang="less">
.canvas-parent-box {
  overflow: hidden;
  width: 100%;
  height: 100%;
  .canvas-box {
    cursor: move;
    width: 100%;
    height: 100%;
    transition: width 0.15s;
    .map-main {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
  }
}
</style>
