// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Common from '../../core/common/common.js';
import * as WasmParser from '../../third_party/wasmparser/wasmparser.js';

export function dissambleWASM(
    params: {
      content: string,
    },
    postMessage: (arg0: unknown) => void): void {
  try {
    const dataBuffer = Common.Base64.decode(params.content);

    let parser: WasmParser.WasmParser.BinaryReader = new WasmParser.WasmParser.BinaryReader();
    parser.setData(dataBuffer.buffer, 0, dataBuffer.byteLength);
    const nameGenerator = new WasmParser.WasmDis.DevToolsNameGenerator();
    nameGenerator.read(parser);

    const data = new Uint8Array(dataBuffer);
    parser = new WasmParser.WasmParser.BinaryReader();
    const dis = new WasmParser.WasmDis.WasmDisassembler();
    dis.addOffsets = true;
    dis.exportMetadata = nameGenerator.getExportMetadata();
    dis.nameResolver = nameGenerator.getNameResolver();
    const lines = [];
    const offsets = [];
    const functionBodyOffsets = [];
    let chunkSize: number = 128 * 1024;
    let buffer = new Uint8Array(chunkSize);
    let pendingSize = 0;
    let offsetInModule = 0;
    for (let i = 0; i < data.length;) {
      if (chunkSize > data.length - i) {
        chunkSize = data.length - i;
      }
      const bufferSize = pendingSize + chunkSize;
      if (buffer.byteLength < bufferSize) {
        const newBuffer = new Uint8Array(bufferSize);
        newBuffer.set(buffer);
        buffer = newBuffer;
      }
      while (pendingSize < bufferSize) {
        buffer[pendingSize++] = data[i++];
      }
      parser.setData(buffer.buffer, 0, bufferSize, i === data.length);

      // The disassemble will attemp to fetch the data as much as possible.
      const finished = dis.disassembleChunk(parser, offsetInModule);

      const result = (dis.getResult() as {
        lines: string[],
        offsets: number[],
        functionBodyOffsets: Array<{
          start: number,
          end: number,
        }>,
      });

      for (const line of result.lines) {
        lines.push(line);
      }
      for (const offset of result.offsets) {
        offsets.push(offset);
      }
      for (const functionBodyOffset of result.functionBodyOffsets) {
        functionBodyOffsets.push(functionBodyOffset);
      }

      if (finished) {
        break;
      }

      if (parser.position === 0) {
        // Parser did not consume anything, needs more data.
        pendingSize = bufferSize;
        continue;
      }

      // Shift the data to the beginning of the buffer.
      const pending = parser.data.subarray(parser.position, parser.length);
      pendingSize = pending.length;
      buffer.set(pending);
      offsetInModule += parser.position;

      const percentage = Math.floor((offsetInModule / data.length) * 100);
      postMessage({event: 'progress', params: {percentage}});
    }

    postMessage({event: 'progress', params: {percentage: 100}});

    postMessage({method: 'disassemble', result: {lines, offsets, functionBodyOffsets}});
  } catch (error) {
    postMessage({method: 'disassemble', error});
  }
}
