(function (root) {
  "use strict";

  function xor(a, b) {
      console.log("xor");
      console.log(a);
      console.log(b);
    var res = "", i = a.length, j = b.length;
    while (i-->0 && j-->0)
        res = String.fromCharCode(a[i] ^ b[j]) + res;
    return res;
  }

  function requestCredentials(module, pw) {
    try {
      var request = module.createCredentialRequest({ pwdU: pw });
      var pub_base16 = module.uint8ArrayToHex(request.pub);
      var xhr = new XMLHttpRequest();
      xhr.open("POST", "/request-creds", true);
      xhr.onreadystatechange = function () {
        var response = onreadystatechange(module, xhr);
        if (response) recoverCredentials(module, response, request);
      };
      xhrSend(
        "request=" + pub_base16,
        module,
        xhr
      );
    } catch (e) {
      module.printErr(e);
    }
  }

  function recoverCredentials(module, response, request) {
    const ids = { idS: "demo server", idU: "demo user" }
    const context = "rbopaque-v0.2.0-demo";
    try {
      var resp_base16 = response.response;
      var credentials = module.recoverCredentials({
        resp: module.hexToUint8Array(resp_base16),
        sec: request.sec,
        context: context,
        ids: ids,
      });
      //var export_key_base16 = module.uint8ArrayToHex(credentials.export_key);
      //module.print("export_key "+ export_key_base16);
      const blob = module.hexToUint8Array(response.blob);
     module.print("Decoded blob: " + xor(credentials.export_key, blob));

    } catch (e) {
      module.printErr(e);
    }
  }

  function onreadystatechange(module, xhr) {
    try {
      if (xhr.readyState !== XMLHttpRequest.DONE) return;
      if (xhr.status !== 200 || !xhr.response) {
        module.printErr(xhr.responseURL + " failed.");
        return;
      }
      module.print(xhr.responseURL);
      module.print("FROM SERVER: " + xhr.response);
      var json = JSON.parse(xhr.response);
      if (!json) {
        module.printErr(xhr.responseURL + " failed.");
        return;
      }
      if (json.error) {
        module.printErr(json.error);
        return;
      }
      return json;
    } catch (e) {
      module.printErr(e);
    }
  }

  function xhrSend(body, module, xhr) {
    module.print("TO SERVER: " + body);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send(body);
  }

  // Module is boilerplate generated by Emscripten. slightly modified.
  var Module = {
    preRun: [],
    postRun: [],
    print: function (text) {
      if (arguments.length > 1)
        text = Array.prototype.slice.call(arguments).join(" ");
      if (typeof text === "string" || text instanceof String)
        postMessage({ print: text });
      else if (text instanceof Error) postMessage({ print: text.message });
      else postMessage({ print: JSON.stringify(text) });
    },
    printErr: function (text) {
      if (arguments.length > 1)
        text = Array.prototype.slice.call(arguments).join(" ");
      if (typeof text === "string" || text instanceof String)
        postMessage({ printErr: text });
      else if (text instanceof Error) postMessage({ printErr: text.message });
      else postMessage({ printErr: JSON.stringify(text) });
    },
    setStatus: function (text) {
      if (!Module.setStatus.last)
        Module.setStatus.last = { time: Date.now(), text: "" };
      if (text === Module.setStatus.last.text) return;
      var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
      var now = Date.now();
      if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
      Module.setStatus.last.time = now;
      Module.setStatus.last.text = text;
      // Send a message to index.js.
      // https://developer.mozilla.org/en-US/docs/Web/API/Worker/postmessage
      postMessage({ print: text });
    },
    totalDependencies: 0,
    monitorRunDependencies: function (left) {
      this.totalDependencies = Math.max(this.totalDependencies, left);
      Module.setStatus(
        left
          ? "Preparing... (" +
              (this.totalDependencies - left) +
              "/" +
              this.totalDependencies +
              ")"
          : "All downloads complete."
      );
    },
  };
  Module.setStatus("Downloading...");
  root.onerror = function (event) {
    // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
    Module.setStatus("Exception thrown, see JavaScript console");
    Module.setStatus = function (text) {
      if (text) Module.printErr("[post-exception status] " + text);
    };
  };

  // See the end of libopaque-post.js for where we hook up root.libopaque_mod.
  root.libopaque_mod = Module;

  // https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
  root.importScripts("libopaque.debug.js");

  // Receive a message from index.js.
  // https://developer.mozilla.org/en-US/docs/Web/API/Worker/onmessage
  root.onmessage = function (e) {
    var action = e.data.action;
    var pwdU = e.data.pw;
    if (action === "fetch") {
      requestCredentials(Module, pwdU);
    } else {
      Module.printErr(action + " is invalid.");
    }
  };
})(this);
