// 弹窗拖动
import { ref, Ref, watch, nextTick, computed, reactive } from 'vue';

type posType = {
  top: number;
  left: number;
};

type directionType =
  | 'TOP'
  | 'RIGHT'
  | 'BOTTOM'
  | 'LEFT'
  | 'TOP_RIGHT'
  | 'BOTTOM_RIGHT'
  | 'BOTTOM_LEFT'
  | 'TOP_LEFT';

type baseSizeType = {
  /**
   * 允许拖动方向 上/上右/右/下右/下/下左/左/上左
   */
  resizeDirection: boolean[];
  layoutTopHeight: number;
  windowHeight: number;
  windowWidth: number;
  // 窗口模式的尺寸
  winWidth: number;
  winHeight: number;
  winMinWidth: number;
  minWidth: number;
  minHeight: number;
  maxHeight: number;
  maxWidth: number;
  transformX: number;
  transformY: number;
  defaultWidth: number;
  defaultHeight: number;
  width: number;
  height: number;
};

type initSizeType = {
  /** 默认宽 */
  width?: number;
  /** 默认高 */
  height?: number;
  /** 最小宽 */
  minWidth?: number;
  /** 最小高 */
  minHeight?: number;
  /** 允许拖动方向 上/上右/右/下右/下/下左/左/上左 */
  resizeDirection?: boolean[];
  /** 初始定位 */
  defaultPosition?: string;
};

/***
 * 初始化默认弹窗位置
 */
const initPos = {
  right: 14,
  top: 60
};

const getSizeToUnit = (num: number, unit = 'px') => {
  return num > 0 ? num + unit : num + '';
};

/**
 * @params classList  可拖动地方的class值，也为唯一值
 * @params boxClass  容器class值必须为唯一值
 * @params dragShow  弹窗是否显示
 * @params initSize 默认尺寸
 */
export default function useDrag(
  classList: string[],
  boxClass: string,
  dragShow: Ref<boolean>,
  initSize?: initSizeType
) {
  const windowInfo = reactive({
    // 小窗口 侧边大窗口
    currentType: 'SMALL' as 'SMALL' | 'LARGE',
    // 弹窗，还是还原
    windowType: 'SMALL' as 'SMALL' | 'LARGE',
    // showScreen: false, // 是否全屏显示
    showType: 'MENU' as 'MENU' | 'CONTENT' // 当前显示哪一部分 - 如果是全屏显示则无效
  });
  const pos = ref<posType>({
    top: -1, // -1 为初始值 代表没有缓存 默认居中
    left: -1
  });

  watch(
    () => windowInfo.windowType,
    () => {
      if (windowInfo.windowType === 'LARGE') {
        baseSize.resizeDirection = [
          true,
          true,
          true,
          true,
          true,
          true,
          true,
          true
        ];
      } else if (windowInfo.windowType === 'SMALL') {
        baseSize.resizeDirection = [
          true,
          false,
          false,
          false,
          true,
          false,
          false,
          false
        ];
      }
      const dragDirectionPoints = document.querySelectorAll(
        `.${boxClass} .dragDirectionPoint`
      );
      dragDirectionPoints.forEach((element: any, index) => {
        if (baseSize.resizeDirection[index]) {
          element.style.pointerEvents = 'all';
        } else {
          element.style.pointerEvents = 'none';
        }
      });
    }
  );

  const styleDrag = computed(() => {
    return {
      ...dragStyles,
      width: getSizeToUnit(baseSize.width),
      height: getSizeToUnit(baseSize.height),
      transform: `translate(${baseSize.transformX}px, ${baseSize.transformY}px)`
    };
  });

  const baseSize = reactive<baseSizeType>({
    resizeDirection: initSize?.resizeDirection || [
      true,
      false,
      false,
      false,
      true,
      false,
      false,
      false
    ],
    layoutTopHeight: 0,
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth,
    // 窗口模式的尺寸
    winWidth: 1010,
    winHeight: 650,
    winMinWidth: initSize?.minWidth || 800,
    minWidth: initSize?.minWidth || 400,
    minHeight: initSize?.minHeight || 340,
    maxHeight: window.innerHeight,
    maxWidth: window.innerWidth > 1024 ? 1024 : window.innerWidth,
    transformX: window.innerWidth - 400 - initPos.right,
    transformY: (window.innerHeight - 640) / 2,
    defaultWidth: initSize?.width || 400,
    defaultHeight: initSize?.height || 640,
    height: 0,
    width: 0
  });
  const dragStyles = reactive({
    maxHeight: getSizeToUnit(baseSize.maxHeight),
    minWidth: getSizeToUnit(baseSize.minWidth),
    minHeight: getSizeToUnit(baseSize.minHeight)
  });

  watch(dragShow, () => {
    if (dragShow.value) {
      // 初始化pos值
      window.addEventListener('resize', refreshPos);
      window.addEventListener('fullscreenchange', onFullscreenchange);
      nextTick(() => {
        document
          .querySelectorAll(`.${boxClass} .dragDirectionPoint`)
          .forEach((value: Element) => {
            value.remove();
          });
        const layoutTopHeight =
          document.querySelector('.layoutTop')?.clientHeight || 0;
        baseSize.layoutTopHeight = Math.ceil(layoutTopHeight);
        baseSize.windowHeight = window.innerHeight - layoutTopHeight;
        baseSize.windowWidth = window.innerWidth;
        baseSize.maxHeight = window.innerHeight - layoutTopHeight;
        baseSize.maxWidth = window.innerWidth > 1024 ? 1024 : window.innerWidth;
        // 判断窗口的高度与默认高度比例
        if (baseSize.defaultHeight >= baseSize.maxHeight) {
          baseSize.defaultHeight = baseSize.maxHeight - 100;
        }
        if (baseSize.winHeight >= baseSize.maxHeight) {
          baseSize.winHeight = baseSize.maxHeight - 100;
        }

        dragStyles.maxHeight = getSizeToUnit(baseSize.maxHeight);

        if (windowInfo.windowType === 'LARGE') {
          baseSize.transformX = (baseSize.windowWidth - baseSize.winWidth) / 2;
          baseSize.transformY =
            (baseSize.windowHeight - baseSize.winHeight) / 2 -
            baseSize.layoutTopHeight / 2;
          baseSize.width = baseSize.winWidth;
          baseSize.height = baseSize.winHeight;
        } else {
          const translateY =
            (baseSize.windowHeight - baseSize.defaultHeight) / 2;
          baseSize.transformX =
            baseSize.windowWidth - baseSize.defaultWidth - initPos.right;
          baseSize.transformY = translateY;
          baseSize.width = baseSize.defaultWidth;
          baseSize.height = baseSize.defaultHeight;
        }

        // 初始化定位
        if (initSize?.defaultPosition === 'center') {
          const transformX = (window.innerWidth - baseSize.defaultWidth) / 2;
          const transformY =
            (baseSize.windowHeight - baseSize.defaultHeight) / 2;
          baseSize.transformX = transformX;
          baseSize.transformY = transformY;
        }

        const boxClassDom = document.querySelector(
          `.${boxClass}`
        ) as HTMLElement;
        if (!boxClassDom) {
          return;
        }
        addReSizeDom(boxClassDom, baseSize.resizeDirection);

        classList.map((className: string) => {
          const classDom = document.querySelector(
            `.${className}`
          ) as HTMLElement;
          if (classDom) {
            classDom.style.cursor = 'move';
            drag(classDom, boxClassDom, baseSize);
          }
        });
      });
    } else {
      window.removeEventListener('resize', refreshPos);
      window.removeEventListener('fullscreenchange', onFullscreenchange);
    }
  });

  /**
   * 添加功能放大缩小操作DOM
   * @param parentElement {添加拖动父级元素}
   * @param direction {允许拖动的位置 上/上右/右/下右/下/下左/左/上左}
   */
  function addReSizeDom(parentElement: HTMLElement, direction: boolean[] = []) {
    function addResizeDirection(params: {
      width: string;
      height: string;
      direction: directionType;
      top?: string | any;
      right?: string | any;
      bottom?: string | any;
      left?: string | any;
      cursor: string;
      zIndex?: string;
      pointerEvents: string;
    }) {
      const dom = document.createElement('div');
      dom.className = 'dragDirectionPoint';
      dom.style.position = 'absolute';
      dom.style.userSelect = 'none';
      dom.style.width = params.width;
      dom.style.height = params.height;
      dom.style.left = params.left;
      dom.style.top = params.top;
      dom.style.bottom = params.bottom;
      dom.style.right = params.right;
      dom.style.zIndex = params.zIndex || '9';
      dom.style.cursor = params.cursor;
      dom.style.pointerEvents = params.pointerEvents;
      parentElement.appendChild(dom);
      drag(dom, parentElement, baseSize, 'RESIZE', params.direction);
    }
    // 上
    addResizeDirection({
      width: '100%',
      height: '10px',
      left: '0',
      top: '-5px',
      cursor: 'row-resize',
      direction: 'TOP',
      pointerEvents: direction[0] ? 'all' : 'none'
    });

    // 上右
    addResizeDirection({
      width: '20px',
      height: '20px',
      right: '-10px',
      top: '-10px',
      zIndex: '10',
      cursor: 'ne-resize',
      direction: 'TOP_RIGHT',
      pointerEvents: direction[1] ? 'all' : 'none'
    });

    // 右
    addResizeDirection({
      width: '10px',
      height: '100%',
      top: '0',
      right: '-5px',
      cursor: 'col-resize',
      direction: 'RIGHT',
      pointerEvents: direction[2] ? 'all' : 'none'
    });

    // 下右
    addResizeDirection({
      width: '20px',
      height: '20px',
      right: '-10px',
      bottom: '-10px',
      cursor: 'se-resize',
      zIndex: '10',
      direction: 'BOTTOM_RIGHT',
      pointerEvents: direction[3] ? 'all' : 'none'
    });

    // 下
    addResizeDirection({
      width: '100%',
      height: '10px',
      left: '0',
      bottom: '-5px',
      cursor: 'row-resize',
      direction: 'BOTTOM',
      pointerEvents: direction[4] ? 'all' : 'none'
    });

    // 下左
    addResizeDirection({
      width: '20px',
      height: '20px',
      left: '-10px',
      bottom: '-10px',
      cursor: 'sw-resize',
      zIndex: '10',
      direction: 'BOTTOM_LEFT',
      pointerEvents: direction[5] ? 'all' : 'none'
    });

    // 左
    addResizeDirection({
      width: '10px',
      height: '100%',
      top: '0',
      left: '-5px',
      cursor: 'col-resize',
      direction: 'LEFT',
      pointerEvents: direction[6] ? 'all' : 'none'
    });

    // 上左
    addResizeDirection({
      width: '20px',
      height: '20px',
      left: '-10px',
      top: '-10px',
      cursor: 'nw-resize',
      zIndex: '10',
      direction: 'TOP_LEFT',
      pointerEvents: direction[7] ? 'all' : 'none'
    });
  }

  let isResizeBlocked = false; // 标志是否阻止 resize 事件
  /** 全屏 */
  function isFullscreen() {
    return document.fullscreenElement || // 支持标准
      (document as any).mozFullScreenElement || // Firefox
      (document as any).webkitFullscreenElement // Chrome, Safari
      ? true
      : false;
  }
  function onFullscreenchange() {
    if (isFullscreen()) {
      isResizeBlocked = true;
    } else {
      setTimeout(() => {
        isResizeBlocked = false;
      }, 100);
    }
  }
  function refreshPos() {
    // 全屏切换的时候不做重置
    if (isResizeBlocked || isFullscreen()) return;

    windowInfo.currentType = 'SMALL';
    windowInfo.windowType = 'SMALL';
    baseSize.winMinWidth = initSize?.minWidth || 800;
    baseSize.minWidth = initSize?.minWidth || 400;
    baseSize.minHeight = initSize?.minHeight || 340;
    baseSize.defaultWidth = initSize?.width || 400;
    baseSize.defaultHeight = initSize?.height || 640;
    isResizeBlocked = false;

    dragShow.value = false;
  }

  /** 切换窗口 */
  function onScreen() {
    if (windowInfo.windowType === 'SMALL') {
      windowInfo.windowType = 'LARGE';
      baseSize.transformX = (baseSize.windowWidth - baseSize.winWidth) / 2;
      baseSize.transformY =
        (baseSize.windowHeight - baseSize.winHeight) / 2 -
        baseSize.layoutTopHeight / 2;
      baseSize.width = baseSize.winWidth;
      baseSize.height = baseSize.winHeight;
    } else if (windowInfo.windowType === 'LARGE') {
      windowInfo.windowType = 'SMALL';
      if (windowInfo.currentType === 'SMALL') {
        baseSize.transformX =
          baseSize.windowWidth - baseSize.defaultWidth - initPos.right;
        baseSize.transformY =
          baseSize.windowHeight - baseSize.defaultHeight - initPos.top;
        baseSize.width = baseSize.defaultWidth;
        baseSize.height = baseSize.defaultHeight;
      } else if (windowInfo.currentType === 'LARGE') {
        baseSize.transformX = baseSize.windowWidth - baseSize.defaultWidth;
        baseSize.transformY = 0;
        baseSize.width = baseSize.defaultWidth;
        baseSize.height = baseSize.maxHeight;
      }
    }
  }

  /** 格式化尺寸 */
  function onResize() {
    windowInfo.windowType = 'SMALL';
    if (windowInfo.currentType === 'SMALL') {
      windowInfo.currentType = 'LARGE';
      baseSize.transformX = baseSize.windowWidth - baseSize.defaultWidth;
      baseSize.transformY = 0;
      baseSize.width = baseSize.defaultWidth;
      baseSize.height = baseSize.maxHeight;
    } else if (windowInfo.currentType === 'LARGE') {
      windowInfo.currentType = 'SMALL';
      baseSize.transformX =
        baseSize.windowWidth - baseSize.defaultWidth - initPos.right;
      baseSize.transformY =
        baseSize.windowHeight - baseSize.defaultHeight - initPos.top;
      baseSize.width = baseSize.defaultWidth;
      baseSize.height = baseSize.defaultHeight;
    }
  }

  /** 重置样式 */
  function onReset() {
    windowInfo.currentType = 'SMALL';
    windowInfo.windowType = 'SMALL';
    if (initSize?.defaultPosition === 'center') {
      const transformX = (window.innerWidth - baseSize.defaultWidth) / 2;
      const transformY = (baseSize.windowHeight - baseSize.defaultHeight) / 2;
      baseSize.transformX = transformX;
      baseSize.transformY = transformY;
    } else {
      baseSize.transformX =
        baseSize.windowWidth - baseSize.defaultWidth - initPos.right;
      baseSize.transformY =
        baseSize.windowHeight - baseSize.defaultHeight - initPos.top;
    }

    baseSize.width = baseSize.defaultWidth;
    baseSize.height = baseSize.defaultHeight;
  }

  return {
    pos,
    baseSize,
    windowInfo,
    styleDrag,
    onScreen,
    onResize,
    onReset
  };
}

// 拖动
function drag(
  el: HTMLElement,
  parentElement: HTMLElement,
  baseSize: baseSizeType,
  type = 'MOVE' as 'MOVE' | 'RESIZE',
  direction?: directionType
) {
  function onDown(e: MouseEvent | TouchEvent) {
    const isTouchEv = isTouchEvent(e);
    const event = isTouchEv ? e.touches[0] : e;
    const parentElementRect = parentElement.getBoundingClientRect();
    const downX = event.clientX;
    const downY = event.clientY;
    const clientWidth = document.documentElement.clientWidth;
    const clientHeight = document.documentElement.clientHeight;
    const maxLeft = clientWidth - parentElementRect.width;
    const maxTop =
      clientHeight - parentElementRect.height - baseSize.layoutTopHeight;

    const maxResizeLeft =
      clientWidth -
      baseSize.winMinWidth -
      (clientWidth - parentElementRect.right);
    // const maxResizeTop =
    //   clientHeight - baseSize.minHeight - baseSize.layoutTopHeight;
    const minLeft = 0;
    const minTop = 0;
    const baseHeight = JSON.parse(JSON.stringify(baseSize.height));
    const baseWidth = JSON.parse(JSON.stringify(baseSize.width));

    function onTop(moveY: number) {
      const maxSuffix =
        parentElementRect.bottom -
        baseSize.minHeight -
        baseSize.layoutTopHeight;
      moveY = moveY > maxSuffix ? maxSuffix : moveY;
      const suffix = baseSize.transformY - moveY;
      if (suffix > 0 || baseSize.height > baseSize.minHeight) {
        baseSize.transformY = moveY;
        baseSize.height = baseSize.height + suffix;
      }
    }
    function onRight(moveX: number) {
      const suffix = Math.ceil(
        baseWidth + moveX - (baseSize.width + baseSize.transformX)
      );
      if (suffix > 0 || baseSize.width > baseSize.winMinWidth) {
        baseSize.width =
          baseSize.width + suffix >= baseSize.maxWidth
            ? baseSize.maxWidth
            : baseSize.width + suffix;
      }
    }
    function onBottom(moveY: number) {
      const suffix = Math.ceil(
        baseHeight + moveY - (baseSize.height + baseSize.transformY)
      );
      if (suffix > 0 || baseSize.height >= baseSize.minHeight) {
        if (baseSize.height + suffix <= baseSize.maxHeight) {
          baseSize.height = baseSize.height + suffix;
        }
      }
    }
    function onLeft(moveX: number) {
      moveX =
        moveX < minLeft
          ? minLeft
          : moveX > maxResizeLeft
          ? maxResizeLeft
          : moveX;
      const suffix = baseSize.transformX - moveX;
      if (suffix > 0 || baseSize.width > baseSize.winMinWidth) {
        if (baseSize.width + suffix <= baseSize.maxWidth) {
          baseSize.transformX = moveX;
          baseSize.width = baseSize.width + suffix;
        }
      }
    }
    function onMove(e: MouseEvent | TouchEvent) {
      const event = isTouchEvent(e) ? e.touches[0] : e;
      if (type === 'MOVE') {
        let moveX = parentElementRect.left + (event.clientX - downX);
        let moveY =
          parentElementRect.top -
          baseSize.layoutTopHeight +
          (event.clientY - downY);
        moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX;
        moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY;
        // 移动
        baseSize.transformY = moveY;
        baseSize.transformX = moveX;
      } else if (type === 'RESIZE') {
        let moveY =
          parentElementRect.top -
          baseSize.layoutTopHeight +
          (event.clientY - downY);
        const moveX = parentElementRect.left + (event.clientX - downX);

        // 拖动
        if (direction === 'TOP') {
          moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY;
          onTop(moveY);
        } else if (direction === 'RIGHT') {
          onRight(moveX);
        } else if (direction === 'BOTTOM') {
          onBottom(moveY);
        } else if (direction === 'LEFT') {
          onLeft(moveX);
        } else if (direction === 'TOP_RIGHT') {
          onTop(moveY);
          onRight(moveX);
        } else if (direction === 'BOTTOM_RIGHT') {
          onBottom(moveY);
          onRight(moveX);
        } else if (direction === 'BOTTOM_LEFT') {
          onBottom(moveY);
          onLeft(moveX);
        } else if (direction === 'TOP_LEFT') {
          onTop(moveY);
          onLeft(moveX);
        }
      }
    }
    function onUp() {
      document.removeEventListener(
        isTouchEv ? 'touchmove' : 'mousemove',
        onMove
      );
      document.removeEventListener(isTouchEv ? 'touchend' : 'mouseup', onUp);
    }
    document.addEventListener(isTouchEv ? 'touchmove' : 'mousemove', onMove);
    document.addEventListener(isTouchEv ? 'touchend' : 'mouseup', onUp);
  }
  el.addEventListener('mousedown', onDown);
  el.addEventListener('touchstart', onDown);
}
function isTouchEvent(e: MouseEvent | TouchEvent): e is TouchEvent {
  return window.TouchEvent && e instanceof window.TouchEvent;
}
