<template>
  <g class="link" :class="{ detailed: isDetailed, highlighted: isHighlighted }">
    <line
      :id="`link-${link.id}`"
      :x1="source.x"
      :y1="source.y"
      :x2="target.x"
      :y2="target.y"
      class="link-line"
      :class="{
        hovered: isHovering,
        'with-arrows': link.direction === true,
        'user-defined': link.vis && link.vis.isUserDefined === true,
        transferred: isTransferred,
        multilink: link.vis && link.vis.isMultilink === true,
        'source-to-target': link.direction && link.direction.sourceToTarget,
        'target-to-source': link.direction && link.direction.targetToSource,
        'non-directional': link.direction && link.direction.nonDirectional
      }"
    ></line>

    <g v-if="!isNaN(pawSourcePosition.x)" class="link-paws">
      <circle
        v-for="pos in [pawSourcePosition, pawTargetPosition]"
        :key="`${pos.x}-${pos.y}`"
        :class="{ hovered: isHovering, detailed: isDetailed, highlighted: isHighlighted }"
        :r="visuals.linkPawRadius"
        :cx="pos.x"
        :cy="pos.y"
      ></circle>
    </g>

    <text
      v-if="inDebug"
      class="label-id"
      :dy="(source.y + target.y) / 2"
      :dx="(source.x + target.x) / 2"
      >{{ link.id }} {{ link.type == 'regular' ? link.type : '' }}</text
    >

    <line
      :x1="source.x"
      :y1="source.y"
      :x2="target.x"
      :y2="target.y"
      class="support-link"
      @mouseover="isHovering = true"
      @mouseover.ctrl.exact="
        SET_LINK_HIGHLIGHT(dataId)
        SET_NODE_HIGHLIGHT(link.source)
        SET_NODE_HIGHLIGHT(link.target)
      "
      @mouseout="
        CLEAR_NODE_HIGHLIGHT()
        CLEAR_LINK_HIGHLIGHT()
        isHovering = false
      "
      @mousedown.prevent
      @mousedown.left.exact="switchDetailedView()"
    ></line>
  </g>
</template>

<script>
import { mapMutations, mapActions, mapState, mapGetters } from 'vuex'
import { getLinkSource, getLinkTarget, isBundledIn, isMultilink } from '@/store/modules/data.store.js'
import * as d3 from 'd3' // FIXME

export default {
  props: {
    dataId: { type: Number, required: true },
    visuals: { type: Object, required: true },
    // all d3-force calculated data:
    source: { type: Object, required: true },
    target: { type: Object, required: true }
  },

  data() {
    return {
      isHovering: false
    }
  },

  computed: {
    ...mapGetters('data', ['getNodeById', 'getLinkById', 'getMultilinkLinks']),
    ...mapState('interaction', ['inDebug']),
    ...mapGetters('interaction', ['isLinkDetailed']),
    link() {
      return this.getLinkById(this.dataId)
    },
    isDetailed() {
      return this.isLinkDetailed(this.dataId)
    },
    isHighlighted() {
      return this.$store.getters['interaction/isLinkHighlighted'](this.dataId)
    },
    pawSourcePosition() {
      const radius = this.visuals.baseNodeRadius + 
        (this.isNodeGroup(this.getNodeById(getLinkTarget(this.link))) ? this.visuals.groupNodeRadiusDifference : 0)
      return this.calculatePawPosition(this.target, this.source, radius)
    },
    pawTargetPosition() {
      const radius = this.visuals.baseNodeRadius + 
        (this.isNodeGroup(this.getNodeById(getLinkSource(this.link))) ? this.visuals.groupNodeRadiusDifference : 0) 
      return this.calculatePawPosition(this.source, this.target, radius)
    },
    showLabel() {
      return this.isDetailed || this.isHovering
    },
    sourceNode() {
      return this.getNodeById(getLinkSource(this.link)) 
    },
    targetNode() {
      return this.getNodeById(getLinkTarget(this.link))
    },
    multilinkLinks() {
      if (!isMultilink(this.link)) return undefined
      return this.getMultilinkLinks(this.dataId)
    },
    isTransferred: {
      cache: false,
      get: function() {
        if (this.link.vis && this.link.vis.forceTransferred) return true

        switch (this.sourceNode.type) {
          case 'regular':
          case 'alias':
            switch (this.targetNode.type) {
              case 'regular':
              case 'alias':
                if (isMultilink(this.link)) {
                  const singlelinks = this.$store.getters['data/getMultilinkLinks'](this.dataId)
                  for (const l of singlelinks) {
                    if (l.vis && l.vis.forceTransferred) return true
                    if (isBundledIn(this.getNodeById(l.source), this.sourceNode.id) || 
                        isBundledIn(this.getNodeById(l.target), this.targetNode.id)) return true
                  }
                } else {
                  // check against original connection
                  if (isBundledIn(this.getNodeById(this.link.source), this.sourceNode.id) || 
                      isBundledIn(this.getNodeById(this.link.target), this.targetNode.id)) return true
                }
                return false
              case 'group':
                return true
              default:
                throw Error(`Cannot compute isTransferred property: unknown link type of '${this.targetNode.type}'`)
            }
          case 'group':
            switch (this.targetNode.type) {
              case 'regular':
              case 'alias':
              case 'group':
                return true
              default:
                throw Error(`Cannot compute isTransferred property: unknown link type of '${this.targetNode.type}'`)
            }
          default:
            throw Error(`Cannot compute isTransferred property: unknown link type of '${this.targetNode.type}'`)
        }
      }
    }
  },

  watch: {
    link: function(link) {
      if (!link) throw Error('Graph is trying to display unknown link')
    }
  },

  methods: {
    ...mapMutations('interaction', [
      'SHOW_LINK_DETAIL',
      'HIDE_LINK_DETAIL',
      'SET_TOOL_SHOW',
      'SET_NODE_HIGHLIGHT',
      'CLEAR_NODE_HIGHLIGHT',
      'SET_LINK_HIGHLIGHT',
      'CLEAR_LINK_HIGHLIGHT'
    ]),
    ...mapActions('interaction', 
      ['showLinkDetail', 'hideLinkDetail']
    ),

    switchDetailedView() {
      this.isDetailed ? this.hideLinkDetail() : this.showLinkDetail(this.link.id)
    },
    setHighlighted(id) {
      this.isHighlighed ? this.CLEAR_LINK_HIGHLIGHT() : this.SET_LINK_HIGHLIGHT(id)
    },
    calculatePawPosition(source, target, nodeRadius) {
      let x, y

      let x_diff = target.x - source.x
      let y_diff = target.y - source.y

      let length = Math.sqrt(Math.pow(x_diff, 2) + Math.pow(y_diff, 2))
      let x_diff_norm = x_diff / length
      let y_diff_norm = y_diff / length

      x = source.x + x_diff_norm * nodeRadius
      y = source.y + y_diff_norm * nodeRadius
      return { x, y }
    },
    isNodeGroup(node) {
      return node.type === 'group'
    }
  }
}
</script>

<style lang="sass" scoped>
.link
  line
    stroke: #bbb
    &.with-arrows
      marker-end: url(#single-link-arrow)
    &.multilink
      stroke-width: 3px
      stroke: #ccc
      &.source-to-target
        marker-end: url(#multi-link-arrow)
        &.non-directional
          marker-end: url(#multi-link-arrow-bar)
      &.target-to-source
        marker-start: url(#multi-link-arrow-opposite)
        &.non-directional
          marker-start: url(#multi-link-arrow-bar-opposite)
    &.user-defined
      stroke-dasharray: 7,7
      stroke-linecap: round
    &.transferred
      stroke-dasharray: 0,6
      stroke-width: 2px
      stroke-linecap: round
      &.multilink
        stroke-width: 4px
    &.highlight.highlightable
      stroke: #444
  line.hovered
    stroke: #757575

.link.detailed line.link-line
    stroke: #757575

.link.highlighted line.link-line
    stroke: #757575

.support-link
  stroke-opacity: 0
  stroke-width: 16px

text.label-id
  font-size: 11pt
  font-weight: 600
  fill: #4903fc
  user-select: none

.link-paws
  circle
    fill: #bbb
  .detailed
    fill: #444
  .hovered
    fill: #757575
  .highlighted
    fill: #757575
</style>
