<template>
  <div class="v-tooltip">
    <teleport to="body">
      <div
        :class="[
          'v-tooltip-content',
          popperClass,
          { light: effect === 'light' },
        ]"
        ref="cTooltipContent"
        @mouseenter="handleMouseEnter('content')"
        @mouseleave="handleMouseLeave"
      >
        <slot name="content">{{ content }}</slot>
        <div
          v-if="visibleArrow"
          class="v-popper__arrow"
          ref="cPopperArrow"
        ></div>
      </div>
    </teleport>
    <div
      ref="cTooltipSlot"
      @mouseenter="handleMouseEnter('tooltip')"
      @mouseleave="handleMouseLeave"
    >
      <slot></slot>
    </div>
  </div>
</template>

<script>
import VairTooltip from "../../types/tooltip";
import { ref, onBeforeUnmount, watch, onMounted, watchEffect } from "vue";
export default {
  name: "tooltip",
  props: VairTooltip,
  setup(props) {
    const cTooltipContent = ref(null);
    const cPopperArrow = ref(null);
    const cTooltipSlot = ref(null);
    const timer = ref(null);
    const styleTimer = ref(null);
    const openDelayTimer = ref(null);
    const hideAfterTimer = ref(null);

    const handleMouseEnter = (type) => {
      if (props.disabled) return;
      if (props.manual) return;
      if (!props.enterable && type === "content") return;
      dealWithEnter();
    };

    const handleMouseLeave = () => {
      if (props.manual) return;
      dealWithLeave();
    };

    const dealWithEnter = () => {
      clearTimeout(timer.value);
      clearTimeout(styleTimer.value);
      clearTimeout(openDelayTimer.value);
      openDelayTimer.value = setTimeout(() => {
        cTooltipContent.value.style.opacity = 1;
        cTooltipContent.value.style.transform = "rotateX(0deg)";
        calculationPosition();
        clearTimeout(hideAfterTimer.value);
        if (props.hideAfter > 0) {
          hideAfterTimer.value = setTimeout(() => {
            dealWithLeave();
          }, props.hideAfter);
        }
      }, props.openDelay);
    };

    const dealWithLeave = () => {
      clearTimeout(openDelayTimer.value);
      clearTimeout(hideAfterTimer.value);
      timer.value = setTimeout(() => {
        cTooltipContent.value.style.opacity = 0;
        styleTimer.value = setTimeout(() => {
          cTooltipContent.value.style.transform = "rotateX(90deg)";
        }, 300);
      }, 200);
    };

    const calculationPosition = () => {
      const cTooltipContentHeight = cTooltipContent.value.clientHeight;
      const cTooltipContentWidth = cTooltipContent.value.clientWidth;
      const cTooltipSlotpHeight = cTooltipSlot.value.clientHeight;
      const cTooltipSlotpWidth = cTooltipSlot.value.clientWidth;
      const { x, y } = cTooltipSlot.value.getBoundingClientRect();
      var top = 0,
        left = 0;
      var cpaTop = 0,
        cpaLeft = 0,
        cpaRight = 0;
      var rotate = 0,
        compensate = 14;

      switch (props.placement) {
        case "top":
          top = y - cTooltipContentHeight - compensate;
          left = x + (cTooltipSlotpWidth - cTooltipContentWidth) / 2;
          cpaTop = cTooltipContentHeight;
          cpaLeft = cTooltipContentWidth / 2 - 6;
          break;
        case "top-start":
          top = y - cTooltipContentHeight - compensate;
          left = x;
          cpaTop = cTooltipContentHeight;
          cpaLeft = cTooltipSlotpWidth / 2 - 6;
          break;
        case "top-end":
          top = y - cTooltipContentHeight - compensate;
          left = x + (cTooltipSlotpWidth - cTooltipContentWidth);
          cpaTop = cTooltipContentHeight;
          cpaLeft = cTooltipContentWidth - cTooltipSlotpWidth / 2 - 6;
          break;
        case "left":
          rotate = -90;
          top = y + (cTooltipSlotpHeight - cTooltipContentHeight) / 2;
          left = x - cTooltipContentWidth - compensate;
          cpaTop = cTooltipContentHeight / 2 - 3;
          cpaLeft = cTooltipContentWidth - 3;
          break;
        case "left-start":
          rotate = -90;
          top = y;
          left = x - cTooltipContentWidth - compensate;
          cpaTop = cTooltipContentHeight / 2 - 3;
          cpaLeft = cTooltipContentWidth - 3;
          break;
        case "left-end":
          rotate = -90;
          top = y + cTooltipSlotpHeight - cTooltipContentHeight;
          left = x - cTooltipContentWidth - compensate;
          cpaTop = cTooltipContentHeight / 2 - 3;
          cpaLeft = cTooltipContentWidth - 3;
          break;
        case "right":
          rotate = 90;
          top = y + (cTooltipSlotpHeight - cTooltipContentHeight) / 2;
          left = x + cTooltipSlotpWidth + compensate;
          cpaTop = cTooltipContentHeight / 2 - 3;
          cpaLeft = -9;
          break;
        case "right-start":
          rotate = 90;
          top = y;
          left = x + cTooltipSlotpWidth + compensate;
          cpaTop = cTooltipContentHeight / 2 - 3;
          cpaLeft = -9;
          break;
        case "right-end":
          rotate = 90;
          top = y + cTooltipSlotpHeight - cTooltipContentHeight;
          left = x + cTooltipSlotpWidth + compensate;
          cpaTop = cTooltipContentHeight / 2 - 3;
          cpaLeft = -9;
          break;
        case "bottom":
          rotate = 180;
          top = y + cTooltipSlotpHeight + compensate;
          left = x + (cTooltipSlotpWidth - cTooltipContentWidth) / 2;
          cpaTop = -6;
          cpaLeft = cTooltipContentWidth / 2 - 6;
          break;
        case "bottom-start":
          rotate = 180;
          top = y + cTooltipSlotpHeight + compensate;
          left = x;
          cpaTop = -6;
          cpaLeft = cTooltipSlotpWidth / 2 - 6;
          break;
        case "bottom-end":
          rotate = 180;
          top = y + cTooltipSlotpHeight + compensate;
          left = x + (cTooltipSlotpWidth - cTooltipContentWidth);
          cpaTop = -6;
          cpaLeft = cTooltipContentWidth - cTooltipSlotpWidth / 2 - 6;
          break;
      }
      top = Math.ceil(top);
      left = Math.ceil(left);
      cTooltipContent.value.style.top = top + "px";
      cTooltipContent.value.style.left = left + "px";
      cPopperArrow.value.style.top = cpaTop + "px";
      cpaLeft && (cPopperArrow.value.style.left = cpaLeft + "px");
      cpaRight && (cPopperArrow.value.style.right = cpaRight + "px");
      rotate && (cPopperArrow.value.style.transform = `rotate(${rotate}deg)`);
    };

    onMounted(() => {
      watchEffect(() => {
        if (props.manual) {
          props.value ? dealWithEnter() : dealWithLeave();
        }
      });
      window.addEventListener("scroll", calculationPosition);
      window.addEventListener("resize", calculationPosition);
    });

    watch(
      () => props.disabled,
      () => {
        props.disabled && handleMouseLeave();
      }
    );

    onBeforeUnmount(() => {
      clearTimeout(timer.value);
      clearTimeout(styleTimer.value);
      clearTimeout(openDelayTimer.value);
      clearTimeout(hideAfterTimer.value);
      window.removeEventListener("scroll", calculationPosition);
      window.removeEventListener("resize", calculationPosition);
    });

    return {
      handleMouseEnter,
      handleMouseLeave,
      cTooltipContent,
      cPopperArrow,
      cTooltipSlot,
    };
  },
};
</script>

<style lang='less' scoped>
.v-tooltip {
  display: inline-block;
}
.v-tooltip-content {
  opacity: 0;
  position: fixed;
  z-index: 9999;
  line-height: 22px;
  white-space: nowrap;
  background: #333;
  color: #fff;
  font-size: 12px;
  border-radius: 4px;
  padding: 6px 10px;
  transform-origin: top center;
  transform: rotateX(90deg);
  transition: opacity 0.3s;
  .v-popper__arrow {
    position: absolute;
    border: 6px solid #333;
    border-left-color: transparent;
    border-right-color: transparent;
    border-bottom: none;
  }
}
.light {
  color: #000;
  background: #fff;
  border: 1px solid #333;
  .v-popper__arrow {
    &:after {
      position: absolute;
      top: -6px;
      left: -5px;
      display: block;
      content: " ";
      border: 5px solid #fff;
      border-left-color: transparent;
      border-right-color: transparent;
      border-bottom: none;
    }
  }
}
</style>