/**
 * 事件管理
 * 基础事件管理基础上加事件类型做区分
 * 每个事件类型为一个事件管理实例
 * 解决同一事件名不同事件类型的监听和移除
 */
class Events {
  constructor(tp) {
    this.tp = tp;
      
    /** {[index: sting]: { listener: any, once: boolean } } */
    this.events = {};
  }

  on(eventName, listener, once) {
    if (!listener || typeof listener !== 'function') {
      return false;
    }
    // 事件注册
    this.events[eventName] = this.events[eventName] || [];
    this.events[eventName].push({ listener, once });
    return true;
  }

  off(eventName, listener) {
    // 如果只指定 listener，遍历所有事件进行移除
    if (!eventName && listener) {
      Object.keys(this.events).forEach(name => this.off(name, listener)) 
      return true
    }

    // 无注册监听
    const listeners = this.events[eventName];
    if (!listeners || !listeners.length) {
      return true;
    }

    // 不传listener 清空所有监听
    if (!listener) {
      try {
        delete this.events[eventName]
      } catch (_) {
        this.events[eventName] = [];
      }
      return true;
    }

    // 监听过滤
    this.events[eventName] = listeners.filter(e => e.listener !== listener);
    return true;
  }

  emit(eventName, data) {
    const listeners = this.events[eventName] || [];
    listeners.forEach(wrap => {
      try {
        wrap.listener(data);
      } catch (e) {
        console.warn(e)
      }

      // 仅触发一次
      if (wrap.once) {
        this.off(eventName, wrap.listener);
      }
    });
  }

  /**
   * 事件是否清空
   * @param {string} [eventName] 事件名，不传返回当前实例的事件是否清空
   * @returns 
   */
  isClean(eventName) {
    if (!eventName) {
      return Object.keys(this.events).length === 0
    }

    const listeners = this.events[eventName];
    return !listeners || !listeners.length;
  }
}

/**
 * 事件管理器
 */
export default class EventManager {
  constructor() {
    this._main = "_main" // 默认事件类型
    this.eventers = {}; // 按类型储存事件实例
  }

  /**
   * 注册后事件
   * 不指定 tp 时，统一注册到 _main 下
   * @param {string} eventName 监听事件名
   * @param {function} listener 事件
   * @param {string} [tp] 事件类型，可选
   * @param {boolean} [once] 是否只触发一次
   * @returns {boolean} 调用结果
   */
  _on(eventName, listener, tp, once) {
    const type = tp || this._main

    // 提取对应类型的事件管理
    this.eventers[type] = this.eventers[type] || new Events(type);

    // 添加事件
    return this.eventers[type].on(eventName, listener, once)
  }

  /**
   * 注册后只触发一次的事件
   * @param {string} eventName 监听事件名
   * @param {function} listener 事件
   * @param {string} tp 事件类型，可选
   * @returns {boolean} 调用结果
   */
  once(eventName, listener, tp) {
    return this._on(eventName, listener, tp, true);
  }

  /**
   * 注册事件
   * @param {string} eventName 监听事件名
   * @param {function} listener 事件
   * @param {string} tp 事件类型
   * @returns {boolean} 调用结果
   */
  on(eventName, listener, tp) {
    return this._on(eventName, listener, tp, false);
  }

  /**
   * 注销事件
   * 不指定 tp 时，移除所有 tp 下注册的 eventName
   * @param {string} eventName 事件名
   * @param {function} listener 事件
   * @param {string} [tp] 事件类型
   * @returns {boolean} 调用结果
   */
  off(eventName, listener, tp) {
    // 没 eventName 有 tp，则移除指定类型的所有事件监听
    if (!eventName && tp) {
      // 指定 listener 进行tp事件移除
      if (listener) {
        this.eventers[tp].off(eventName, listener)
        return true
      }

      // 不指定 listener 则移除 tp 
      try {
        delete this.eventers[tp]
      } catch (_) {
        this.eventers[tp] = undefined;
      }
      return true;
    }

    // 没 eventName 没 tp 不处理
    if (!eventName) return true

    // 有 eventName 有 tp 只针对某个类型进行监听移除
    if (tp) {
      if (!this.eventers[tp]) return true;
      return this.eventers[tp].off(eventName, listener)
    }

    // 有 eventName 没 tp 遍历所有类型，移除监听事件
    Object.keys(this.eventers).forEach(type => this.off(eventName, listener, type))
    return true
  }

  /**
   * 事件触发
   * 不指定 tp 时，触发所有 tp 的 eventName
   * @param {string} eventName 触发事件名
   * @param {object} data 传参
   * @param {string} [tp] 事件类型
   */
  emit(eventName, data, tp) {
    // 只触发指定类型的事件
    if (tp) {
      this.eventers[tp] && this.eventers[tp].emit(eventName, data)
      return
    }

    // 没指定类型则触发所有管理器里面同名事件
    Object.keys(this.eventers).forEach(type => this.emit(eventName, data, type))
  }

  /**
   * 是否存在注册事件
   * 不指定 tp 时，返回所有 tp 的 eventName 是否存在监听
   * @param {string} eventName 事件名
   * @param {string} [tp] 事件类型，特殊情况下使用
   */
  isClean(eventName, tp) {
    // 指定tp，则只判断此类型是否clean
    if (tp) {
      return !this.eventers[tp] || this.eventers[tp].isClean(eventName)
    }

    // 判断所有类型是否clean
    return Object.keys(this.eventers).every(type => this.isClean(eventName, type))
  }

  /**
   * 获取某个类型下的所有监听事件名
   * @param {string} tp 事件类型
   * @returns {string[]}
   */
  getNamesByTp(tp) {
    if (!this.eventers[tp]) return []
    return Object.keys(this.eventers[tp].events || {})
  }
}
