import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { createStyleSheet2 } from '../../../../base/browser/dom.js';
import { alert } from '../../../../base/browser/ui/aria/aria.js';
import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js';
import { disposableObservableValue, observableValue, transaction } from '../../../../base/common/observableInternal/base.js';
import { derived } from '../../../../base/common/observableInternal/derived.js';
import { autorun, autorunHandleChanges } from '../../../../base/common/observableInternal/autorun.js';
import { observableFromEvent, mapObservableArrayCached, observableSignal, constObservable } from '../../../../base/common/observableInternal/utils.js';
import '../../../../base/common/cancellation.js';
import { CoreEditingCommands } from '../../../browser/coreCommands.js';
import { Position } from '../../../common/core/position.js';
import { ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js';
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
import { inlineSuggestCommitId } from './commandIds.js';
import { GhostTextWidget } from './ghostTextWidget.js';
import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js';
import { InlineSuggestionHintsContentWidget, InlineCompletionsHintsWidget } from './inlineCompletionsHintsWidget.js';
import { VersionIdChangeReason, InlineCompletionsModel } from './inlineCompletionsModel.js';
import { SuggestWidgetAdaptor } from './suggestWidgetInlineCompletionProvider.js';
import { localizeWithPath } from '../../../../nls.js';
import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
var InlineCompletionsController_1;
let InlineCompletionsController = class InlineCompletionsController extends Disposable {
    static { InlineCompletionsController_1 = this; }
    static { this.ID = 'editor.contrib.inlineCompletionsController'; }
    static get(editor) {
        return editor.getContribution(InlineCompletionsController_1.ID);
    }
    constructor(editor, _instantiationService, _contextKeyService, _configurationService, _commandService, _debounceService, _languageFeaturesService, _accessibilitySignalService, _keybindingService) {
        super();
        this.editor = editor;
        this._instantiationService = _instantiationService;
        this._contextKeyService = _contextKeyService;
        this._configurationService = _configurationService;
        this._commandService = _commandService;
        this._debounceService = _debounceService;
        this._languageFeaturesService = _languageFeaturesService;
        this._accessibilitySignalService = _accessibilitySignalService;
        this._keybindingService = _keybindingService;
        this.model = this._register(disposableObservableValue('inlineCompletionModel', undefined));
        this._textModelVersionId = observableValue(this, -1);
        this._positions = observableValue(this, [( new Position(1, 1))]);
        this._suggestWidgetAdaptor = this._register(( new SuggestWidgetAdaptor(
            this.editor,
            () => this.model.get()?.selectedInlineCompletion.get()?.toSingleTextEdit(undefined),
            (tx) => this.updateObservables(tx, VersionIdChangeReason.Other),
            (item) => {
                transaction(tx => {
                    this.updateObservables(tx, VersionIdChangeReason.Other);
                    this.model.get()?.handleSuggestAccepted(item);
                });
            }
        )));
        this._enabled = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(62 ).enabled);
        this._fontFamily = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(62 ).fontFamily);
        this._ghostTexts = derived(this, (reader) => {
            const model = this.model.read(reader);
            return model?.ghostTexts.read(reader) ?? [];
        });
        this._stablizedGhostTexts = convertItemsToStableObservables(this._ghostTexts, this._store);
        this._ghostTextWidgets = mapObservableArrayCached(this, this._stablizedGhostTexts, (ghostText, store) => {
            return store.add(this._instantiationService.createInstance(GhostTextWidget, this.editor, {
                ghostText: ghostText,
                minReservedLineCount: constObservable(0),
                targetTextModel: ( this.model.map(v => v?.textModel)),
            }));
        }).recomputeInitiallyAndOnChange(this._store);
        this._debounceValue = this._debounceService.for(this._languageFeaturesService.inlineCompletionsProvider, 'InlineCompletionsDebounce', { min: 50, max: 50 });
        this._playAccessibilitySignal = observableSignal(this);
        this._isReadonly = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(91 ));
        this._textModel = observableFromEvent(this.editor.onDidChangeModel, () => this.editor.getModel());
        this._textModelIfWritable = derived(reader => this._isReadonly.read(reader) ? undefined : this._textModel.read(reader));
        this._register(( new InlineCompletionContextKeys(this._contextKeyService, this.model)));
        this._register(autorun(reader => {
            const textModel = this._textModelIfWritable.read(reader);
            transaction(tx => {
                this.model.set(undefined, tx);
                this.updateObservables(tx, VersionIdChangeReason.Other);
                if (textModel) {
                    const model = _instantiationService.createInstance(InlineCompletionsModel, textModel, this._suggestWidgetAdaptor.selectedItem, this._textModelVersionId, this._positions, this._debounceValue, observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(118 ).preview), observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(118 ).previewMode), observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(62 ).mode), this._enabled);
                    this.model.set(model, tx);
                }
            });
        }));
        const styleElement = this._register(createStyleSheet2());
        this._register(autorun(reader => {
            const fontFamily = this._fontFamily.read(reader);
            styleElement.setStyle(fontFamily === '' || fontFamily === 'default' ? `` : `
.monaco-editor .ghost-text-decoration,
.monaco-editor .ghost-text-decoration-preview,
.monaco-editor .ghost-text {
	font-family: ${fontFamily};
}`);
        }));
        const getReason = (e) => {
            if (e.isUndoing) {
                return VersionIdChangeReason.Undo;
            }
            if (e.isRedoing) {
                return VersionIdChangeReason.Redo;
            }
            if (this.model.get()?.isAcceptingPartially) {
                return VersionIdChangeReason.AcceptWord;
            }
            return VersionIdChangeReason.Other;
        };
        this._register(editor.onDidChangeModelContent((e) => transaction(tx =>
        this.updateObservables(tx, getReason(e)))));
        this._register(editor.onDidChangeCursorPosition(e => transaction(tx => {
            this.updateObservables(tx, VersionIdChangeReason.Other);
            if (e.reason === 3  || e.source === 'api') {
                this.model.get()?.stop(tx);
            }
        })));
        this._register(editor.onDidType(() => transaction(tx => {
            this.updateObservables(tx, VersionIdChangeReason.Other);
            if (this._enabled.get()) {
                this.model.get()?.trigger(tx);
            }
        })));
        this._register(this._commandService.onDidExecuteCommand((e) => {
            const commands = ( new Set([
                CoreEditingCommands.Tab.id,
                CoreEditingCommands.DeleteLeft.id,
                CoreEditingCommands.DeleteRight.id,
                inlineSuggestCommitId,
                'acceptSelectedSuggestion',
            ]));
            if (( commands.has(e.commandId)) && editor.hasTextFocus() && this._enabled.get()) {
                transaction(tx => {
                    this.model.get()?.trigger(tx);
                });
            }
        }));
        this._register(this.editor.onDidBlurEditorWidget(() => {
            if (this._contextKeyService.getContextKeyValue('accessibleViewIsShown') || this._configurationService.getValue('editor.inlineSuggest.keepOnBlur') ||
                editor.getOption(62 ).keepOnBlur) {
                return;
            }
            if (InlineSuggestionHintsContentWidget.dropDownVisible) {
                return;
            }
            transaction(tx => {
                this.model.get()?.stop(tx);
            });
        }));
        this._register(autorun(reader => {
            const state = this.model.read(reader)?.state.read(reader);
            if (state?.suggestItem) {
                if (state.primaryGhostText.lineCount >= 2) {
                    this._suggestWidgetAdaptor.forceRenderingAbove();
                }
            }
            else {
                this._suggestWidgetAdaptor.stopForceRenderingAbove();
            }
        }));
        this._register(toDisposable(() => {
            this._suggestWidgetAdaptor.stopForceRenderingAbove();
        }));
        let lastInlineCompletionId = undefined;
        this._register(autorunHandleChanges({
            handleChange: (context, changeSummary) => {
                if (context.didChange(this._playAccessibilitySignal)) {
                    lastInlineCompletionId = undefined;
                }
                return true;
            },
        }, async (reader) => {
            this._playAccessibilitySignal.read(reader);
            const model = this.model.read(reader);
            const state = model?.state.read(reader);
            if (!model || !state || !state.inlineCompletion) {
                lastInlineCompletionId = undefined;
                return;
            }
            if (state.inlineCompletion.semanticId !== lastInlineCompletionId) {
                lastInlineCompletionId = state.inlineCompletion.semanticId;
                const lineText = model.textModel.getLineContent(state.primaryGhostText.lineNumber);
                this._accessibilitySignalService.playSignal(AccessibilitySignal.inlineSuggestion).then(() => {
                    if (this.editor.getOption(8 )) {
                        this.provideScreenReaderUpdate(state.primaryGhostText.renderForScreenReader(lineText));
                    }
                });
            }
        }));
        this._register(( new InlineCompletionsHintsWidget(this.editor, this.model, this._instantiationService)));
        this._register(this._configurationService.onDidChangeConfiguration(e => {
            if (e.affectsConfiguration('accessibility.verbosity.inlineCompletions')) {
                this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.inlineCompletions') });
            }
        }));
        this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.inlineCompletions') });
    }
    playAccessibilitySignal(tx) {
        this._playAccessibilitySignal.trigger(tx);
    }
    provideScreenReaderUpdate(content) {
        const accessibleViewShowing = this._contextKeyService.getContextKeyValue('accessibleViewIsShown');
        const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView');
        let hint;
        if (!accessibleViewShowing && accessibleViewKeybinding && this.editor.getOption(149 )) {
            hint = ( localizeWithPath(
                'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController',
                'showAccessibleViewHint',
                "Inspect this in the accessible view ({0})",
                accessibleViewKeybinding.getAriaLabel()
            ));
        }
        hint ? alert(content + ', ' + hint) : alert(content);
    }
    updateObservables(tx, changeReason) {
        const newModel = this.editor.getModel();
        this._textModelVersionId.set(newModel?.getVersionId() ?? -1, tx, changeReason);
        this._positions.set(this.editor.getSelections()?.map(selection => selection.getPosition()) ?? [( new Position(1, 1))], tx);
    }
    shouldShowHoverAt(range) {
        const ghostText = this.model.get()?.primaryGhostText.get();
        if (ghostText) {
            return ( ghostText.parts.some(p => range.containsPosition(( new Position(ghostText.lineNumber, p.column)))));
        }
        return false;
    }
    shouldShowHoverAtViewZone(viewZoneId) {
        return this._ghostTextWidgets.get()[0]?.ownsViewZone(viewZoneId) ?? false;
    }
    hide() {
        transaction(tx => {
            this.model.get()?.stop(tx);
        });
    }
};
InlineCompletionsController = InlineCompletionsController_1 = ( __decorate([
    ( __param(1, IInstantiationService)),
    ( __param(2, IContextKeyService)),
    ( __param(3, IConfigurationService)),
    ( __param(4, ICommandService)),
    ( __param(5, ILanguageFeatureDebounceService)),
    ( __param(6, ILanguageFeaturesService)),
    ( __param(7, IAccessibilitySignalService)),
    ( __param(8, IKeybindingService))
], InlineCompletionsController));
function convertItemsToStableObservables(items, store) {
    const result = observableValue('result', []);
    const innerObservables = [];
    store.add(autorun(reader => {
        const itemsValue = items.read(reader);
        transaction(tx => {
            if (itemsValue.length !== innerObservables.length) {
                innerObservables.length = itemsValue.length;
                for (let i = 0; i < innerObservables.length; i++) {
                    if (!innerObservables[i]) {
                        innerObservables[i] = observableValue('item', itemsValue[i]);
                    }
                }
                result.set([...innerObservables], tx);
            }
            innerObservables.forEach((o, i) => o.set(itemsValue[i], tx));
        });
    }));
    return result;
}
export { InlineCompletionsController };
