<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>
    <rect
      v-if="isMultilink"
      class="link-label-box"
      :width="20"
      height="22"
      :y="(source.y + target.y) / 2 - 20"
      :x="(source.x + target.x) / 2 - 10"
      rx="5"
      ry="5"
    />
    <text
      v-if="isMultilink"
      class="multilink-label"
      :dy="(source.y + target.y) / 2 - 5"
      :dx="(source.x + target.x) / 2"
      text-anchor="middle"
    >{{ containedLinksCount }}</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)

        if (
          link.vis !== undefined &&
          link.vis.newTarget !== undefined &&
          link.vis.newSource !== undefined
        ) {
          SET_NODE_HIGHLIGHT(link.vis.newTarget)
          SET_NODE_HIGHLIGHT(link.vis.newSource)
        }
      "
      @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,
  isLinkTransferred
} from '@/store/modules/data.store.js'

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))
    },
    isMultilink() {
      return isMultilink(this.link)
    },
    multilinkLinks() {
      if (!isMultilink(this.link)) return undefined
      return this.getMultilinkLinks(this.dataId)
    },
    containedLinksCount() {
      if (!isMultilink(this.link)) return 0
      return this.getMultilinkLinks(this.dataId).length
    },
    isTransferred: {
      cache: false,
      get: function() {
        return isLinkTransferred(this.link, this.$store)
      }
    }
  },

  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

.link-label-box
  display: inline
  fill: rgba(220, 220, 220, 0.7)

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

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