// The file including this must also include /common/get-host-info.sub.js to
// pick up the necessary constants.

const HOST = get_host_info().ORIGINAL_HOST;
const PORT = '{{ports[webtransport-h3][0]}}';
const BASE = `https://${HOST}:${PORT}`;

// Wait for the given number of milliseconds (ms).
function wait(ms) { return new Promise(res => step_timeout(res, ms)); }

// Create URL for WebTransport session.
function webtransport_url(handler) {
  return `${BASE}/webtransport/handlers/${handler}`;
}

// Converts WebTransport stream error code to HTTP/3 error code.
// https://ietf-wg-webtrans.github.io/draft-ietf-webtrans-http3/draft-ietf-webtrans-http3.html#section-4.3
function webtransport_code_to_http_code(n) {
  const first = 0x52e4a40fa8db;
  return first + n + Math.floor(n / 0x1e);
}

// Read all chunks from |readable_stream| and return as an array of arrays
async function read_stream(readable_stream) {
  const reader = readable_stream.getReader();

  let chunks = [];
  while (true) {
    const {value: chunk, done} = await reader.read();
    if (done) {
      break;
    }
    chunks.push(chunk);
  }
  reader.releaseLock();

  return chunks;
}

// Read all chunks from |readable_stream|, decode chunks to a utf-8 string, then
// return the string.
async function read_stream_as_string(readable_stream) {
  const decoder = new TextDecoderStream();
  const decode_stream = readable_stream.pipeThrough(decoder);
  const reader = decode_stream.getReader();

  let chunks = '';
  while (true) {
    const {value: chunk, done} = await reader.read();
    if (done) {
      break;
    }
    chunks += chunk;
  }
  reader.releaseLock();

  return chunks;
}

// Decode all chunks in a given ReadableStream, and parse the data using JSON.
async function read_stream_as_json(readable_stream) {
  const text = await read_stream_as_string(readable_stream);
  return JSON.parse(text);
}

// Check the standard request headers and delete them, leaving any "unique"
// headers to check in the test.
function check_and_remove_standard_headers(headers) {
  assert_equals(headers[':scheme'], 'https');
  delete headers[':scheme'];
  assert_equals(headers[':method'], 'CONNECT');
  delete headers[':method'];
  assert_equals(headers[':authority'], `${HOST}:${PORT}`);
  delete headers[':authority'];
  assert_equals(headers[':path'], '/webtransport/handlers/echo-request-headers.py');
  delete headers[':path'];
  assert_equals(headers[':protocol'], 'webtransport');
  delete headers[':protocol'];
  assert_equals(headers['origin'], `${get_host_info().ORIGIN}`);
  delete headers['origin'];
}

async function query(token) {
  const wt = new WebTransport(webtransport_url(`query.py?token=${token}`));
  try {
    await wt.ready;
    const streams = await wt.incomingUnidirectionalStreams;
    const streams_reader = streams.getReader();
    const { value: readable } = await streams_reader.read();
    streams_reader.releaseLock();

    return await read_stream_as_json(readable);
  } finally {
    wt.close();
  }
}

async function readInto(reader, buffer) {
  let offset = 0;

  while (offset < buffer.byteLength) {
    const {value: view, done} = await reader.read(
        new Uint8Array(buffer, offset, buffer.byteLength - offset));
    buffer = view.buffer;
    if (done) {
      break;
    }
    offset += view.byteLength;
  }

  return buffer;
}
