<template>
  <div
    ref="outerWrapper"
    :class="{
      'mm-split': true,
      'mm-split--moving': isMoving,
    }">
    <div
      v-if="isHorizontal"
      class="mm-split-horizontal">
      <div
        :class="[paneClasses, 'mm-split-pane__left']"
        :style="{
          right: `${anotherOffset}%`
        }">
        <slot name="left"></slot>
      </div>
      <div
        v-drag="{
          dragstart: dragstartHandler,
          drag: dragHandler,
          dragend: dragendHandler,
        }"
        class="mm-split-trigger-wrap"
        :style="{
          left: `${offset}%`
        }">
        <slot name="trigger">
          <ui-trigger mode="vertical"></ui-trigger>
        </slot>
      </div>
      <div
        :class="[paneClasses, 'mm-split-pane__right']"
        :style="{
          left: `${offset}%`
        }">
        <slot name="right"></slot>
      </div>
    </div>

    <div
      v-else
      class="mm-split-vertical">
      <div
        :class="[paneClasses, 'mm-split-pane__top']"
        :style="{
          bottom: `${anotherOffset}%`
        }">
        <slot name="top"></slot>
      </div>
      <div
        v-drag="{
          dragstart: dragstartHandler,
          drag: dragHandler,
          dragend: dragendHandler,
        }"
        class="mm-split-trigger-wrap"
        :style="{
          top: `${offset}%`
        }">
        <slot name="trigger">
          <ui-trigger mode="horizontal"></ui-trigger>
        </slot>
      </div>
      <div
        :class="[paneClasses, 'mm-split-pane__bottom']"
        :style="{
          top: `${offset}%`
        }">
        <slot name="bottom"></slot>
      </div>
    </div>
  </div>
</template>

<script>
import drag from "../../directives/drag"

import trigger from "./trigger"
import config from "../../managers/config"

config.defaults({
  "Split.mode": "horizontal",
  "Split.min": "40px",
  "Split.max": "40px",
})

const MAX_PERCENTAGE = 100
const DEFAULT_PERCENTAGE = 50

export default {
  name: "mm-split",
  directives: { drag },
  components: {
    "ui-trigger": trigger,
  },
  props: {
    value: {
      type: [Number, String],
      default: 0.5,
    },
    mode: {
      type: String,
      default: () => config.get("Split.mode"),
      validator(value) {
        return ["horizontal", "vertical"].includes(value)
      },
    },
    min: {
      type: [Number, String],
      default: () => config.get("Split.min"),
    },
    max: {
      type: [Number, String],
      default: () => config.get("Split.max"),
    },
  },
  data() {
    return {
      localValue: this.value,
      isMoving: false,
      offset: DEFAULT_PERCENTAGE,
      oldOffset: 0,
      computedMin: this.min,
      computedMax: this.max,
    }
  },
  computed: {
    isHorizontal() {
      return this.mode === "horizontal"
    },
    offsetSize() {
      return this.isHorizontal ? "offsetWidth" : "offsetHeight"
    },
    paneClasses() {
      return {
        "mm-split-pane": true,
        "mm-split-pane--moving": this.isMoving,
      }
    },
    anotherOffset() {
      return MAX_PERCENTAGE - this.offset
    },
    valueIsPx() {
      return typeof this.value === "string"
    },
    realValue() {
      return this.computedThresholdValue(this.valueIsPx
        ? this.px2percent(this.localValue, this.$refs.outerWrapper[this.offsetSize])
        : this.localValue)
    },
  },
  watch: {
    value: {
      immediate: true,
      handler(value) {
        this.localValue = value
      },
    },
    localValue(value) {
      this.computeOffset()
      this.$emit("input", value)
    },
    min() {
      this.computedMin = this.getComputedThresholdValue("min")
    },
    max() {
      this.computedMax = this.getComputedThresholdValue("max")
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.handleResize()
    })
    window.addEventListener("resize", this.handleResize)
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.handleResize)
  },
  methods: {
    px2percent(numerator, denominator) {
      return parseFloat(numerator) / parseFloat(denominator)
    },
    getAnotherOffset(value) {
      return 1 - value
    },
    getComputedThresholdValue(type) {
      const size = this.$refs.outerWrapper[this.offsetSize]
      return typeof this[type] === "string" ? this.px2percent(this[type], size) : this[type]
    },
    computedThreshold() {
      this.computedMin = this.getComputedThresholdValue("min")
      this.computedMax = this.getComputedThresholdValue("max")
    },
    computedThresholdValue(value) {
      let anotherValue = this.getAnotherOffset(value)

      if (parseFloat(value) <= parseFloat(this.computedMin)) {
        value = Math.max(value, this.computedMin)
      }

      if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) {
        value = this.getAnotherOffset(Math.max(anotherValue, this.computedMax))
      }

      return value
    },
    computeOffset() {
      this.offset = this.realValue * 10000 / 100
    },
    handleResize() {
      this.computedThreshold()
      this.computeOffset()
    },
    dragHandler(e) {
      const { distanceX, distanceY, event } = e
      const outerWidth = this.$refs.outerWrapper[this.offsetSize]
      const distance = this.isHorizontal ? distanceX : distanceY
      const value = this.computedThresholdValue(
        this.px2percent(outerWidth * this.oldOffset + distance, outerWidth)
      )

      event.atMin = value === this.computedMin
      event.atMax = this.getAnotherOffset(value).toFixed(5) === this.computedMax.toFixed(5)

      this.localValue = this.valueIsPx ? `${Math.round(parseInt(outerWidth) * value)}px` : value
      this.$emit("moving", event)
    },
    dragstartHandler() {
      this.isMoving = true
      this.oldOffset = this.realValue
      this.$emit("move-start")
    },
    dragendHandler() {
      this.isMoving = false
      this.$emit("move-end")
    },
  },
}
</script>
