export class EventResult {
  get isDefaultPrevented() {
    return this._isDefaultPrevented;
  }

  get isStopped() {
    return this._isStopped;
  }

  private _isDefaultPrevented = false;

  private _isStopped = false;

  preventDefault() {
    this._isDefaultPrevented = true;
  }

  stop() {
    this._isStopped = true;
  }
}

function nextTick(callback: VoidFunction) {
  if (typeof window === "object" && window.requestAnimationFrame)
    window.requestAnimationFrame(callback);
  else if (typeof setTimeout === "function") setTimeout(callback, 1);
  // 若不支持setTimeout降级为同步方法（例如j2v8环境）
  else callback();
}

export class Eventer<EventMap extends Record<string, any> = Record<string, any>> {
  private listeners: Record<any, Array<(...args: any) => any>> = {};

  /* 绑定事件监听器 */
  on<K extends keyof EventMap>(
    type: K,
    handler: (ev: EventMap[K], eventResult: EventResult) => void
  ) {
    const list = this.listeners[type] || [];
    list.push(handler);
    this.listeners[type] = list;
    return () => this.off(type, handler);
  }

  /**
   * 把当前已经注册的监听器重新输送出去
   * @param callback 输出的回调
   * @param clearCurrent 输出后是否清除当前实例上的监听器
   */
  pipe<M extends EventMap = EventMap>(
    callback: <K extends keyof M>(
      type: K,
      handler: (ev: M[K], eventResult: EventResult) => void
    ) => void,
    clearCurrent = true
  ) {
    const { listeners } = this;
    Object.keys(listeners).forEach(key => {
      const list = listeners[key] || [];
      list.forEach(h => callback(key, h));
    });
    if (clearCurrent) this.listeners = {};
  }

  /* 移除事件监听器 */
  off<K extends keyof EventMap>(
    type: K,
    handler?: (ev: EventMap[K], eventResult: EventResult) => void
  ) {
    const list = this.listeners[type] || [];
    this.listeners[type] = handler ? list.filter(f => f !== handler) : [];
  }

  /* 移除所有监听器 */
  offAll() {
    Object.keys(this.listeners).forEach(k => {
      this.listeners[k] = [];
    });
  }

  /* 触发事件（同步执行） */
  emit<K extends keyof EventMap>(type: K, event: EventMap[K]): EventResult {
    const eventResult = new EventResult();
    const list = this.listeners[type] || [];
    for (const handler of list) {
      handler(event, eventResult);
      if (eventResult.isStopped) {
        break;
      }
    }
    return eventResult;
  }

  /* 触发事件（下一帧执行） */
  emitAsync<K extends keyof EventMap>(type: K, event: EventMap[K]) {
    nextTick(() => this.emit(type, event));
  }
}
