import { isSameSet } from "./utils";

/**
 * 键盘快捷键监听
 * @param {*} fn
 * @param {*} target
 * @param {*} timeout
 * @returns
 */
export const shortcutListener = (fn, target = document, timeout = 3000) => {
  const allkeys = new Set();
  const curkeys = new Set();
  let timer = null;

  const handleKeydown = (e) => {
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      allkeys.clear();
      curkeys.clear();
      clearTimeout(timer);
      timer = null;
    }, timeout);

    if (e.code) {
      allkeys.add(e.code);
      curkeys.add(e.code);
      fn([...curkeys], [...allkeys]);
    }
  };

  const handleKeyup = (e) => {
    curkeys.delete(e.code);
    if (curkeys.size === 0) {
      fn([...curkeys], [...allkeys]);
      allkeys.clear();
    }
  };

  target.addEventListener("keydown", handleKeydown);
  target.addEventListener("keyup", handleKeyup);
  return () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    target.removeEventListener("keydown", handleKeydown);
    target.removeEventListener("keyup", handleKeyup);
  };
};

/**
 * 注册键盘快捷键
 * @param {*} targetKeys
 * @param {*} fn
 * @param {*} target
 * @returns
 */
export const shortcutRegister = (targetKeys = [], fn, target = document) => {
  return shortcutListener((curkeys) => {
    if (
      targetKeys.length > 0 &&
      isSameSet(new Set(targetKeys), new Set(curkeys))
    ) {
      fn();
    }
  }, target);
};

/**
 * 注册连续快捷键
 * @param {*} targetKeys
 * @param {*} fn
 * @param {*} step
 * @param {*} timeout
 * @param {*} target
 * @returns
 */
export const stepShortcutRegister = (
  targetKeys = [],
  fn,
  step = 3,
  timeout = 500,
  target = document
) => {
  let count = 0;
  let pre = Date.now();
  let timer;
  return shortcutListener((curkeys, allkeys) => {
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      clearTimeout(timer);
      count = 0;
    }, timeout);

    if (targetKeys.length > 0 && curkeys.length === 0) {
      const now = Date.now();
      if (
        (count === 0 || now - pre < timeout) &&
        isSameSet(new Set(targetKeys), new Set(allkeys))
      ) {
        count++;
        if (count === step) {
          count = 0;
          fn();
        }
      } else {
        count = 0;
      }
      pre = now;
    }
  }, target);
};
