/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Get the source text offset equivalent to a given line/column pair.
 *
 * @param {Debugger.Source} source
 * @param {number} line The 1-based line number.
 * @param {number} column The 0-based column number.
 * @returns {number} The codepoint offset into the source's text.
 */
function findSourceOffset(source, line, column) {
  const offsets = getSourceLineOffsets(source);
  const offset = offsets[line - 1];

  if (offset) {
    // Make sure that columns that technically don't exist in the line text
    // don't cause the offset to wrap to the next line.
    return Math.min(offset.start + column, offset.textEnd);
  }

  return line < 0 ? 0 : offsets[offsets.length - 1].end;
}
exports.findSourceOffset = findSourceOffset;

const NEWLINE = /(\r?\n|\r|\u2028|\u2029)/g;
const SOURCE_OFFSETS = new WeakMap();
/**
 * Generate and cache line information for a given source to track what
 * text offsets mark the start and end of lines. Each entry in the array
 * represents a line in the source text.
 *
 * @param {Debugger.Source} source
 * @returns {Array<{ start, textEnd, end }>}
 *    - start - The codepoint offset of the start of the line.
 *    - textEnd - The codepoint offset just after the last non-newline character.
 *    - end - The codepoint offset of the end of the line. This will be
 *            be the same as the 'start' value of the next offset object,
 *            and this includes the newlines for the line itself, where
 *            'textEnd' excludes newline characters.
 */
function getSourceLineOffsets(source) {
  const cached = SOURCE_OFFSETS.get(source);
  if (cached) {
    return cached;
  }

  const { text } = source;

  const lines = text.split(NEWLINE);

  const offsets = [];
  let offset = 0;
  for (let i = 0; i < lines.length; i += 2) {
    const line = lines[i];
    const start = offset;

    // Calculate the end codepoint offset.
    let end = offset;
    // eslint-disable-next-line no-unused-vars
    for (const c of line) {
      end++;
    }
    const textEnd = end;

    if (i + 1 < lines.length) {
      end += lines[i + 1].length;
    }

    offsets.push(Object.freeze({ start, textEnd, end }));
    offset = end;
  }
  Object.freeze(offsets);

  SOURCE_OFFSETS.set(source, offsets);
  return offsets;
}

/**
 * Given a target actor and a source platform internal ID,
 * return the related SourceActor ID.

 * @param TargetActor targetActor
 *        The Target Actor from which this source originates.
 * @param String id
 *        Platform Source ID
 * @return String
 *         The SourceActor ID
 */
function getActorIdForInternalSourceId(targetActor, id) {
  const actor = targetActor.sourcesManager.getSourceActorByInternalSourceId(id);
  return actor ? actor.actorID : null;
}
exports.getActorIdForInternalSourceId = getActorIdForInternalSourceId;
