"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.computeOperations = computeOperations;
exports.OperationKind = void 0;
var OperationKind;
exports.OperationKind = OperationKind;

(function (OperationKind) {
  OperationKind[OperationKind["COPY"] = 0] = "COPY";
  OperationKind[OperationKind["DOWNLOAD"] = 1] = "DOWNLOAD";
})(OperationKind || (exports.OperationKind = OperationKind = {}));

function computeOperations(oldBlockMap, newBlockMap, logger) {
  const nameToOldBlocks = buildBlockFileMap(oldBlockMap.files);
  const nameToNewBlocks = buildBlockFileMap(newBlockMap.files);
  const oldEntryMap = buildEntryMap(oldBlockMap.files);
  let lastOperation = null;
  const operations = [];

  for (const blockMapFile of newBlockMap.files) {
    const name = blockMapFile.name;
    const oldEntry = oldEntryMap.get(name);

    if (oldEntry == null) {
      // new file
      operations.push({
        kind: OperationKind.DOWNLOAD,
        start: blockMapFile.offset,
        end: blockMapFile.offset + blockMapFile.sizes.reduce((accumulator, currentValue) => accumulator + currentValue)
      });
      continue;
    }

    const newFile = nameToNewBlocks.get(name);
    let changedBlockCount = 0;
    const {
      checksumToOffset: checksumToOldOffset,
      checksumToOldSize
    } = buildChecksumMap(nameToOldBlocks.get(name), oldEntry.offset);
    let newOffset = blockMapFile.offset;

    for (let i = 0; i < newFile.checksums.length; newOffset += newFile.sizes[i], i++) {
      const blockSize = newFile.sizes[i];
      const checksum = newFile.checksums[i];
      let oldOffset = checksumToOldOffset.get(checksum);

      if (oldOffset != null && checksumToOldSize.get(checksum) !== blockSize) {
        logger.warn(`Checksum ("${checksum}") matches, but size differs (old: ${checksumToOldSize.get(checksum)}, new: ${blockSize})`);
        oldOffset = null;
      }

      if (oldOffset == null) {
        changedBlockCount++;

        if (lastOperation == null || lastOperation.kind !== OperationKind.DOWNLOAD || lastOperation.end !== newOffset) {
          lastOperation = {
            kind: OperationKind.DOWNLOAD,
            start: newOffset,
            end: newOffset + blockSize
          };
          operations.push(lastOperation);
        } else {
          lastOperation.end += blockSize;
        }
      } else if (lastOperation == null || lastOperation.kind !== OperationKind.COPY || lastOperation.end !== oldOffset) {
        lastOperation = {
          kind: OperationKind.COPY,
          start: oldOffset,
          end: oldOffset + blockSize
        };
        operations.push(lastOperation);
      } else {
        lastOperation.end += blockSize;
      }
    }

    if (changedBlockCount > 0) {
      logger.info(`File${blockMapFile.name === "file" ? "" : " " + blockMapFile.name} has ${changedBlockCount} changed blocks`);
    }
  }

  return operations;
}

function buildChecksumMap(file, fileOffset) {
  const checksumToOffset = new Map();
  const checksumToSize = new Map();
  let offset = fileOffset;

  for (let i = 0; i < file.checksums.length; i++) {
    const checksum = file.checksums[i];
    const size = file.sizes[i];
    checksumToOffset.set(checksum, offset);
    checksumToSize.set(checksum, size);
    offset += size;
  }

  return {
    checksumToOffset,
    checksumToOldSize: checksumToSize
  };
}

function buildEntryMap(list) {
  const result = new Map();

  for (const item of list) {
    result.set(item.name, item);
  }

  return result;
}

function buildBlockFileMap(list) {
  const result = new Map();

  for (const item of list) {
    result.set(item.name, item);
  }

  return result;
} 
//# sourceMappingURL=downloadPlanBuilder.js.map