import { LcsDiff } from "../../../../base/common/diff/diff.mjs";
import { getLeadingWhitespace, splitLines } from "../../../../base/common/strings.mjs";
import { Range } from "../../../common/core/range.mjs";
import { GhostTextPart, GhostText } from "./ghostText.mjs";
function normalizedInlineCompletionsEquals(a, b) {
  if (a === b) {
    return true;
  }
  if (!a || !b) {
    return false;
  }
  return a.range.equalsRange(b.range) && a.text === b.text && a.command === b.command;
}
function inlineCompletionToGhostText(inlineCompletion, textModel, mode, cursorPosition, previewSuffixLength = 0) {
  if (inlineCompletion.range.startLineNumber !== inlineCompletion.range.endLineNumber) {
    return void 0;
  }
  const sourceLine = textModel.getLineContent(inlineCompletion.range.startLineNumber);
  const sourceIndentationLength = getLeadingWhitespace(sourceLine).length;
  const suggestionTouchesIndentation = inlineCompletion.range.startColumn - 1 <= sourceIndentationLength;
  if (suggestionTouchesIndentation) {
    const suggestionAddedIndentationLength = getLeadingWhitespace(inlineCompletion.text).length;
    const replacedIndentation = sourceLine.substring(inlineCompletion.range.startColumn - 1, sourceIndentationLength);
    const rangeThatDoesNotReplaceIndentation = Range.fromPositions(inlineCompletion.range.getStartPosition().delta(0, replacedIndentation.length), inlineCompletion.range.getEndPosition());
    const suggestionWithoutIndentationChange = inlineCompletion.text.startsWith(replacedIndentation) ? inlineCompletion.text.substring(replacedIndentation.length) : inlineCompletion.text.substring(suggestionAddedIndentationLength);
    inlineCompletion = {
      range: rangeThatDoesNotReplaceIndentation,
      text: suggestionWithoutIndentationChange,
      command: inlineCompletion.command
    };
  }
  const valueToBeReplaced = textModel.getValueInRange(inlineCompletion.range);
  const changes = cachingDiff(valueToBeReplaced, inlineCompletion.text);
  if (!changes) {
    return void 0;
  }
  const lineNumber = inlineCompletion.range.startLineNumber;
  const parts = new Array();
  if (mode === "prefix") {
    const filteredChanges = changes.filter((c) => c.originalLength === 0);
    if (filteredChanges.length > 1 || filteredChanges.length === 1 && filteredChanges[0].originalStart !== valueToBeReplaced.length) {
      return void 0;
    }
  }
  const previewStartInCompletionText = inlineCompletion.text.length - previewSuffixLength;
  for (const c of changes) {
    const insertColumn = inlineCompletion.range.startColumn + c.originalStart + c.originalLength;
    if (mode === "subwordSmart" && cursorPosition && cursorPosition.lineNumber === inlineCompletion.range.startLineNumber && insertColumn < cursorPosition.column) {
      return void 0;
    }
    if (c.originalLength > 0) {
      return void 0;
    }
    if (c.modifiedLength === 0) {
      continue;
    }
    const modifiedEnd = c.modifiedStart + c.modifiedLength;
    const nonPreviewTextEnd = Math.max(c.modifiedStart, Math.min(modifiedEnd, previewStartInCompletionText));
    const nonPreviewText = inlineCompletion.text.substring(c.modifiedStart, nonPreviewTextEnd);
    const italicText = inlineCompletion.text.substring(nonPreviewTextEnd, Math.max(c.modifiedStart, modifiedEnd));
    if (nonPreviewText.length > 0) {
      const lines = splitLines(nonPreviewText);
      parts.push(new GhostTextPart(insertColumn, lines, false));
    }
    if (italicText.length > 0) {
      const lines = splitLines(italicText);
      parts.push(new GhostTextPart(insertColumn, lines, true));
    }
  }
  return new GhostText(lineNumber, parts, 0);
}
let lastRequest = void 0;
function cachingDiff(originalValue, newValue) {
  if ((lastRequest === null || lastRequest === void 0 ? void 0 : lastRequest.originalValue) === originalValue && (lastRequest === null || lastRequest === void 0 ? void 0 : lastRequest.newValue) === newValue) {
    return lastRequest === null || lastRequest === void 0 ? void 0 : lastRequest.changes;
  } else {
    const changes = smartDiff(originalValue, newValue);
    lastRequest = {
      originalValue,
      newValue,
      changes
    };
    return changes;
  }
}
function smartDiff(originalValue, newValue) {
  if (originalValue.length > 5e3 || newValue.length > 5e3) {
    return void 0;
  }
  function getMaxCharCode(val) {
    let maxCharCode2 = 0;
    for (let i = 0, len = val.length; i < len; i++) {
      const charCode = val.charCodeAt(i);
      if (charCode > maxCharCode2) {
        maxCharCode2 = charCode;
      }
    }
    return maxCharCode2;
  }
  const maxCharCode = Math.max(getMaxCharCode(originalValue), getMaxCharCode(newValue));
  function getUniqueCharCode(id) {
    if (id < 0) {
      throw new Error("unexpected");
    }
    return maxCharCode + id + 1;
  }
  function getElements(source) {
    let level = 0;
    let group = 0;
    const characters = new Int32Array(source.length);
    for (let i = 0, len = source.length; i < len; i++) {
      const id = group * 100 + level;
      if (source[i] === "(") {
        characters[i] = getUniqueCharCode(2 * id);
        level++;
      } else if (source[i] === ")") {
        characters[i] = getUniqueCharCode(2 * id + 1);
        if (level === 1) {
          group++;
        }
        level = Math.max(level - 1, 0);
      } else {
        characters[i] = source.charCodeAt(i);
      }
    }
    return characters;
  }
  const elements1 = getElements(originalValue);
  const elements2 = getElements(newValue);
  return new LcsDiff({ getElements: () => elements1 }, { getElements: () => elements2 }).ComputeDiff(false).changes;
}
export {
  inlineCompletionToGhostText,
  normalizedInlineCompletionsEquals
};
