/**
 * Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

/* import-globals-from helpers.js */

var bufferCache = [];
var utils = SpecialPowers.getDOMWindowUtils(window);

function getBuffer(size) {
  let buffer = new ArrayBuffer(size);
  is(buffer.byteLength, size, "Correct byte length");
  return buffer;
}

function getRandomBuffer(size) {
  let buffer = getBuffer(size);
  let view = new Uint8Array(buffer);
  for (let i = 0; i < size; i++) {
    view[i] = parseInt(Math.random() * 255);
  }
  return buffer;
}

function getView(size) {
  let buffer = new ArrayBuffer(size);
  let view = new Uint8Array(buffer);
  is(buffer.byteLength, size, "Correct byte length");
  return view;
}

function getRandomView(size) {
  let view = getView(size);
  for (let i = 0; i < size; i++) {
    view[i] = parseInt(Math.random() * 255);
  }
  return view;
}

function compareBuffers(buffer1, buffer2) {
  if (buffer1.byteLength != buffer2.byteLength) {
    return false;
  }
  let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1);
  let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2);
  for (let i = 0; i < buffer1.byteLength; i++) {
    if (view1[i] != view2[i]) {
      return false;
    }
  }
  return true;
}

function getBlob(type, view) {
  return new Blob([view], { type });
}

function getFile(name, type, view) {
  return new File([view], name, { type });
}

function getRandomBlob(size) {
  return getBlob("binary/random", getRandomView(size));
}

function getRandomFile(name, size) {
  return getFile(name, "binary/random", getRandomView(size));
}

function getNullBlob(size) {
  return getBlob("binary/null", getView(size));
}

function getNullFile(name, size) {
  return getFile(name, "binary/null", getView(size));
}

function getWasmModule(binary) {
  let module = new WebAssembly.Module(binary);
  return module;
}

function verifyBuffers(buffer1, buffer2) {
  ok(compareBuffers(buffer1, buffer2), "Correct buffer data");
}

function verifyBlob(blob1, blob2, fileId, blobReadHandler) {
  // eslint-disable-next-line mozilla/use-cc-etc
  is(SpecialPowers.wrap(Blob).isInstance(blob1), true, "Instance of Blob");
  is(blob1 instanceof File, blob2 instanceof File, "Instance of DOM File");
  is(blob1.size, blob2.size, "Correct size");
  is(blob1.type, blob2.type, "Correct type");
  if (blob2 instanceof File) {
    is(blob1.name, blob2.name, "Correct name");
  }
  is(utils.getFileId(blob1), fileId, "Correct file id");

  let buffer1;
  let buffer2;

  for (let i = 0; i < bufferCache.length; i++) {
    if (bufferCache[i].blob == blob2) {
      buffer2 = bufferCache[i].buffer;
      break;
    }
  }

  if (!buffer2) {
    let reader = new FileReader();
    reader.readAsArrayBuffer(blob2);
    reader.onload = function (event) {
      buffer2 = event.target.result;
      bufferCache.push({ blob: blob2, buffer: buffer2 });
      if (buffer1) {
        verifyBuffers(buffer1, buffer2);
        if (blobReadHandler) {
          blobReadHandler();
        } else {
          testGenerator.next();
        }
      }
    };
  }

  let reader = new FileReader();
  reader.readAsArrayBuffer(blob1);
  reader.onload = function (event) {
    buffer1 = event.target.result;
    if (buffer2) {
      verifyBuffers(buffer1, buffer2);
      if (blobReadHandler) {
        blobReadHandler();
      } else {
        testGenerator.next();
      }
    }
  };
}

function verifyBlobArray(blobs1, blobs2, expectedFileIds) {
  is(blobs1 instanceof Array, true, "Got an array object");
  is(blobs1.length, blobs2.length, "Correct length");

  if (!blobs1.length) {
    return;
  }

  let verifiedCount = 0;

  function blobReadHandler() {
    if (++verifiedCount == blobs1.length) {
      testGenerator.next();
    } else {
      verifyBlob(
        blobs1[verifiedCount],
        blobs2[verifiedCount],
        expectedFileIds[verifiedCount],
        blobReadHandler
      );
    }
  }

  verifyBlob(
    blobs1[verifiedCount],
    blobs2[verifiedCount],
    expectedFileIds[verifiedCount],
    blobReadHandler
  );
}

function verifyView(view1, view2) {
  is(view1.byteLength, view2.byteLength, "Correct byteLength");
  verifyBuffers(view1, view2);
  continueToNextStep();
}

function verifyWasmModule(module1, module2) {
  // We assume the given modules have no imports and export a single function
  // named 'run'.
  var instance1 = new WebAssembly.Instance(module1);
  var instance2 = new WebAssembly.Instance(module2);
  is(instance1.exports.run(), instance2.exports.run(), "same run() result");

  continueToNextStep();
}

function grabFileUsageAndContinueHandler(request) {
  testGenerator.next(request.result.fileUsage);
}

function getCurrentUsage(usageHandler) {
  let qms = SpecialPowers.Services.qms;
  let principal = SpecialPowers.wrap(document).nodePrincipal;
  let cb = SpecialPowers.wrapCallback(usageHandler);
  qms.getUsageForPrincipal(principal, cb);
}

function getFileId(file) {
  return utils.getFileId(file);
}

function getFilePath(file) {
  return utils.getFilePath(file);
}

function* assertEventuallyHasFileInfo(name, id) {
  yield* assertEventuallyWithGC(
    () => utils.getFileReferences(name, id),
    `Expect existing DatabaseFileInfo for ${name}/${id}`
  );
}

function* assertEventuallyHasNoFileInfo(name, id) {
  yield* assertEventuallyWithGC(
    () => !utils.getFileReferences(name, id),
    `Expect no existing DatabaseFileInfo for ${name}/${id}`
  );
}

function* assertEventuallyFileRefCount(name, id, expectedCount) {
  yield* assertEventuallyWithGC(() => {
    let count = {};
    utils.getFileReferences(name, id, count);
    return count.value == expectedCount;
  }, `Expect ${expectedCount} existing references for ${name}/${id}`);
}

function getFileDBRefCount(name, id) {
  let count = {};
  utils.getFileReferences(name, id, {}, count);
  return count.value;
}

function flushPendingFileDeletions() {
  utils.flushPendingFileDeletions();
}
