"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const arrays = require("../../../../base/common/arrays.js");
const async = require("../../../../base/common/async.js");
const event = require("../../../../base/common/event.js");
const lifecycle = require("../../../../base/common/lifecycle.js");
const position = require("../../../common/core/position.js");
const range = require("../../../common/core/range.js");
const snippetParser = require("../../snippet/browser/snippetParser.js");
const snippetSession = require("../../snippet/browser/snippetSession.js");
const suggestController = require("../../suggest/browser/suggestController.js");
const inlineCompletionsModel = require("./inlineCompletionsModel.js");
const inlineCompletionToGhostText = require("./inlineCompletionToGhostText.js");
class SuggestWidgetInlineCompletionProvider extends lifecycle.Disposable {
  constructor(editor, suggestControllerPreselector) {
    super();
    this.editor = editor;
    this.suggestControllerPreselector = suggestControllerPreselector;
    this.isSuggestWidgetVisible = false;
    this.isShiftKeyPressed = false;
    this._isActive = false;
    this._currentSuggestItemInfo = void 0;
    this.onDidChangeEmitter = new event.Emitter();
    this.onDidChange = this.onDidChangeEmitter.event;
    this.setInactiveDelayed = this._register(new async.RunOnceScheduler(() => {
      if (!this.isSuggestWidgetVisible) {
        if (this._isActive) {
          this._isActive = false;
          this.onDidChangeEmitter.fire();
        }
      }
    }, 100));
    this._register(editor.onKeyDown((e) => {
      if (e.shiftKey && !this.isShiftKeyPressed) {
        this.isShiftKeyPressed = true;
        this.update(this._isActive);
      }
    }));
    this._register(editor.onKeyUp((e) => {
      if (e.shiftKey && this.isShiftKeyPressed) {
        this.isShiftKeyPressed = false;
        this.update(this._isActive);
      }
    }));
    const suggestController$1 = suggestController.SuggestController.get(this.editor);
    if (suggestController$1) {
      this._register(suggestController$1.registerSelector({
        priority: 100,
        select: (model, pos, suggestItems) => {
          const textModel = this.editor.getModel();
          const normalizedItemToPreselect = inlineCompletionsModel.minimizeInlineCompletion(textModel, this.suggestControllerPreselector());
          if (!normalizedItemToPreselect) {
            return -1;
          }
          const position$1 = position.Position.lift(pos);
          const candidates = suggestItems.map((suggestItem, index) => {
            const inlineSuggestItem = suggestionToSuggestItemInfo(suggestController$1, position$1, suggestItem, this.isShiftKeyPressed);
            const normalizedSuggestItem = inlineCompletionsModel.minimizeInlineCompletion(textModel, inlineSuggestItem === null || inlineSuggestItem === void 0 ? void 0 : inlineSuggestItem.normalizedInlineCompletion);
            if (!normalizedSuggestItem) {
              return void 0;
            }
            const valid = rangeStartsWith(normalizedItemToPreselect.range, normalizedSuggestItem.range) && normalizedItemToPreselect.text.startsWith(normalizedSuggestItem.text);
            return { index, valid, prefixLength: normalizedSuggestItem.text.length, suggestItem };
          }).filter((item) => item && item.valid);
          const result = arrays.findMaxBy(candidates, arrays.compareBy((s) => s.prefixLength, arrays.numberComparator));
          return result ? result.index : -1;
        }
      }));
      let isBoundToSuggestWidget = false;
      const bindToSuggestWidget = () => {
        if (isBoundToSuggestWidget) {
          return;
        }
        isBoundToSuggestWidget = true;
        this._register(suggestController$1.widget.value.onDidShow(() => {
          this.isSuggestWidgetVisible = true;
          this.update(true);
        }));
        this._register(suggestController$1.widget.value.onDidHide(() => {
          this.isSuggestWidgetVisible = false;
          this.setInactiveDelayed.schedule();
          this.update(this._isActive);
        }));
        this._register(suggestController$1.widget.value.onDidFocus(() => {
          this.isSuggestWidgetVisible = true;
          this.update(true);
        }));
      };
      this._register(event.Event.once(suggestController$1.model.onDidTrigger)((e) => {
        bindToSuggestWidget();
      }));
    }
    this.update(this._isActive);
  }
  /**
   * Returns undefined if the suggest widget is not active.
  */
  get state() {
    if (!this._isActive) {
      return void 0;
    }
    return { selectedItem: this._currentSuggestItemInfo };
  }
  update(newActive) {
    const newInlineCompletion = this.getSuggestItemInfo();
    let shouldFire = false;
    if (!suggestItemInfoEquals(this._currentSuggestItemInfo, newInlineCompletion)) {
      this._currentSuggestItemInfo = newInlineCompletion;
      shouldFire = true;
    }
    if (this._isActive !== newActive) {
      this._isActive = newActive;
      shouldFire = true;
    }
    if (shouldFire) {
      this.onDidChangeEmitter.fire();
    }
  }
  getSuggestItemInfo() {
    const suggestController$1 = suggestController.SuggestController.get(this.editor);
    if (!suggestController$1) {
      return void 0;
    }
    if (!this.isSuggestWidgetVisible) {
      return void 0;
    }
    const focusedItem = suggestController$1.widget.value.getFocusedItem();
    if (!focusedItem) {
      return void 0;
    }
    return suggestionToSuggestItemInfo(suggestController$1, this.editor.getPosition(), focusedItem.item, this.isShiftKeyPressed);
  }
  stopForceRenderingAbove() {
    const suggestController$1 = suggestController.SuggestController.get(this.editor);
    if (suggestController$1) {
      suggestController$1.stopForceRenderingAbove();
    }
  }
  forceRenderingAbove() {
    const suggestController$1 = suggestController.SuggestController.get(this.editor);
    if (suggestController$1) {
      suggestController$1.forceRenderingAbove();
    }
  }
}
function rangeStartsWith(rangeToTest, prefix) {
  return prefix.startLineNumber === rangeToTest.startLineNumber && prefix.startColumn === rangeToTest.startColumn && (prefix.endLineNumber < rangeToTest.endLineNumber || prefix.endLineNumber === rangeToTest.endLineNumber && prefix.endColumn <= rangeToTest.endColumn);
}
function suggestItemInfoEquals(a, b) {
  if (a === b) {
    return true;
  }
  if (!a || !b) {
    return false;
  }
  return a.completionItemKind === b.completionItemKind && a.isSnippetText === b.isSnippetText && inlineCompletionToGhostText.normalizedInlineCompletionsEquals(a.normalizedInlineCompletion, b.normalizedInlineCompletion);
}
function suggestionToSuggestItemInfo(suggestController2, position2, item, toggleMode) {
  if (Array.isArray(item.completion.additionalTextEdits) && item.completion.additionalTextEdits.length > 0) {
    return {
      completionItemKind: item.completion.kind,
      isSnippetText: false,
      normalizedInlineCompletion: {
        // Dummy element, so that space is reserved, but no text is shown
        range: range.Range.fromPositions(position2, position2),
        text: ""
      }
    };
  }
  let { insertText } = item.completion;
  let isSnippetText = false;
  if (item.completion.insertTextRules & 4) {
    const snippet = new snippetParser.SnippetParser().parse(insertText);
    const model = suggestController2.editor.getModel();
    if (snippet.children.length > 100) {
      return void 0;
    }
    snippetSession.SnippetSession.adjustWhitespace(model, position2, snippet, true, true);
    insertText = snippet.toString();
    isSnippetText = true;
  }
  const info = suggestController2.getOverwriteInfo(item, toggleMode);
  return {
    isSnippetText,
    completionItemKind: item.completion.kind,
    normalizedInlineCompletion: {
      text: insertText,
      range: range.Range.fromPositions(position2.delta(0, -info.overwriteBefore), position2.delta(0, Math.max(info.overwriteAfter, 0)))
    }
  };
}
exports.SuggestWidgetInlineCompletionProvider = SuggestWidgetInlineCompletionProvider;
exports.rangeStartsWith = rangeStartsWith;
