type ExtraConfig = {
  extraScore: number,
  done: boolean
  extraTime: number,
  landCb: (...args: any[]) => void
}

export class Tools {
  static PageData: {
    isFirstGame: boolean
    skinName: string,
    gameDiff: {
      score: number
      speed: number
    }[],
    extraFunc?: (...args: any[]) => ExtraConfig
  } = {
    isFirstGame: false,
    skinName: '',
    gameDiff: []
  }
}


/**
 * 函数防抖，如下拉菜单
 * @param {Function} fn 
 * @param {Number} delay 
 * @returns 
 */
 export function debounce(fn: Function, delay: number=2000) {
  let timer:any = null;
  function invokeFunc(...args: any[]) {
    let context = this;
    if(timer) clearTimeout(timer)
    timer = setTimeout(()=>{
      fn.apply(context, args)
    }, delay)
  }
  function cancel() {
    if (timer !== undefined) {
      clearTimeout(timer)
    }
  }
  invokeFunc.cancel = cancel
  return invokeFunc
}

/**
 * 函数防抖的 装饰器版本
 * @param time 
 * @returns 
 */
export function debounceDecorator(time: number = 1000) {
  return function (target: any, property: string, descriptor: PropertyDescriptor) {
    const func = descriptor.value
    if (typeof func !== 'function') {
      throw new Error('debounceDecorator error: are u kidding me ?')
    }
    let timerId: any
    function invokeFunc(...args: any[]) {
      let context = this;
      if(timerId) clearTimeout(timerId)
      timerId = setTimeout(()=>{
        func.apply(context, args)
      }, time)
    }
    function cancel() {
      if (timerId !== undefined) {
        clearTimeout(timerId)
      }
    }
    invokeFunc.cancel = cancel
    descriptor.value = invokeFunc
  }
}




/**
* 函数节流， 用作防连点
* @param {Function} fn 
* @param {Number} delay 
* @returns 
*/
export function throttle(fn: Function, delay: number=2000) {
  let flag: boolean = true,
    timerId: any
  return function (...args: any[]) {
    if (!flag) return
    flag = false
    timerId && clearTimeout(timerId)
    timerId = setTimeout(function() {
      flag = true
    }, delay)
    const context = this
    return fn.apply(context, args)
  };
}


/**
 * 函数节流的 装饰器版本
 * @param time 
 * @returns 
 */
export function throttleDecorator(time: number = 1000) {
  return function(target: any, property: string, descriptor: PropertyDescriptor) {
    const func: Function = descriptor.value
    if (typeof func !== 'function') {
      throw new Error('debounceDecorator error: are u kidding me ?')
    }
    let flag: boolean = true,
    timerId: any
    function invokeFunc(...args: any[]) {
      if (!flag) return
      flag = false
      timerId && clearTimeout(timerId)
      timerId = setTimeout(function() {
        flag = true
      }, time)
      const context = this
      return func.apply(context, args)
    }
    descriptor.value = invokeFunc
  }
}

export const PromiseAwaitCacheMap = new Map<Function, {
  waitPromise: Promise<any>,
  waitPromiseResolve: (value: any) => void
}>()

/**
 * 只有装饰方法 执行完了之后 才可以第二次执行
 * @param target 
 * @param property 
 * @param descriptor 
 */
 export function PromiseAwait(target: any, property: string, descriptor: PropertyDescriptor) {
  const func: Function = descriptor.value
  descriptor.value = async function(...args:any[]) {
    const cache = PromiseAwaitCacheMap.get(func)
    if (cache) return cache.waitPromise
    // @ts-ignore
    let shouldCache: {
      waitPromise: Promise<any>,
      waitPromiseResolve: (value: any) => void
    } = {}
    shouldCache.waitPromise = new Promise(resolve => {
      shouldCache.waitPromiseResolve = resolve
    })
    PromiseAwaitCacheMap.set(func, shouldCache)
    // @ts-ignore
    const res = await func.apply(this, args)
    shouldCache.waitPromiseResolve(res)
    PromiseAwaitCacheMap.delete(func)
    return res
  }
}

/**
 * PromiseAwait 闭包版本
 * @param func 
 * @returns 
 */
export function promiseAwaitFunc(func: Function) {
  let waitPromise: Promise<any> | null = null
  let waitPromiseResolve: (value: unknown) => void
  return async function(...args: any[]) {
    if (waitPromise) return waitPromise
    waitPromise = new Promise(resolve => {
      waitPromiseResolve = resolve
    })
    // @ts-ignore
    const res = await func.apply(this, args)
    waitPromiseResolve(res)
    waitPromise = null
    return res
  }
}