import messager from '../common/messager';
import nativeEventer from '../common/nativeEventer';
import strRender from '../../../utils/strRender';

const WS_OPEN = 'ws.open';
const WS_CLOSE = 'ws.close';
const WS_ERROR = 'ws.error';
const WS_MESSAGE = 'ws.message';

/**
 * 初始化监听 wsMessage 消息
 */
const wsEventer = new nativeEventer({
  topic: [WS_OPEN, WS_CLOSE, WS_ERROR, WS_MESSAGE],
  resReducer: (data, topic) => {
    return {
      event: topic, content: data
    }
  }
});

// topicListener.on('ws.open', );

export default class Socket {
  constructor(opt) {
    this._reset();
    this._initEvents(opt);
  }

  _reset() {
    this.onopen = () => { };
    this.onclose = () => { };
    this.onerror = () => { };
    this.onmessage = () => { };
  }

  _initEvents(opt) {
    // 建立连接，获取id
    messager.dispatch('wsConnect', opt).then(({ wsId, binaryType, protocol }) => {
      this._wsId = wsId;
      this.binaryType = binaryType;
      this.protocol = protocol;
    }).catch(err => this.onerror(err));

    wsEventer.on(WS_OPEN, ({ wsId }) => {
      if (wsId === this._wsId) this.onopen();
    });

    wsEventer.on(WS_CLOSE, ({ wsId, code, reason, wasClean }) => {
      if (wsId === this._wsId) this.onclose({ code, reason, wasClean });
    });

    wsEventer.on(WS_ERROR, ({ wsId, message }) => {
      if (wsId === this._wsId) this.onerror(new Error(message));
    });

    wsEventer.on(WS_MESSAGE, ev => {
      if (ev && ev.wsId === this._wsId) this.onmessage(ev);
    });
  }

  /**
   * 发送数据
   * @param {any} data 
   */
  send(data) {
    const msg = strRender.getWSMessage(data);
    if (!msg) {
      this.onerror(new Error('send invalid data'));
    }

    const param = {
      wsId: this._wsId,
      message: msg,
    };
    messager.dispatch('wsSend', param).catch(err => this.onerror(err));
  }

  /**
   * 断开链接
   * @param {number} code 
   * @param {string} reason 
   */
  close(code, reason) {
    messager.dispatch('wsClose', { wsId: this._wsId, code, reason }).catch(err => this.onerror(err));
  }

  /**
   * 销毁
   */
  destroy() {
    this._reset();
  }
}