"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const async = require("../../../../base/common/async.js");
const errors = require("../../../../base/common/errors.js");
const event = require("../../../../base/common/event.js");
const lifecycle = require("../../../../base/common/lifecycle.js");
const characterClassifier = require("../../../common/core/characterClassifier.js");
const languages = require("../../../common/languages.js");
const provideSignatureHelp = require("./provideSignatureHelp.js");
var __awaiter = globalThis && globalThis.__awaiter || function(thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function(resolve) {
      resolve(value);
    });
  }
  return new (P || (P = Promise))(function(resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }
    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }
    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }
    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};
var ParameterHintState;
(function(ParameterHintState2) {
  ParameterHintState2.Default = {
    type: 0
    /* Default */
  };
  class Pending {
    constructor(request, previouslyActiveHints) {
      this.request = request;
      this.previouslyActiveHints = previouslyActiveHints;
      this.type = 2;
    }
  }
  ParameterHintState2.Pending = Pending;
  class Active {
    constructor(hints) {
      this.hints = hints;
      this.type = 1;
    }
  }
  ParameterHintState2.Active = Active;
})(ParameterHintState || (ParameterHintState = {}));
class ParameterHintsModel extends lifecycle.Disposable {
  constructor(editor, providers, delay = ParameterHintsModel.DEFAULT_DELAY) {
    super();
    this._onChangedHints = this._register(new event.Emitter());
    this.onChangedHints = this._onChangedHints.event;
    this.triggerOnType = false;
    this._state = ParameterHintState.Default;
    this._pendingTriggers = [];
    this._lastSignatureHelpResult = this._register(new lifecycle.MutableDisposable());
    this.triggerChars = new characterClassifier.CharacterSet();
    this.retriggerChars = new characterClassifier.CharacterSet();
    this.triggerId = 0;
    this.editor = editor;
    this.providers = providers;
    this.throttledDelayer = new async.Delayer(delay);
    this._register(this.editor.onDidBlurEditorWidget(() => this.cancel()));
    this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange()));
    this._register(this.editor.onDidChangeModel((e) => this.onModelChanged()));
    this._register(this.editor.onDidChangeModelLanguage((_) => this.onModelChanged()));
    this._register(this.editor.onDidChangeCursorSelection((e) => this.onCursorChange(e)));
    this._register(this.editor.onDidChangeModelContent((e) => this.onModelContentChange()));
    this._register(this.providers.onDidChange(this.onModelChanged, this));
    this._register(this.editor.onDidType((text) => this.onDidType(text)));
    this.onEditorConfigurationChange();
    this.onModelChanged();
  }
  get state() {
    return this._state;
  }
  set state(value) {
    if (this._state.type === 2) {
      this._state.request.cancel();
    }
    this._state = value;
  }
  cancel(silent = false) {
    this.state = ParameterHintState.Default;
    this.throttledDelayer.cancel();
    if (!silent) {
      this._onChangedHints.fire(void 0);
    }
  }
  trigger(context, delay) {
    const model = this.editor.getModel();
    if (!model || !this.providers.has(model)) {
      return;
    }
    const triggerId = ++this.triggerId;
    this._pendingTriggers.push(context);
    this.throttledDelayer.trigger(() => {
      return this.doTrigger(triggerId);
    }, delay).catch(errors.onUnexpectedError);
  }
  next() {
    if (this.state.type !== 1) {
      return;
    }
    const length = this.state.hints.signatures.length;
    const activeSignature = this.state.hints.activeSignature;
    const last = activeSignature % length === length - 1;
    const cycle = this.editor.getOption(
      76
      /* parameterHints */
    ).cycle;
    if ((length < 2 || last) && !cycle) {
      this.cancel();
      return;
    }
    this.updateActiveSignature(last && cycle ? 0 : activeSignature + 1);
  }
  previous() {
    if (this.state.type !== 1) {
      return;
    }
    const length = this.state.hints.signatures.length;
    const activeSignature = this.state.hints.activeSignature;
    const first = activeSignature === 0;
    const cycle = this.editor.getOption(
      76
      /* parameterHints */
    ).cycle;
    if ((length < 2 || first) && !cycle) {
      this.cancel();
      return;
    }
    this.updateActiveSignature(first && cycle ? length - 1 : activeSignature - 1);
  }
  updateActiveSignature(activeSignature) {
    if (this.state.type !== 1) {
      return;
    }
    this.state = new ParameterHintState.Active(Object.assign(Object.assign({}, this.state.hints), { activeSignature }));
    this._onChangedHints.fire(this.state.hints);
  }
  doTrigger(triggerId) {
    return __awaiter(this, void 0, void 0, function* () {
      const isRetrigger = this.state.type === 1 || this.state.type === 2;
      const activeSignatureHelp = this.getLastActiveHints();
      this.cancel(true);
      if (this._pendingTriggers.length === 0) {
        return false;
      }
      const context = this._pendingTriggers.reduce(mergeTriggerContexts);
      this._pendingTriggers = [];
      const triggerContext = {
        triggerKind: context.triggerKind,
        triggerCharacter: context.triggerCharacter,
        isRetrigger,
        activeSignatureHelp
      };
      if (!this.editor.hasModel()) {
        return false;
      }
      const model = this.editor.getModel();
      const position = this.editor.getPosition();
      this.state = new ParameterHintState.Pending(async.createCancelablePromise((token) => provideSignatureHelp.provideSignatureHelp(this.providers, model, position, triggerContext, token)), activeSignatureHelp);
      try {
        const result = yield this.state.request;
        if (triggerId !== this.triggerId) {
          result === null || result === void 0 ? void 0 : result.dispose();
          return false;
        }
        if (!result || !result.value.signatures || result.value.signatures.length === 0) {
          result === null || result === void 0 ? void 0 : result.dispose();
          this._lastSignatureHelpResult.clear();
          this.cancel();
          return false;
        } else {
          this.state = new ParameterHintState.Active(result.value);
          this._lastSignatureHelpResult.value = result;
          this._onChangedHints.fire(this.state.hints);
          return true;
        }
      } catch (error) {
        if (triggerId === this.triggerId) {
          this.state = ParameterHintState.Default;
        }
        errors.onUnexpectedError(error);
        return false;
      }
    });
  }
  getLastActiveHints() {
    switch (this.state.type) {
      case 1:
        return this.state.hints;
      case 2:
        return this.state.previouslyActiveHints;
      default:
        return void 0;
    }
  }
  get isTriggered() {
    return this.state.type === 1 || this.state.type === 2 || this.throttledDelayer.isTriggered();
  }
  onModelChanged() {
    this.cancel();
    this.triggerChars = new characterClassifier.CharacterSet();
    this.retriggerChars = new characterClassifier.CharacterSet();
    const model = this.editor.getModel();
    if (!model) {
      return;
    }
    for (const support of this.providers.ordered(model)) {
      for (const ch of support.signatureHelpTriggerCharacters || []) {
        this.triggerChars.add(ch.charCodeAt(0));
        this.retriggerChars.add(ch.charCodeAt(0));
      }
      for (const ch of support.signatureHelpRetriggerCharacters || []) {
        this.retriggerChars.add(ch.charCodeAt(0));
      }
    }
  }
  onDidType(text) {
    if (!this.triggerOnType) {
      return;
    }
    const lastCharIndex = text.length - 1;
    const triggerCharCode = text.charCodeAt(lastCharIndex);
    if (this.triggerChars.has(triggerCharCode) || this.isTriggered && this.retriggerChars.has(triggerCharCode)) {
      this.trigger({
        triggerKind: languages.SignatureHelpTriggerKind.TriggerCharacter,
        triggerCharacter: text.charAt(lastCharIndex)
      });
    }
  }
  onCursorChange(e) {
    if (e.source === "mouse") {
      this.cancel();
    } else if (this.isTriggered) {
      this.trigger({ triggerKind: languages.SignatureHelpTriggerKind.ContentChange });
    }
  }
  onModelContentChange() {
    if (this.isTriggered) {
      this.trigger({ triggerKind: languages.SignatureHelpTriggerKind.ContentChange });
    }
  }
  onEditorConfigurationChange() {
    this.triggerOnType = this.editor.getOption(
      76
      /* parameterHints */
    ).enabled;
    if (!this.triggerOnType) {
      this.cancel();
    }
  }
  dispose() {
    this.cancel(true);
    super.dispose();
  }
}
ParameterHintsModel.DEFAULT_DELAY = 120;
function mergeTriggerContexts(previous, current) {
  switch (current.triggerKind) {
    case languages.SignatureHelpTriggerKind.Invoke:
      return current;
    case languages.SignatureHelpTriggerKind.ContentChange:
      return previous;
    case languages.SignatureHelpTriggerKind.TriggerCharacter:
    default:
      return current;
  }
}
exports.ParameterHintsModel = ParameterHintsModel;
