<script lang="ts">
import type { PropType } from 'vue'
import { computed, defineComponent, ref, watch } from 'vue'
import { useLocalStorage } from '@vueuse/core'
import { useOrientation } from './orientation'

export default defineComponent({
  props: {
    defaultSplit: {
      type: Number,
      default: 50,
    },

    min: {
      type: Number,
      default: 20,
    },

    max: {
      type: Number,
      default: 80,
    },

    draggerOffset: {
      type: String as PropType<'before' | 'center' | 'after'>,
      default: 'center',
      validator: (value: string) => ['before', 'center', 'after'].includes(value),
    },

    saveId: {
      type: String,
      default: null,
    },

    collapsableLeft: {
      type: Boolean,
      default: false,
    },

    collapsableRight: {
      type: Boolean,
      default: false,
    },
  },

  emits: ['leftCollapsed', 'rightCollapsed'],

  setup(props, { emit }) {
    const { orientation } = useOrientation()

    const split = props.saveId ? useLocalStorage(`split-pane-${props.saveId}`, props.defaultSplit) : ref(props.defaultSplit)
    const leftCollapsed = props.saveId ? useLocalStorage(`split-pane-collapsed-left-${props.saveId}`, false) : ref(false)
    const rightCollapsed = props.saveId ? useLocalStorage(`split-pane-collapsed-right-${props.saveId}`, false) : ref(false)

    watch(leftCollapsed, (value) => {
      emit('leftCollapsed', value)
    }, {
      immediate: true,
    })

    watch(rightCollapsed, (value) => {
      emit('rightCollapsed', value)
    }, {
      immediate: true,
    })

    const boundSplit = computed(() => {
      if (split.value < props.min) {
        return props.min
      }
      else if (split.value > props.max) {
        return props.max
      }
      else {
        return split.value
      }
    })

    const leftStyle = computed(() => ({
      [orientation.value === 'landscape' ? 'width' : 'height']: `${leftCollapsed.value ? 0 : rightCollapsed.value ? 100 : boundSplit.value}%`,
    }))

    const rightStyle = computed(() => ({
      [orientation.value === 'landscape' ? 'width' : 'height']: `${rightCollapsed.value ? 0 : leftCollapsed.value ? 100 : 100 - boundSplit.value}%`,
    }))

    const dragging = ref(false)
    let startPosition = 0
    let startSplit = 0
    const el = ref(null)

    function dragStart(e: MouseEvent) {
      dragging.value = true
      startPosition = orientation.value === 'landscape' ? e.pageX : e.pageY
      startSplit = boundSplit.value
    }

    function dragMove(e: MouseEvent) {
      if (dragging.value) {
        let position
        let totalSize
        if (orientation.value === 'landscape') {
          position = e.pageX
          totalSize = el.value.offsetWidth
        }
        else {
          position = e.pageY
          totalSize = el.value.offsetHeight
        }
        const dPosition = position - startPosition
        split.value = startSplit + ~~(dPosition / totalSize * 100)
      }
    }

    function dragEnd() {
      dragging.value = false
    }

    // Collapsing

    function collapseLeft() {
      if (rightCollapsed.value) {
        rightCollapsed.value = false
      }
      else {
        leftCollapsed.value = true
      }
    }

    function collapseRight() {
      if (leftCollapsed.value) {
        leftCollapsed.value = false
      }
      else {
        rightCollapsed.value = true
      }
    }

    return {
      el,
      dragging,
      orientation,
      dragStart,
      dragMove,
      dragEnd,
      leftStyle,
      rightStyle,
      leftCollapsed,
      rightCollapsed,
      collapseLeft,
      collapseRight,
    }
  },
})
</script>

<template>
  <div
    ref="el"
    class="flex h-full"
    :class="{
      'flex-col meow': orientation === 'portrait',
      'cursor-ew-resize': dragging && orientation === 'landscape',
      'cursor-ns-resize': dragging && orientation === 'portrait',
      [orientation]: true,
    }"
    @mousemove="dragMove"
    @mouseup="dragEnd"
    @mouseleave="dragEnd"
  >
    <div
      class="relative top-0 left-0 overflow-hidden"
      :class="{
        'pointer-events-none': dragging,
        'border-r border-gray-200 dark:border-gray-700': orientation === 'landscape' && !leftCollapsed && !rightCollapsed,
      }"
      :style="leftStyle"
    >
      <slot
        v-if="!leftCollapsed"
        name="left"
      />

      <div
        v-if="!leftCollapsed && !rightCollapsed"
        class="dragger absolute z-100 hover:bg-green-500 hover:bg-opacity-25 transition-colors duration-150 delay-150"
        :class="{
          'top-0 bottom-0 cursor-ew-resize': orientation === 'landscape',
          'left-0 right-0 cursor-ns-resize': orientation === 'portrait',
          [`dragger-offset-${draggerOffset}`]: true,
        }"
        @mousedown.prevent="dragStart"
      />

      <div
        v-if="(collapsableLeft && !leftCollapsed) || rightCollapsed"
        class="collapse-btn absolute z-[101] flex items-center pointer-events-none"
        :class="{
          'top-0 bottom-0 right-0': orientation === 'landscape',
          'left-0 right-0 bottom-0 flex-col': orientation === 'portrait',
        }"
      >
        <button
          v-tooltip="rightCollapsed ? `Expand ${orientation === 'landscape' ? 'Right' : 'Bottom'} pane` : `Collapse ${orientation === 'landscape' ? 'Left' : 'Top'} pane`"
          class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 flex items-center justify-center w-2.5 h-6 rounded overflow-hidden pointer-events-auto opacity-70 hover:opacity-100"
          :class="{
            'rounded-r-none border-r-0': orientation === 'landscape',
            'rounded-b-none border-b-0': orientation === 'portrait',
          }"
          @click="collapseLeft()"
        >
          <VueIcon
            :icon="orientation === 'landscape' ? 'arrow_left' : 'arrow_drop_up'"
            class="flex-none w-5 h-5"
          />
        </button>
      </div>
    </div>
    <div
      class="relative bottom-0 right-0"
      :class="{
        'pointer-events-none': dragging,
        'border-t border-gray-200 dark:border-gray-700': orientation === 'portrait',
      }"
      :style="rightStyle"
    >
      <div
        v-if="(collapsableRight && !rightCollapsed) || leftCollapsed"
        class="collapse-btn absolute z-[101] flex items-center pointer-events-none"
        :class="{
          'top-0 bottom-0 left-0': orientation === 'landscape',
          'left-0 right-0 top-0 flex-col': orientation === 'portrait',
        }"
      >
        <button
          v-tooltip="leftCollapsed ? `Expand ${orientation === 'landscape' ? 'Left' : 'Top'} pane` : `Collapse ${orientation === 'landscape' ? 'Right' : 'Bottom'} pane`"
          class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 flex items-center justify-center w-2.5 h-6 rounded overflow-hidden pointer-events-auto opacity-70 hover:opacity-100"
          :class="{
            'rounded-l-none border-l-0': orientation === 'landscape',
            'rounded-t-none border-t-0': orientation === 'portrait',
          }"
          @click="collapseRight()"
        >
          <VueIcon
            :icon="orientation === 'landscape' ? 'arrow_right' : 'arrow_drop_down'"
            class="flex-none w-5 h-5"
          />
        </button>
      </div>

      <slot
        v-if="!rightCollapsed"
        name="right"
      />
    </div>
  </div>
</template>

<style lang="postcss" scoped>
.dragger {
  .landscape & {
    width: 10px;
  }

  .portrait & {
    height: 10px;
  }

  &.dragger-offset-before {
    .landscape & {
      right: 0;
    }

    .portrait & {
      bottom: 0;
    }
  }

  &.dragger-offset-center {
    .landscape & {
      right: -5px;
    }

    .portrait & {
      bottom: -5px;
    }
  }

  &.dragger-offset-after {
    .landscape & {
      right: -10px;
    }

    .portrait & {
      bottom: -10px;
    }
  }
}

.collapse-btn {
  button {
    .portrait & {
      @apply w-6 h-2.5;
    }
  }
}
</style>
