<template>
  <g
    class="flow-node"
    :class="{'flow-node--dragging':dragging}"
    @mousedown="$emit('nodePointerDown',$event)"
    @dblclick="$emit('nodeDoubleClick',$event)"
  >

    <rect
      ref="body"
      class="flow-node__body"
      :class="{'flow-node__body--dragging':dragging}"
      v-bind="bodyProps"
    />
    <text
      ref="label"
      class="flow-node__label"
      v-bind="labelProps">
      <tspan x="0" dy="1em" v-for="s in labelWrap">
        {{ s }}
      </tspan>
      <!--{{ label }}-->
    </text>

    <!-- input -->
    <circle
      class="flow-node__socket flow-node__socket--inputs"
      v-for="(inp,i) in inputs"
      v-bind="inputProps(i)"
      :data-nodeid="id"
      :data-in="i"
      :class="{'flow-node__socket--match': match.in && (inp == match.in || match.in == 'any' || input=='any')}"
      :key="'in'+i"
      @mousedown.stop.prevent="socketPointerDown($event, {in:i})"
    />
    <!-- output -->
    <circle
      v-if="output"
      class="flow-node__socket flow-node__socket--outputs"
      :data-nodeid="id"
      :data-out="0"
      :key="'out'+0"
      :class="{ 'flow-node__socket--match': match.out && (output == match.out || match.out == 'any' || output == 'any')}"
      v-bind="outputProps(0)"
      @mousedown.stop.prevent="socketPointerDown($event, {out:0})"
    />

    <text
      class="flow-node__socket-detail"
      v-for="(inp,i) in inputs"
      text-anchor="end"
      :x="inputPos(i).x - 10"
      :y="inputPos(i).y+5"
    >{{ inp }}</text>
    <text
      class="flow-node__socket-detail"
      :x="outputPos(0).x + 10"
      :y="outputPos(0).y+5">
      {{ output }}
    </text>

  </g>
</template>

<script>

export default {
  name: 'FlowNode',
  props: {
    'id': {type: String, required: true},
    'label': {type: String, default: ''},
    'inputs': {type: Array, default: () => []},
    'output': {type: String, default: ''},
    'match': {type: Object, default: () => {}},
    'dragging': {type: Boolean, default: false},

    'nodeStyle': {type: Object, default: () => {}}
    /* 'type': {type: String, default: ''},
    'color': {type: String, default: '#777'},
    'textColor': {type: String, default: '#fff'} */
    // 'value': {type: Object, default: () => {}}
  },
  data () {
    return {
      // maintain reference here?
      labelRect: {x: 0, y: 0, width: 0, height: 0},
      bodyRect: {x: 0, y: 0, width: 0, height: 0}
    }
  },
  computed: {
    labelWrap () {
      var ret = []
      const parts = this.label.split(' ', -1)
      let wrapThreshold = 10 // initial wrap threshold
      parts.forEach(n => {
        wrapThreshold = Math.max(n.length + 1, wrapThreshold)
      })
      ret.push(parts[0])
      for (let i = 1; i < parts.length; i++) {
        let ri = ret.length - 1 // last
        if (ret[ret.length - 1].length + parts[i].length > wrapThreshold) {
          ret.push(parts[i])
        } else {
          // we can add to same
          ret[ri] += ' ' + parts[i]
        }
      }

      return ret
    },
    labelProps () {
      return {
        x: 0,
        y: -0.2 + 'em',
        fill: this.textColor,
        transform: `translate(${-this.labelRect.width / 2},${-this.labelRect.height / 2})`
      }
    },
    bodyProps () {
      let width = this.labelRect.width + 30
      let height = Math.max(this.labelRect.height + 20, 60)
      if (this.nodeStyle.type === 'circle') {
        height = Math.max(width, height)
        width = height
      }
      const rect = {
        x: -width / 2,
        y: -height / 2,
        width: width,
        height: height,
        stroke: this.nodeStyle.color,
        fill: this.nodeStyle.color
      }
      if (this.nodeStyle && this.nodeStyle.type === 'circle') {
        rect.rx = width
        rect.ry = width
      }
      return rect
    },
    inputProps () {
      return (i) => {
        const {x, y} = this.inputPos(i)
        return {
          transform: `translate(${x} ${y})`,
          r: 5
        }
      }
    },
    outputProps () {
      return (i) => {
        const {x, y} = this.outputPos(i)
        return {
          transform: `translate(${x} ${y})`,
          r: 5
        }
      }
    }
  },
  watch: {
    // the only thing now that affects geometry
    'label' () {
      this.$nextTick(() => {
        this.labelRect = this.$refs.label.getBBox()
      })
    }
  },
  mounted () {
    // After render
    this.$nextTick(() => { // after mount we reupdate with new values
      if (!this.$refs.label) return
      this.labelRect = this.$refs.label.getBBox()
      this.$forceUpdate()
    })
  },
  methods: {
    inputPos (i) {
      const ilen = this.inputs.length
      if (ilen === 0) return {}
      const d = this.bodyProps.height / (ilen * 2)
      return {
        x: this.bodyProps.x + 7,
        y: this.bodyProps.y + d + (i * 2 * d)
      }
    },
    outputPos (i) {
      const rect = this.bodyProps
      return {
        x: rect.x + rect.width - 7,
        y: 0
      }
    },
    socketPointerDown (ev, socket) {
      this.$emit('socketPointerDown', ev, socket)
    }

  }
}
</script>
<style>
.flow-view:not(.activity) .flow-node:hover,
.flow-node--dragging {
  cursor:move;
}
.flow-node__socket {
  pointer-events: none;
  stroke-width:1;
  opacity:0;
  transition: all .3s;
}
.flow-node__body {
  transition: all .3s;
}
.flow-view:not(.activity) .flow-node__socket:hover {
  stroke-width:10;
  cursor:pointer;
}
.flow-node__socket--match {
  cursor:pointer;
  stroke-width:10;
}
/*
Override flow-node
for hidden
 */
.flow-linking .flow-node__socket {
  opacity:1;
  pointer-events: initial;
}
.flow-linking .flow-node__socket--match,
.flow-node__socket--match {
  opacity:1;
  pointer-events: initial;
}

.flow-node__label {
  stroke:none;
  pointer-events:none;
  user-select:none;
  fill:#333;
}
.flow-node__socket-detail {
  pointer-events:none;
  opacity:0;
}
.flow-view.flow-detail .flow-node__socket-detail {
  opacity:1;
  filter: url(#solid) /* doubt */
}

</style>
