<template>
  <g>
    <rect
      class="flow-pan-zoom__grid"
      width="100%"
      height="100%"
      fill="url(#grid)"
    />
    <rect
      ref="transformer"
      class="flow-pan-zoom__transformer"
      width="100%"
      height="100%"
      @mousedown="dragStart"/>
    <g
      ref="transform"
      v-bind="transformProps">
      <slot/>
    </g>
  </g>

</template>
<script>
export default {
  name: 'FlowPanZoom',
  props: {
    value: {type: Object, default: () => { return {x: 0, y: 0, zoom: 1} }}
  },
  data () {
    return {
      zoom: this.value.zoom,
      x: this.value.x,
      y: this.value.y,
      moving: false
    }
  },
  computed: {
    transformProps () {
      const transString = 'matrix(' + [
        this.zoom, 0, 0,
        this.zoom, this.x, this.y
      ].join(' ') + ')'

      return {
        transform: transString,
        class: 'flow-pan-zoom__transform ' + this.moving ? 'moving' : ''
      }
    }
  },

  watch: {
    value: {
      handler () {
        this.zoom = this.value.zoom
        this.x = this.value.x
        this.y = this.value.y
      },
      deep: true
    }
  },
  mounted () {
    this.$el.addEventListener('wheel', this.wheel)
  },
  beforeDestroy () {
    this.$el.removeEventListener('wheel', this.wheel)
  },
  methods: {
    // panStart
    dragStart (ev) {
      document.activeElement && document.activeElement.blur()
      if (ev.button !== 1) return // first button
      if (ev.target !== this.$refs.transformer) return
      ev.stopPropagation()

      const drag = (ev) => {
        this.moving = true
        this.update(this.x + ev.movementX, this.y + ev.movementY)
      }
      const drop = (ev) => {
        this.moving = false
        document.removeEventListener('mousemove', drag)
        document.removeEventListener('mouseup', drop)
      }
      document.addEventListener('mousemove', drag)
      document.addEventListener('mouseup', drop)
    },
    wheel (ev) {
      ev.preventDefault()
      let deltaY = (ev.deltaY > 0) ? 1 : -1
      deltaY *= (ev.shiftKey) ? 0.01 : 0.07
      const svgRect = this.$refs.transformer.getBoundingClientRect()
      const oX = ev.clientX - svgRect.left
      const oY = ev.clientY - svgRect.top
      const z = this.zoom - (deltaY * this.zoom)

      var curX = this.x
      var curY = this.y
      var scaleD = z / this.zoom // delta

      var nx = scaleD * (curX - oX) + oX
      var ny = scaleD * (curY - oY) + oY

      this.update(nx, ny, z)
    },
    update (x, y, zoom) {
      if (x) this.x = x
      if (y) this.y = y
      if (zoom) this.zoom = zoom

      this.$emit('input', {x: this.x, y: this.y, zoom: this.zoom})
    },
    transformedPoint (point) {
      const ctm = this.$refs.transform.getCTM()
      return point.matrixTransform(ctm.inverse())
    }
  }
}

</script>

<style>
.flow-pan-zoom__transformer {
  fill:transparent;
}

.flow-pan-zoom__grid {
  fill:transparent;
  pointer-events:none;
}

</style>
