<template>
  <g
    :id="'stickPreview_' + this.stickID"
    @click="setActiveStick"
    v-show="stickVisible"
  >
    <defs>
      <linearGradient id="selectTop" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%" style="stop-color:#000000;stop-opacity:1" />
        <stop offset="60%" style="stop-color:#000000;stop-opacity:0" />
      </linearGradient>
      <linearGradient id="selectBottom" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="40%" style="stop-color:#000000;stop-opacity:0" />
        <stop offset="100%" style="stop-color:#000000;stop-opacity:1" />
      </linearGradient>
    </defs>
    <rect
      class="stick"
      :class="{ active: isActive }"
      :x="pointTop.x - stickWidth / 2"
      :y="pointTop.y - stickWidth / 2"
      :width="stickWidth"
      :height="length + stickWidth"
      :transform="
        'rotate(' + rotation + ',' + pointTop.x + ',' + pointTop.y + ')'
      "
      :rx="stickWidth / 2"
      v-show="showStick"
    />
    <rect
      class="label-background"
      :x="labelBBox.x - 50"
      :y="labelBBox.y"
      :width="labelBBox.width + 100"
      :height="labelBBox.height"
      :rx="snowMarkerHeight / 2"
      v-show="showLabel"
    />
    <text
      class="label"
      :x="pointBottom.x"
      :y="pointBottom.y + 120"
      v-show="showLabel"
    >
      {{ stickLabel }}
    </text>
    <circle
      class="selector top"
      :cx="pointTop.x"
      :cy="pointTop.y"
      :r="length / 6"
    />
    <circle
      class="selector middle"
      :cx="(pointTop.x + pointBottom.x) / 2"
      :cy="(pointTop.y + pointBottom.y) / 2"
      :r="length / 6"
    />
    <circle
      class="selector bottom"
      :cx="pointBottom.x"
      :cy="pointBottom.y"
      :r="length / 6"
    />
    <path
      class="snow-marker"
      v-show="showSnowMarker"
      :d="
        'm ' +
          snowMarkerPosition.x +
          ' ' +
          snowMarkerPosition.y +
          ' h ' +
          (snowMarkerWidth / 2 - snowMarkerHeight) +
          ' l ' +
          snowMarkerHeight / 2 +
          ' -' +
          snowMarkerHeight / 2 +
          ' a 1 1 0 0 1 0 ' +
          snowMarkerHeight +
          ' l -' +
          snowMarkerHeight / 2 +
          ' -' +
          snowMarkerHeight / 2 +
          ' h -' +
          (snowMarkerWidth - snowMarkerHeight * 2) +
          ' l -' +
          snowMarkerHeight / 2 +
          ' -' +
          snowMarkerHeight / 2 +
          ' a 1 1 0 0 0 0 ' +
          snowMarkerHeight +
          ' l ' +
          snowMarkerHeight / 2 +
          ' -' +
          snowMarkerHeight / 2
      "
    />
  </g>
</template>

<script>
import * as d3 from 'd3'

export default {
  name: 'StickDetail',
  props: {
    cameraID: {
      type: String,
      required: true
    },
    imageName: {
      type: String,
      required: true
    },
    stickID: {
      type: String,
      required: true
    },
    showStick: {
      type: Boolean,
      required: true
    },
    showSnowMarker: {
      type: Boolean,
      required: true
    },
    showLabel: {
      type: Boolean,
      required: true
    }
  },
  data() {
    return {
      stickWidth: 30,
      snowMarkerWidth: 150,
      snowMarkerHeight: 50,
      labelBBox: { x: 0, y: 0, width: 0, height: 0 },
      backup: {
        top: { x: 0, y: 0 },
        bottom: { x: 0, y: 0 },
        snowHeight: 0,
        imageApprovedByUser: false,
        stickCorrectedByUser: false
      }
    }
  },
  computed: {
    // --- D3 selectors ---
    $label() {
      return d3.select('#' + 'stickPreview_' + this.stickID).select('.label')
    },
    $stick() {
      return d3.select('#' + 'stickPreview_' + this.stickID).select('.stick')
    },
    $snowMarker() {
      return d3
        .select('#' + 'stickPreview_' + this.stickID)
        .select('.snow-marker')
    },
    $selectorTop() {
      return d3
        .select('#' + 'stickPreview_' + this.stickID)
        .select('.selector.top')
    },
    $selectorMiddle() {
      return d3
        .select('#' + 'stickPreview_' + this.stickID)
        .select('.selector.middle')
    },
    $selectorBottom() {
      return d3
        .select('#' + 'stickPreview_' + this.stickID)
        .select('.selector.bottom')
    },
    // ---

    pointTop: {
      get() {
        const stick = this.$store.getters.sticksByImageName(
          this.cameraID,
          this.imageName
        )[this.stickID]
        return {
          x: stick.top[0],
          y: stick.top[1]
        }
      },
      set(position) {
        // position = {x: number, y: number}
        const point = [position.x, position.y]
        this.$store.dispatch('setStickTop', {
          cameraID: this.cameraID,
          imageName: this.imageName,
          stickID: this.stickID,
          point,
          sendDataToServer: false
        })
      }
    },
    pointBottom: {
      get() {
        const stick = this.$store.getters.sticksByImageName(
          this.cameraID,
          this.imageName
        )[this.stickID]
        return {
          x: stick.bottom[0],
          y: stick.bottom[1]
        }
      },
      set(position) {
        // position = {x: number, y: number}
        const point = [position.x, position.y]
        this.$store.dispatch('setStickBottom', {
          cameraID: this.cameraID,
          imageName: this.imageName,
          stickID: this.stickID,
          point,
          sendDataToServer: false
        })
      }
    },
    snowHeight: {
      get() {
        return this.$store.getters.snowHeight(
          this.cameraID,
          this.stickID,
          this.imageName
        )
      },
      set(value) {
        this.$store.dispatch('setStickSnowHeight', {
          cameraID: this.cameraID,
          imageName: this.imageName,
          stickID: this.stickID,
          snowHeight: value,
          sendDataToServer: false
        })
      }
    },

    deltaX() {
      return this.pointBottom.x - this.pointTop.x
    },
    deltaY() {
      return this.pointBottom.y - this.pointTop.y
    },
    length() {
      return Math.sqrt(Math.pow(this.deltaX, 2) + Math.pow(this.deltaY, 2))
    },
    rotation() {
      return -(Math.atan2(this.deltaX, this.deltaY) * 180) / Math.PI
    },
    snowMarkerPosition() {
      const x =
        ((this.pointTop.x - this.pointBottom.x) * this.snowHeight) /
          this.stickLengthCm +
        this.pointBottom.x
      const y =
        ((this.pointTop.y - this.pointBottom.y) * this.snowHeight) /
          this.stickLengthCm +
        this.pointBottom.y
      return { x, y }
    },

    // --- Vuex getters ---
    isActive() {
      return this.$store.getters.activeStick === this.stickID
    },
    stickVisible() {
      return this.$store.getters.stickVisible(
        this.cameraID,
        this.stickID,
        this.imageName
      )
    },
    stickLabel() {
      return this.$store.getters.stickViewLabel(this.stickID, this.cameraID)
    },
    stickLengthCm() {
      return this.$store.getters.stickLengthCm(this.stickID)
    }
    // ---
  },
  watch: {
    cameraID() {
      this.buildStickBackupData()
    },
    imageName() {
      this.buildStickBackupData()
    },
    pointBottom() {
      this.$nextTick(() => {
        this.setLabelBBox()
      })
    }
  },
  methods: {
    setLabelBBox() {
      if (this.$label.node()) {
        this.labelBBox = this.$label.node().getBBox()
        return
      }
      this.labelBBox = { x: 0, y: 0, width: 0, height: 0 }
    },
    dragStarted() {
      this.$selectorTop.attr('cursor', 'grabbing')
      this.$selectorMiddle.attr('cursor', 'grabbing')
      this.$selectorBottom.attr('cursor', 'grabbing')
    },
    dragEnded() {
      this.$selectorTop.attr('cursor', 'grab')
      this.$selectorMiddle.attr('cursor', 'grab')
      this.$selectorBottom.attr('cursor', 'grab')

      this.$store.dispatch('setFlagStickCorrectedByUser', {
        cameraID: this.cameraID,
        imageName: this.imageName,
        stickID: this.stickID,
        flagState: true
      })
      this.$store.dispatch('setFlagImageApprovedByUser', {
        cameraID: this.cameraID,
        imageName: this.imageName,
        flagState: true
      })
    },
    dragTop(event) {
      const x = this.pointTop.x + event.dx
      const y = this.pointTop.y + event.dy
      this.pointTop = { x, y }
    },
    dragBottom(event) {
      const x = this.pointBottom.x + event.dx
      const y = this.pointBottom.y + event.dy
      this.pointBottom = { x, y }
      this.setLabelBBox()
    },
    dragMiddle(event) {
      const xTop = this.pointTop.x + event.dx
      const yTop = this.pointTop.y + event.dy
      this.pointTop = { x: xTop, y: yTop }
      const xBottom = this.pointBottom.x + event.dx
      const yBottom = this.pointBottom.y + event.dy
      this.pointBottom = { x: xBottom, y: yBottom }
      this.setLabelBBox()
    },
    dragMarker(event) {
      const newSnowHeight =
        this.snowHeight - (event.dy * this.stickLengthCm) / this.length
      if (newSnowHeight < 0) {
        this.snowHeight = 0
      } else if (newSnowHeight > this.stickLengthCm) {
        this.snowHeight = this.stickLengthCm
      } else {
        this.snowHeight = newSnowHeight
      }
    },

    setActiveStick() {
      this.$store.dispatch('setActivePoint', {
        stickID: this.stickID,
        cameraID: this.cameraID,
        imageName: this.imageName
      })
    },
    buildStickBackupData() {
      this.backup.top.x = this.pointTop.x
      this.backup.top.y = this.pointTop.y
      this.backup.bottom.x = this.pointBottom.x
      this.backup.bottom.y = this.pointBottom.y
      this.backup.snowHeight = this.snowHeight
      this.backup.imageApprovedByUser = this.$store.getters.flagImageApprovedByUser(
        this.cameraID,
        this.imageName
      )
      this.backup.stickCorrectedByUser = this.$store.getters.flagStickCorrectedByUser(
        this.cameraID,
        this.imageName,
        this.stickID
      )
    },
    revertChangeToSticks() {
      this.pointTop = this.backup.top
      this.pointBottom = this.backup.bottom
      this.snowHeight = this.backup.snowHeight

      this.$store.dispatch('setFlagStickCorrectedByUser', {
        cameraID: this.cameraID,
        imageName: this.imageName,
        stickID: this.stickID,
        flagState: this.backup.stickCorrectedByUser
      })
    }
  },
  created() {
    this.$parent.$parent.$on('revertChangeToSticks', this.revertChangeToSticks)
    this.buildStickBackupData()
  },
  mounted() {
    this.$selectorTop
      .call(
        d3
          .drag()
          .on('start', this.dragStarted)
          .on('drag', this.dragTop)
          .on('end', this.dragEnded)
      )
      .on('mouseover', () => {
        this.$stick.classed('select-top', true)
      })
      .on('mouseout', () => {
        this.$stick.classed('select-top', false)
      })

    this.$selectorMiddle
      .call(
        d3
          .drag()
          .on('start', this.dragStarted)
          .on('drag', this.dragMiddle)
          .on('end', this.dragEnded)
      )
      .on('mouseover', () => {
        this.$stick.classed('select-full', true)
      })
      .on('mouseout', () => {
        this.$stick.classed('select-full', false)
      })

    this.$selectorBottom
      .call(
        d3
          .drag()
          .on('start', this.dragStarted)
          .on('drag', this.dragBottom)
          .on('end', this.dragEnded)
      )
      .on('mouseover', () => {
        this.$stick.classed('select-bottom', true)
      })
      .on('mouseout', () => {
        this.$stick.classed('select-bottom', false)
      })

    this.$snowMarker
      .call(
        d3
          .drag()
          .on('drag', this.dragMarker)
          .on('end', this.dragEnded)
      )
      .on('mouseover', () => {
        this.$snowMarker.classed('select-full', true)
      })
      .on('mouseout', () => {
        this.$snowMarker.classed('select-full', false)
      })
  }
}
</script>

<style lang="scss" scoped>
.stick {
  fill: var(--color-accent-2);
  fill-opacity: 25%;
}

.stick.active {
  fill: var(--color-accent-3);
}

.stick.select-top {
  stroke-width: 10px;
  stroke: url(#selectTop);
  fill-opacity: 50%;
}

.stick.select-bottom {
  stroke-width: 10px;
  stroke: url(#selectBottom);
  fill-opacity: 50%;
}

.stick.select-full {
  stroke-width: 10px;
  stroke: #000000;
  fill-opacity: 50%;
}

.snow-marker {
  fill: #0c5aa3;
  fill-opacity: 60%;
  stroke: #0c5aa3;
  stroke-opacity: 60%;
  stroke-width: 10px;

  cursor: ns-resize;
}

.snow-marker.select-full {
  stroke-width: 10px;
  stroke: #000000;
  stroke-opacity: 100%;
}

.label {
  font-size: 96px;
  text-align: center;
  text-anchor: middle;
}

.label-background {
  fill: #888;
  opacity: 30%;
}

.selector {
  opacity: 0%;
  cursor: grab;
}
</style>
