const { AppConstants } = ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);

// Stamp an array buffer with a pattern; verified by verify_chunk below.
function init(array) {
  for (var i = 0; i < array.length; i++) {
    array[i] = i % 256;
  }
}

// The construction of the file below ends up with 12 instances
// of the array buffer -- we want this to be larger than 2**32 bytes
// to exercise potential truncation of nsIInputStream::Read's count
// parameter.
const ABLENGTH = (2 ** 32 + 8) / 12;

// Get a large file (bigger than 2GB!)
function get_file() {
  const array = new ArrayBuffer(ABLENGTH);
  const buff = new Uint8Array(array);

  // Stamp with pattern.
  init(buff);

  const blob = new Blob([buff, buff], {});
  return new File([blob, blob, blob, blob, blob, blob], {});
}

// Verify that the chunks the stream recieve correspond to the initialization
function verify_chunk(chunk, verification_state) {
  for (var j = 0; j < chunk.length; j++) {
    // If we don't match the fill pattern.
    if (chunk[j] != verification_state.expected) {
      // we ran out of array buffer; so we should be looking at the first byte of the array again.
      if (
        verification_state.total_index % ABLENGTH != 0 ||
        chunk[j] != 0 /* ASSUME THAT THE INITIALIZATION OF THE BUFFER IS ZERO */
      ) {
        throw new Error(
          `Mismatch: chunk[${j}] (${chunk[j]}) != ${verification_state.expected} (total_index ${verification_state.total_index})`
        );
      }
      // Reset the fill expectation to 1 for next round.
      verification_state.expected = 1;
    } else {
      // We are inside regular fill section
      verification_state.expected = (verification_state.expected + 1) % 256;
    }
    verification_state.total_index++;
  }
}

// Pipe To Testing: Win32 can't handle the file size created in this test and OOMs.
add_task(
  {
    skip_if: () => AppConstants.platform == "win" && !Services.appinfo.is64Bit,
  },
  async () => {
    var chunk_verification_state = {
      expected: 0,
      total_index: 0,
    };

    const file = get_file();

    await file.stream().pipeTo(
      new WritableStream({
        write(chunk) {
          verify_chunk(chunk, chunk_verification_state);
        },
      })
    );
  }
);

// Do the same test as above, but this time don't use pipeTo.
add_task(
  {
    skip_if: () => AppConstants.platform == "win" && !Services.appinfo.is64Bit,
  },
  async () => {
    var file = get_file();

    var chunk_verification_state = {
      expected: 0,
      total_index: 0,
    };

    var streamReader = file.stream().getReader();

    while (true) {
      var res = await streamReader.read();
      if (res.done) {
        break;
      }
      var chunk = res.value;
      verify_chunk(chunk, chunk_verification_state);
    }
  }
);
