<script lang="tsx">
import {
  defineComponent, onBeforeUnmount, onMounted, type PropType, type Ref, ref,
} from 'vue'
import ScrollBar from '../scrollbar/scrollbar'

let counter = 0

class ScrollFocusEvent extends Event {
  id?: string
}

/**
 * Компонент, для отображения большого контента внутри себя \
 * Позволяет прокручивать внутри себя контент в любом направлении
 */
export default defineComponent({
  name: 'ScrollArea',
  props: {
    /**
     * Опция отображения полосы прокрутки по горизонтали
     */
    hasHorizontalScroll: {
      type: Boolean,
      default: true,
    },

    /**
     * Опция отображения полосы прокрутки по вертикали
     */
    hasVerticalScroll: {
      type: Boolean,
      default: true,
    },

    /**
     * Позиция отображения полосы прокрутки по горизонтали
     *
     * @prop 'top' - отображается сверху
     * @prop 'bottom' - отображается снизу
     *
     * @default 'bottom'
     */
    horizontalScrollPosition: {
      type: String as PropType<'top'|'bottom'>,
      default: 'bottom',
    },

    /**
     * Позиция отображения полосы прокрутки по вертикали
     *
     * @prop 'left' - отображается слева
     * @prop 'right' - отображается справа
     *
     * @default 'right'
     */
    verticalScrollPosition: {
      type: String as PropType<'left'|'right'>,
      default: 'right',
    },
  },
  setup(props, app) {
    const horizontalPosition = ref(0)
    const verticalPosition = ref(0)
    const root: Ref<HTMLElement|undefined> = ref(undefined)
    counter += 1
    const id = String(counter)
    const blocked = ref(false)

    const setHorizontalScroll = (n: number) => {
      horizontalPosition.value = n
      root.value?.scrollTo({ left: horizontalPosition.value, behavior: 'smooth' })
    }

    const setVerticalScroll = (n: number) => {
      verticalPosition.value = n
      root.value?.scrollTo({ top: verticalPosition.value, behavior: 'smooth' })
    }

    const scrollUpABit = (delta = 10) => {
      const value = verticalPosition.value - delta
      if (value < 0) return
      setVerticalScroll(value)
    }

    const scrollDownABit = (delta = 10) => {
      const value = verticalPosition.value + delta
      if (value > Number(root.value?.clientHeight)) return
      setVerticalScroll(value)
    }

    const scrollLeftABit = (delta = 10) => {
      const value = horizontalPosition.value - delta
      if (value < 0) return
      setHorizontalScroll(value)
    }

    const scrollRightABit = (delta = 10) => {
      const value = horizontalPosition.value + delta
      if (value > Number(root.value?.clientWidth)) return
      setHorizontalScroll(value)
    }

    const onKeyPress = (e: KeyboardEvent) => {
      if (blocked.value) return
      if (e.key === 'ArrowDown') scrollDownABit()
      if (e.key === 'ArrowUp') scrollUpABit()
      if (e.key === 'ArrowLeft') scrollLeftABit()
      if (e.key === 'ArrowRight') scrollRightABit()
      e.preventDefault()
    }

    const onTouchmove = (e: any) => {
      if (blocked.value) return
      e.preventDefault()
      if (e.deltaY > 0) scrollDownABit(Math.abs(e.deltaY) / 2)
      if (e.deltaY < 0) scrollUpABit(Math.abs(e.deltaY) / 2)
      if (e.deltaX > 0) scrollLeftABit(Math.abs(e.deltaX))
      if (e.deltaX < 0) scrollRightABit(Math.abs(e.deltaX))
    }

    const mouseenterHandler = () => {
      document.addEventListener('keydown', onKeyPress)
      window.addEventListener('wheel', onTouchmove, { passive: false })
      const event = new ScrollFocusEvent('scrollFocus')
      event.id = id
      document.dispatchEvent(event)
      blocked.value = false
    }

    const mouseleaveHandler = () => {
      document.removeEventListener('keydown', onKeyPress)
      window.removeEventListener('wheel', onTouchmove)
      const event = new ScrollFocusEvent('scrollFocusOut')
      event.id = id
      document.dispatchEvent(event)
    }

    const scrollFocus = (e: ScrollFocusEvent) => {
      if (e.id !== id) {
        blocked.value = true
      }
    }

    const scrollFocusOut = (e: ScrollFocusEvent) => {
      blocked.value = false
    }

    onMounted(() => {
      document.addEventListener('scrollFocus', scrollFocus)
      document.addEventListener('scrollFocusOut', scrollFocusOut)
    })

    onBeforeUnmount(() => {
      document.removeEventListener('scrollFocus', scrollFocus)
      document.removeEventListener('scrollFocusOut', scrollFocusOut)
    })

    return () => (
      <div
        class="ark-ui-scroll-area"
        onMouseenter={mouseenterHandler}
        onMouseleave={mouseleaveHandler}
      >
        <div
          class={`ark-ui-scroll-area-inner has-padding-${props.horizontalScrollPosition} has-padding-${props.verticalScrollPosition}`}
          ref={root}
        >
          { app.slots.default && app.slots.default() }
        </div>
        { props.hasHorizontalScroll && (
          <div class={`ark-ui-scroll-area-horizontal-scrollbar ${props.horizontalScrollPosition}`}>
            <ScrollBar
              type="horizontal"
              scroll={(horizontalPosition.value / Number(root.value?.clientWidth)) * 100}
              onScroll={(n) => setHorizontalScroll((n * Number(root.value?.clientWidth)) / 100)}
            />
          </div>
        ) }
        { props.hasVerticalScroll && (
          <div class={`ark-ui-scroll-area-vertical-scrollbar ${props.verticalScrollPosition}`}>
            <ScrollBar
              type="vertical"
              scroll={(verticalPosition.value / Number(root.value?.clientHeight)) * 100}
              onScroll={(n) => setVerticalScroll((n * Number(root.value?.clientHeight)) / 100)}
            />
          </div>
        ) }
      </div>
    )
  },
})
</script>

<style lang="scss" scoped>
@import '../../styles/variables.scss';

.ark-ui-scroll-area {
  height: 100%;
  position: relative;
  width: 100%;
}
.ark-ui-scroll-area-inner {
  box-sizing: border-box;
  height: 100%;
  overflow: hidden;
  width: 100%;
  &.has-padding-left {
    padding-left: 8px;
  }
  &.has-padding-right {
    padding-right: 8px;
  }
  &.has-padding-top {
    padding-top: 8px;
  }
  &.has-padding-bottom {
    padding-bottom: 8px;
  }
}
.ark-ui-scroll-area-horizontal-scrollbar-switch, .ark-ui-scroll-area-vertical-scrollbar-switch {
  background-color: $font-color;
  border-radius: 4px;
}
.ark-ui-scroll-area-vertical-scrollbar {
  background: white;
  cursor: pointer;
  height: 100%;
  position: absolute;
  top: 0;
  width: 8px;
  &.left {
    left: 0;
  }
  &.right {
    right: 0;
  }
  .ark-ui-scroll-area-vertical-scrollbar-switch {
    height: 40px;
    width: 100%;
  }
}
.ark-ui-scroll-area-horizontal-scrollbar {
  background: white;
  cursor: pointer;
  height: 8px;
  left: 0;
  position: absolute;
  width: 100%;
  &.top {
    top: 0;
  }
  &.bottom {
    bottom: 0;
  }
  .ark-ui-scroll-area-horizontal-scrollbar-switch {
    height: 100%;
    width: 40px;
  }
}
</style>
