<template>
  <div
    :class="b({ active: active && !disabled, move: moveActive, click: disabled })"
    :style="styleName"
    @mousedown.stop="handleMouseDown"
    @contextmenu.prevent
  >
    <!-- <el-input
      ref="input"
      v-model="value"
      :class="b('focus')"
    /> -->
    <div
      ref="wrapper"
      :class="b('wrapper')"
    >
      <template v-if="active && !disabled">
        <div
          :style="styleLineName"
          :class="b('line', ['left'])"
        />
        <div
          :style="styleLineName"
          :class="b('line', ['top'])"
        />
        <div
          :class="b('line', ['label'])"
          :style="styleLabelName"
        >{{ baseLeft }},{{ baseTop }}</div>
        <div
          v-for="item in rangeList"
          :key="item.classname"
          :class="b('range', [item.classname])"
          :style="[styleRangeName]"
          @mousedown.stop="handleRange($event, item.classname)"
        />
      </template>
      <div
        ref="item"
        :class="b('item')"
      >
        <slot />
      </div>
    </div>
  </div>
</template>

<script>
function getFixed (val = 0, len = 2) {
  return Number(val.toFixed(len))
}

const ELEMENT = '__'
const MODS = '--'

function join (name, el, symbol) {
  return el ? name + symbol + el : name
}

function prefix (name, mods) {
  if (typeof mods === 'string') {
    return join(name, mods, MODS)
  }

  if (Array.isArray(mods)) {
    return mods.map(item => prefix(name, item))
  }

  const ret = {}
  Object.keys(mods || {}).forEach(key => {
    ret[name + MODS + key] = mods[key]
  })
  return ret
}

function validatenull (val) {
  if (val && parseInt(val) === 0) {
    return false
  }
  if (val instanceof Date || typeof val === 'boolean' || typeof val === 'number') {
    return false
  }
  if (val instanceof Array) {
    if (val.length === 0) {
      return true
    }
  } else if (
    val === 'null'
    || val == null
    || val === 'undefined'
    || val === ''
  ) {
    return true
  }
  return false
}

export default {
  name: 'Draggable',
  props: {
    scale: {
      type: Number,
      default: 1
    },
    resize: {
      type: Boolean,
      default: true
    },
    disabled: {
      type: Boolean,
      default: false
    },
    lock: {
      type: Boolean,
      default: false
    },
    left: {
      type: Number,
      default: 0
    },
    top: {
      type: Number,
      default: 0
    },
    width: {
      type: Number,
      default: 0
    },
    height: {
      type: Number,
      default: 0
    },
    zIndex: {
      type: Number,
      default: 1
    }
  },
  data () {
    return {
      value: '',
      active: false,
      baseTop: 0,
      baseLeft: 0,
      baseWidth: 0,
      baseHeight: 0,
      children: {},
      moveActive: false,
      rangeActive: false,
      keyDown: null,
      rangeList: [
        {
          classname: 'left'
        },
        {
          classname: 'right'
        },
        {
          classname: 'top'
        },
        {
          classname: 'bottom'
        },
        {
          classname: 'top-left'
        },
        {
          classname: 'top-right'
        },
        {
          classname: 'bottom-left'
        },
        {
          classname: 'bottom-right'
        }
      ]
    }
  },
  computed: {
    step () {
      return this.scale
    },
    styleLineName () {
      return {
        borderWidth: this.setPx(this.scale)
      }
    },
    styleRangeName () {
      const calc = 6 * this.scale
      return {
        width: this.setPx(calc),
        height: this.setPx(calc)
      }
    },
    styleLabelName () {
      return {
        fontSize: this.setPx(14 * this.scale)
      }
    },
    styleName () {
      return Object.assign(
        this.active
          ? {
            zIndex: 9999,
            ...this.styleLineName
          }
          : { zIndex: this.zIndex }
        ,
        {
          top: this.setPx(this.baseTop),
          left: this.setPx(this.baseLeft),
          width: this.setPx(this.baseWidth),
          height: this.setPx(this.baseHeight)
        }
      )
    }
  },
  watch: {
    // active (val) {
    //   if (val) {
    //     this.handleKeydown()
    //   } else {
    //     document.onkeydown = this.keyDown
    //   }
    // },
    top (val) {
      this.baseTop = getFixed(val)
    },
    left (val) {
      this.baseLeft = getFixed(val)
    },
    width (val) {
      this.baseWidth = getFixed(val) || this.children.offsetWidth
    },
    height (val) {
      this.baseHeight = getFixed(val) || this.children.offsetHeight
    },
    baseWidth (val) {
      this.$refs.wrapper.style.width = this.setPx(val)
      if (this.resize && this.children.style) {
        this.children.style.width = this.setPx(val)
      }
    },
    baseHeight (val) {
      this.$refs.wrapper.style.height = this.setPx(val)
      if (this.resize && this.children.style) {
        this.children.style.height = this.setPx(val)
      }
    },
    baseTop (n, o) {
      this.setMove(0, n - o)
    },
    baseLeft (n, o) {
      this.setMove(n - o, 0)
    }
  },
  mounted () {
    this.init()
  },
  methods: {
    b (el, mods) {
      if (el && typeof el !== 'string') {
        mods = el
        el = ''
      }
      el = join('c-draggable', el, ELEMENT)

      return mods ? [el, prefix(el, mods)] : el
    },
    setPx (val, defval = '') {
      if (validatenull(val)) {
        val = defval
      }
      if (validatenull(val)) {
        return ''
      }
      val += ''
      if (val.indexOf('%') === -1) {
        val += 'px'
      }
      return val
    },
    init () {
      this.children = this.$refs.item.firstChild
      this.baseWidth = getFixed(this.width) || this.children.offsetWidth
      this.baseHeight = getFixed(this.height) || this.children.offsetHeight
      this.baseLeft = getFixed(this.left)
      this.baseTop = getFixed(this.top)
      // this.keyDown = document.onkeydown
    },
    setMove (left, top) {
      this.$emit('move', {
        index: this.index,
        left, top
      })
    },
    setLeft (left) {
      this.baseLeft = left
    },
    setTop (top) {
      this.baseTop = top
    },
    setActive (val) {
      this.active = val
    },
    handleClear () {
      document.onmouseup = () => {
        clearTimeout(this.$timer)
        document.onmousemove = null
        document.onmouseup = null
        this.onMouseUp()
      }
    },
    handleRange (e, position) {
      if (this.disabled || this.lock || e.button !== 0) {
        return
      }
      e.stopPropagation()
      this.rangeActive = true
      // 移动的方向
      let x
      let y
      // 移动的位置
      let xp
      let yp
      // 移动的正负
      let xc
      let yc
      let disX = e.clientX
      let disY = e.clientY
      document.onmousemove = e => {
        this.moveActive = true
        if (position === 'right') {
          x = true
          y = false
        } else if (position === 'left') {
          x = true
          xp = true
          xc = true
          y = false
        } else if (position === 'top') {
          x = false
          y = true
          yp = true
          yc = true
        } else if (position === 'bottom') {
          x = false
          y = true
        } else if (position === 'bottom-right') {
          x = true
          y = true
        } else if (position === 'bottom-left') {
          x = true
          y = true
          xp = true
          xc = true
        } else if (position === 'top-right') {
          x = true
          y = true
          yp = true
          yc = true
        } else if (position === 'top-left') {
          x = true
          y = true
          xp = true
          xc = true
          yp = true
          yc = true
        }
        const left = e.clientX - disX
        const top = e.clientY - disY
        disX = e.clientX
        disY = e.clientY
        if (x) {
          let calc = left * this.step
          if (xc) {
            calc = -calc
          }
          if (xp) {
            this.baseLeft = getFixed(this.baseLeft - calc)
          }
          this.baseWidth = getFixed(this.baseWidth + calc)
        }
        if (y) {
          let calc = top * this.step
          if (yc) {
            calc = -calc
          }
          if (yp) {
            this.baseTop = getFixed(this.baseTop - calc)
          }
          this.baseHeight = getFixed(this.baseHeight + calc)
        }
      }
      this.handleClear()
    },
    handleMouseDown (e) {
      if (this.disabled || this.lock) {
        return
      }
      // this.$nextTick(() => {
      //   this.$refs.input.focus()
      // })
      this.active = true
      if (e.button !== 0) {
        this.$emit('menu', e)
        return
      }
      this.$emit('focus', {
        top: this.baseTop,
        left: this.baseLeft,
        width: this.baseWidth,
        height: this.baseHeight
      })
      this.$timer = setTimeout(() => {
        this.moveActive = true
        this.$emit('will-move')
      }, 100)
      let disX = e.clientX
      let disY = e.clientY
      document.onmousemove = e => {
        if (!this.moveActive) {
          this.moveActive = true
          this.$emit('will-move')
        }
        const left = e.clientX - disX
        const top = e.clientY - disY
        disX = e.clientX
        disY = e.clientY
        this.baseLeft = getFixed(this.baseLeft + left * this.step)
        this.baseTop = getFixed(this.baseTop + top * this.step)
      }
      this.handleClear()
    },
    handleKeydown () {
      document.onkeydown = e => {
        const step = 1 * this.step
        if (this.$refs.input.focused) {
          if (e && e.keyCode === 38) { // 上
            this.baseTop = getFixed(this.baseTop - step)
          } else if (e && e.keyCode === 37) { // 左
            this.baseLeft = getFixed(this.baseLeft - step)
          } else if (e && e.keyCode === 40) { // 下
            this.baseTop = getFixed(this.baseTop + step)
          } else if (e && e.keyCode === 39) { // 右
            this.baseLeft = getFixed(this.baseLeft + step)
          }
          e.stopPropagation()
          e.preventDefault()
          this.$emit('blur', {
            index: this.index,
            width: this.baseWidth,
            height: this.baseHeight,
            left: this.baseLeft,
            top: this.baseTop
          })
          this.keyDown && this.keyDown(e)
        }
      }
    },
    onMouseUp () {
      if (this.moveActive || this.rangeActive) {
        this.moveActive = false
        this.rangeActive = false
        this.$emit('blur', {
          top: this.baseTop,
          left: this.baseLeft,
          width: this.baseWidth,
          height: this.baseHeight
        })
      }
    }
  }
}
</script>

<style lang="scss" scoped>
$active: rgba(115, 170, 229, 0.5);
$border: #0099ff;

.c-draggable {
  position: absolute;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  cursor: move;

  &__focus {
    opacity: 0;
    position: absolute !important;
    top: 0;
    left: 0;
    z-index: -1024;
  }

  &__item {
    max-width: 100%;
    max-height: 100%;
    overflow: hidden;
  }

  &--active {
    outline: 2px dashed $border;
    background-color: $active;
  }

  &--move {
    opacity: 0.6;
    background-color: $active;
  }

  &--click {
    cursor: pointer;
  }

  &__line {
    &--left {
      position: absolute;
      top: 0;
      width: 10000px;
      height: 0;
      border-top: 1px dashed $border;
      transform: translateX(-100%);
    }

    &--top {
      position: absolute;
      left: 0;
      width: 0;
      height: 10000px;
      border-left: 1px dashed $border;
      transform: translateY(-100%);
    }

    &--label {
      position: absolute;
      top: -5px;
      left: -8px;
      padding: 5px;
      color: $border;
      font-size: 18px;
      white-space: nowrap;
      transform: translate(-100%, -100%);
      cursor: move;
    }
  }

  &__range {
    position: absolute;
    width: 6px;
    height: 6px;
    border-radius: 100%;
    background-color: $border;
    z-index: 9999;

    &--left,
    &--right {
      top: 50%;

      &:hover {
        cursor: ew-resize;
      }
    }

    &--left {
      left: 0;
      transform: translate(-50%, -50%);
    }

    &--right {
      right: 0;
      transform: translate(50%, -50%);
    }

    &--top,
    &--bottom {
      left: 50%;

      &:hover {
        cursor: ns-resize;
      }
    }

    &--top {
      top: 0;
      transform: translate(-50%, -50%);
    }

    &--bottom {
      bottom: 0;
      transform: translate(-50%, 50%);
    }

    &--bottom-right,
    &--top-left {
      &:hover {
        cursor: nwse-resize;
      }
    }

    &--bottom-left,
    &--top-right {
      &:hover {
        cursor: nesw-resize;
      }
    }

    &--top-right {
      top: 0;
      right: 0;
      transform: translate(50%, -50%);
    }

    &--top-left {
      top: 0;
      left: 0;
      transform: translate(-50%, -50%);
    }

    &--bottom-right {
      right: 0;
      bottom: 0;
      transform: translate(50%, 50%);
    }

    &--bottom-left {
      left: 0;
      bottom: 0;
      transform: translate(-50%, 50%);
    }
  }
}
</style>
