import { PromiseExt } from './ui_utils';

export default class MessageHandler {

  constructor(sourceName, targetName, comObj) {
    this.sourceName = sourceName;
    this.targetName = targetName;
    this.comObj = comObj;
    this.callbackId = 1
    this.callbackCapabilities = Object.create(null);
    this.actionHandler = Object.create(null);

    this._onComObjOnMessage = (event) => {
      const data = event.data
      if (data.targetName !== this.sourceName) {
        return;
      }
      if (data.callback) {
        const callbackId = data.callbackId;
        const capability = this.callbackCapabilities[callbackId]
        if (!capability) {
          throw new Error(`Cannot resolve callback ${callbackId}`)
        }
        delete this.callbackCapabilities[callbackId]

        if (data.callback === 'resolve') {
          capability.resolve(data.data);
        } else {
          throw new Error("Unexpected callback case");
        }
        return;
      }

      const action = this.actionHandler[data.action];
      if (!action) {
        throw new Error(`Unknown action from worker: ${data.action}`);
      }
      if (data.callbackId) {
        const cbSourceName = this.sourceName;
        const cbTargetName = data.sourceName;

        new Promise(function (resolve) {
          resolve(action(data.data));
        }).then(
          function (result) {
            comObj.postMessage({
              sourceName: cbSourceName,
              targetName: cbTargetName,
              callback: 'resolve',
              callbackId: data.callbackId,
              data: result,
            });
          },
          function (reason) {
            comObj.postMessage({
              data,
              sourceName: cbSourceName,
              targetName: cbTargetName,
              callback: 'reject',
              callbackId: data.callbackId,
              reason: reason,
            });
          }
        )
        return
      }
      action(data.data);
    };
    comObj.addEventListener("message", this._onComObjOnMessage);
  }

  on(actionName, handler) {
    const ah = this.actionHandler;
    if (ah[actionName]) {
      throw new Error(`There is already an actionName called "${actionName}"`);
    }
    ah[actionName] = handler;
  }

  send(actionName, data) {
    this.comObj.postMessage(
      {
        sourceName: this.sourceName,
        targetName: this.targetName,
        action: actionName,
        data,
      }
    );
  }

  sendWithPromise(actionName, data) {
    const callbackId = this.callbackId++;

    let promiseExt = new PromiseExt()
    this.callbackCapabilities[callbackId] = promiseExt

    try {
      this.comObj.postMessage(
        {
          sourceName: this.sourceName,
          targetName: this.targetName,
          action: actionName,
          callbackId,
          data,
        }
      );
    } catch (ex) {
      promiseExt.reject(ex);
    }
    return promiseExt.promise;
  }

  destroy() {
    this.comObj.removeEventListener("message", this._onComObjOnMessage);
  }
}
