<script lang="ts" setup>
const drBox = ref<HTMLElement | null>(null)
let isDragging = false
let initialX: number
let initialY: number
let initialLeft: number
let initialTop: number

let isResizing = false
let initialWidth: number
let initialHeight: number
let resizeDirection: string

const props = defineProps<{
  enable: boolean
  modelValue: {
    x: number
    y: number
    width: number
    height: number
  }
}>()

const emits = defineEmits([ 'mousedown', 'update:modelValue' ])

const style = computed(() => {
  return {
    left: props.modelValue.x + 'px',
    top: props.modelValue.y + 'px',
    width: props.modelValue.width === 0 ? 'max-content' : props.modelValue.width + 'px',
    height: props.modelValue.height === 0 ? 'max-content' : props.modelValue.height + 'px'
  }
})

function disableTextSelection() {
  document.body.style.userSelect = 'none'
}

function enableTextSelection() {
  document.body.style.userSelect = 'auto'
}

function moveStartHandler(e: MouseEvent) {
  emits('mousedown')
  disableTextSelection()
  isDragging = true
  initialX = e.clientX
  initialY = e.clientY
  initialLeft = parseInt(getComputedStyle(drBox.value!).left, 10)
  initialTop = parseInt(getComputedStyle(drBox.value!).top, 10)
  initialWidth = parseInt(getComputedStyle(drBox.value!).width, 10)
  initialHeight = parseInt(getComputedStyle(drBox.value!).height, 10)
}

function resizeStart(e: MouseEvent, direction: string) {
  disableTextSelection()
  isResizing = true
  initialX = e.clientX
  initialY = e.clientY
  initialWidth = parseInt(getComputedStyle(drBox.value!).width, 10)
  initialHeight = parseInt(getComputedStyle(drBox.value!).height, 10)
  initialLeft = parseInt(getComputedStyle(drBox.value!).left, 10)
  initialTop = parseInt(getComputedStyle(drBox.value!).top, 10)
  resizeDirection = direction
}

function processDragging(e: MouseEvent) {
  const dx = e.clientX - initialX
  const dy = e.clientY - initialY
  const x = initialLeft + dx
  const y = initialTop + dy
  emits('update:modelValue', {x, y, width: props.modelValue.width, height: props.modelValue.height})
}

const processResizing: { [key: string]: (e: MouseEvent) => void } = {
  'top-left'(e: MouseEvent) {
    const dx = e.clientX - initialX
    const dy = e.clientY - initialY
    const x = initialLeft + dx
    const y = initialTop + dy
    const width = initialWidth - dx
    const height = initialHeight - dy
    emits('update:modelValue', {x, y, width, height})
  },
  'top-right'(e: MouseEvent) {
    const dx = e.clientX - initialX
    const dy = e.clientY - initialY
    const y = initialTop + dy
    const width = initialWidth + dx
    const height = initialHeight - dy
    emits('update:modelValue', {x: props.modelValue.x, y, width, height})
  },

  'bottom-left'(e: MouseEvent) {
    const dx = e.clientX - initialX
    const dy = e.clientY - initialY
    const x = initialLeft + dx
    const width = initialWidth - dx
    const height = initialHeight + dy
    emits('update:modelValue', {x, y: props.modelValue.y, width, height})
  },

  'bottom-right'(e: MouseEvent) {
    const dx = e.clientX - initialX
    const dy = e.clientY - initialY
    const width = initialWidth + dx
    const height = initialHeight + dy
    emits('update:modelValue', {x: props.modelValue.x, y: props.modelValue.y, width, height})
  },
}

function movingHandler(e: MouseEvent) {
  disableTextSelection()
  if (isDragging) processDragging(e)
  if (isResizing) processResizing[resizeDirection](e)
}

function optEndHandler() {
  enableTextSelection()
  isDragging = false
  isResizing = false
}

document.addEventListener('mousemove', movingHandler)

document.addEventListener('mouseup', optEndHandler)

onMounted(() => {
  const x = parseInt(getComputedStyle(drBox.value!).left, 10)
  const y = parseInt(getComputedStyle(drBox.value!).top, 10)
  const width = parseInt(getComputedStyle(drBox.value!).width, 10)
  const height = parseInt(getComputedStyle(drBox.value!).height, 10)
  emits('update:modelValue', {x, y, width, height})
})

onUnmounted(() => {
  document.removeEventListener('mousemove', movingHandler)
  document.removeEventListener('mouseup', optEndHandler)
})

</script>

<template>
  <div id="draggable-resizable" ref="drBox" :style="style" class="draggable-resizable" @mousedown="moveStartHandler">
    <div :class="{disable: !enable}" class="resizer top-left" @mousedown.stop="resizeStart($event,'top-left')"></div>
    <div :class="{disable: !enable}" class="resizer top-right" @mousedown.stop="resizeStart($event,'top-right')"></div>
    <div :class="{disable: !enable}" class="resizer bottom-left" @mousedown.stop="resizeStart($event,'bottom-left')"></div>
    <div :class="{disable: !enable}" class="resizer bottom-right" @mousedown.stop="resizeStart($event,'bottom-right')"></div>
    <slot/>
  </div>
</template>

<style lang="stylus" scoped>
.draggable-resizable {
  position: absolute;
  cursor: move;
}


.resizer {
  position: absolute;
  width: 5px;
  height: 5px;
  background-color: #205DD9;
}

.disable.resizer {
  display none
}

.top-left {
  top: -5px;
  left: -5px;
  cursor: nwse-resize;
}

.top-right {
  top: -5px;
  right: -5px;
  cursor: nesw-resize;
}

.bottom-left {
  bottom: -5px;
  left: -5px;
  cursor: nesw-resize;
}

.bottom-right {
  bottom: -5px;
  right: -5px;
  cursor: nwse-resize;
}
</style>
