
import { registerCss } from '../../../../../../../css.js';
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { $, addStandardDisposableGenericMouseDownListener, getDomNodePagePosition, addDisposableListener } from '../../../../base/browser/dom.js';
import { Gesture } from '../../../../base/browser/touch.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import '../../../../base/common/observableInternal/index.js';
import { ThemeIcon } from '../../../../base/common/themables.js';
import * as lightBulbWidget from './lightBulbWidget.css';
import { ContentWidgetPositionPreference } from '../../../browser/editorBrowser.js';
import { EditorOption } from '../../../common/config/editorOptions.js';
import { TrackedRangeStickiness, GlyphMarginLane } from '../../../common/model.js';
import { ModelDecorationOptions } from '../../../common/model/textModel.js';
import { computeIndentLevel } from '../../../common/model/utils.js';
import { autoFixCommandId, quickFixCommandId } from './codeAction.js';
import { localize } from '../../../../nls.js';
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.service.js';
import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js';
import { Range } from '../../../common/core/range.js';
import { observableValue } from '../../../../base/common/observableInternal/observables/observableValue.js';
import { derived } from '../../../../base/common/observableInternal/observables/derived.js';
import { autorun } from '../../../../base/common/observableInternal/reactions/autorun.js';

var LightBulbWidget_1;
registerCss(lightBulbWidget);
const GUTTER_LIGHTBULB_ICON = registerIcon("gutter-lightbulb", Codicon.lightBulb, ( localize(
    980,
    "Icon which spawns code actions menu from the gutter when there is no space in the editor."
)));
const GUTTER_LIGHTBULB_AUTO_FIX_ICON = registerIcon("gutter-lightbulb-auto-fix", Codicon.lightbulbAutofix, ( localize(
    981,
    "Icon which spawns code actions menu from the gutter when there is no space in the editor and a quick fix is available."
)));
const GUTTER_LIGHTBULB_AIFIX_ICON = registerIcon("gutter-lightbulb-sparkle", Codicon.lightbulbSparkle, ( localize(
    982,
    "Icon which spawns code actions menu from the gutter when there is no space in the editor and an AI fix is available."
)));
const GUTTER_LIGHTBULB_AIFIX_AUTO_FIX_ICON = registerIcon(
    "gutter-lightbulb-aifix-auto-fix",
    Codicon.lightbulbSparkleAutofix,
    ( localize(
    983,
    "Icon which spawns code actions menu from the gutter when there is no space in the editor and an AI fix and a quick fix is available."
))
);
const GUTTER_SPARKLE_FILLED_ICON = registerIcon("gutter-lightbulb-sparkle-filled", Codicon.sparkleFilled, ( localize(
    984,
    "Icon which spawns code actions menu from the gutter when there is no space in the editor and an AI fix and a quick fix is available."
)));
var LightBulbState;
(function(LightBulbState) {
    let Type;
    (function(Type) {
        Type[Type["Hidden"] = 0] = "Hidden";
        Type[Type["Showing"] = 1] = "Showing";
    })(Type = LightBulbState.Type || (LightBulbState.Type = {}));
    LightBulbState.Hidden = {
        type: Type.Hidden
    };
    class Showing {
        constructor(actions, trigger, editorPosition, widgetPosition) {
            this.actions = actions;
            this.trigger = trigger;
            this.editorPosition = editorPosition;
            this.widgetPosition = widgetPosition;
            this.type = Type.Showing;
        }
    }
    LightBulbState.Showing = Showing;
})(LightBulbState || (LightBulbState = {}));
let LightBulbWidget = class LightBulbWidget extends Disposable {
    static {
        LightBulbWidget_1 = this;
    }
    static {
        this.GUTTER_DECORATION = ModelDecorationOptions.register({
            description: "codicon-gutter-lightbulb-decoration",
            glyphMarginClassName: ThemeIcon.asClassName(Codicon.lightBulb),
            glyphMargin: {
                position: GlyphMarginLane.Left
            },
            stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
        });
    }
    static {
        this.ID = "editor.contrib.lightbulbWidget";
    }
    static {
        this._posPref = [ContentWidgetPositionPreference.EXACT];
    }
    static _computeLightBulbInfo(state, forGutter, preferredKbLabel, quickFixKbLabel) {
        if (state.type !== LightBulbState.Type.Showing) {
            return undefined;
        }
        const {
            actions,
            trigger
        } = state;
        let icon;
        let autoRun = false;
        if (actions.allAIFixes) {
            icon = forGutter ? GUTTER_SPARKLE_FILLED_ICON : Codicon.sparkleFilled;
            if (actions.validActions.length === 1) {
                autoRun = true;
            }
        } else if (actions.hasAutoFix) {
            if (actions.hasAIFix) {
                icon = forGutter ? GUTTER_LIGHTBULB_AIFIX_AUTO_FIX_ICON : Codicon.lightbulbSparkleAutofix;
            } else {
                icon = forGutter ? GUTTER_LIGHTBULB_AUTO_FIX_ICON : Codicon.lightbulbAutofix;
            }
        } else if (actions.hasAIFix) {
            icon = forGutter ? GUTTER_LIGHTBULB_AIFIX_ICON : Codicon.lightbulbSparkle;
        } else {
            icon = forGutter ? GUTTER_LIGHTBULB_ICON : Codicon.lightBulb;
        }
        let title;
        if (autoRun) {
            title = ( localize(985, "Run: {0}", actions.validActions[0].action.title));
        } else if (actions.hasAutoFix && preferredKbLabel) {
            title = ( localize(
                986,
                "Show Code Actions. Preferred Quick Fix Available ({0})",
                preferredKbLabel
            ));
        } else if (!actions.hasAutoFix && quickFixKbLabel) {
            title = ( localize(987, "Show Code Actions ({0})", quickFixKbLabel));
        } else {
            title = ( localize(988, "Show Code Actions"));
        }
        return {
            actions,
            trigger,
            icon,
            autoRun,
            title,
            isGutter: forGutter
        };
    }
    constructor(_editor, _keybindingService) {
        super();
        this._editor = _editor;
        this._keybindingService = _keybindingService;
        this._onClick = this._register(( new Emitter()));
        this.onClick = this._onClick.event;
        this._state = observableValue(this, LightBulbState.Hidden);
        this._gutterState = observableValue(this, LightBulbState.Hidden);
        this._combinedInfo = derived(this, reader => {
            const gutterState = this._gutterState.read(reader);
            if (gutterState.type === LightBulbState.Type.Showing) {
                return LightBulbWidget_1._computeLightBulbInfo(
                    gutterState,
                    true,
                    this._preferredKbLabel.read(reader),
                    this._quickFixKbLabel.read(reader)
                );
            }
            const state = this._state.read(reader);
            if (state.type === LightBulbState.Type.Showing) {
                return LightBulbWidget_1._computeLightBulbInfo(
                    state,
                    false,
                    this._preferredKbLabel.read(reader),
                    this._quickFixKbLabel.read(reader)
                );
            }
            return undefined;
        });
        this.lightBulbInfo = this._combinedInfo;
        this._iconClasses = [];
        this.lightbulbClasses = [
            "codicon-" + GUTTER_LIGHTBULB_ICON.id,
            "codicon-" + GUTTER_LIGHTBULB_AIFIX_AUTO_FIX_ICON.id,
            "codicon-" + GUTTER_LIGHTBULB_AUTO_FIX_ICON.id,
            "codicon-" + GUTTER_LIGHTBULB_AIFIX_ICON.id,
            "codicon-" + GUTTER_SPARKLE_FILLED_ICON.id
        ];
        this._preferredKbLabel = observableValue(this, undefined);
        this._quickFixKbLabel = observableValue(this, undefined);
        this.gutterDecoration = LightBulbWidget_1.GUTTER_DECORATION;
        this._domNode = $("div.lightBulbWidget");
        this._domNode.role = "listbox";
        this._register(Gesture.ignoreTarget(this._domNode));
        this._editor.addContentWidget(this);
        this._register(this._editor.onDidChangeModelContent(_ => {
            const editorModel = this._editor.getModel();
            const state = this._state.get();
            if (state.type !== LightBulbState.Type.Showing || !editorModel || state.editorPosition.lineNumber >= editorModel.getLineCount()) {
                this.hide();
            }
            const gutterState = this._gutterState.get();
            if (gutterState.type !== LightBulbState.Type.Showing || !editorModel || gutterState.editorPosition.lineNumber >= editorModel.getLineCount()) {
                this.gutterHide();
            }
        }));
        this._register(addStandardDisposableGenericMouseDownListener(this._domNode, e => {
            const state = this._state.get();
            if (state.type !== LightBulbState.Type.Showing) {
                return;
            }
            this._editor.focus();
            e.preventDefault();
            const {
                top,
                height
            } = getDomNodePagePosition(this._domNode);
            const lineHeight = this._editor.getOption(EditorOption.lineHeight);
            let pad = Math.floor(lineHeight / 3);
            if (state.widgetPosition.position !== null && state.widgetPosition.position.lineNumber < state.editorPosition.lineNumber) {
                pad += lineHeight;
            }
            this._onClick.fire({
                x: e.posx,
                y: top + height + pad,
                actions: state.actions,
                trigger: state.trigger
            });
        }));
        this._register(addDisposableListener(this._domNode, "mouseenter", e => {
            if ((e.buttons & 1) !== 1) {
                return;
            }
            this.hide();
        }));
        this._register(
            Event.runAndSubscribe(this._keybindingService.onDidUpdateKeybindings, () => {
                this._preferredKbLabel.set(
                    this._keybindingService.lookupKeybinding(autoFixCommandId)?.getLabel() ?? undefined,
                    undefined
                );
                this._quickFixKbLabel.set(
                    this._keybindingService.lookupKeybinding(quickFixCommandId)?.getLabel() ?? undefined,
                    undefined
                );
            })
        );
        this._register(autorun(reader => {
            const info = this._combinedInfo.read(reader);
            this._updateLightBulbTitleAndIcon(info);
            this._updateGutterDecorationOptions(info);
        }));
        this._register(this._editor.onMouseDown(async e => {
            if (!e.target.element || !( this.lightbulbClasses.some(cls => e.target.element && e.target.element.classList.contains(cls)))) {
                return;
            }
            const gutterState = this._gutterState.get();
            if (gutterState.type !== LightBulbState.Type.Showing) {
                return;
            }
            this._editor.focus();
            const {
                top,
                height
            } = getDomNodePagePosition(e.target.element);
            const lineHeight = this._editor.getOption(EditorOption.lineHeight);
            let pad = Math.floor(lineHeight / 3);
            if (gutterState.widgetPosition.position !== null && gutterState.widgetPosition.position.lineNumber < gutterState.editorPosition.lineNumber) {
                pad += lineHeight;
            }
            this._onClick.fire({
                x: e.event.posx,
                y: top + height + pad,
                actions: gutterState.actions,
                trigger: gutterState.trigger
            });
        }));
    }
    dispose() {
        super.dispose();
        this._editor.removeContentWidget(this);
        if (this._gutterDecorationID) {
            this._removeGutterDecoration(this._gutterDecorationID);
        }
    }
    getId() {
        return "LightBulbWidget";
    }
    getDomNode() {
        return this._domNode;
    }
    getPosition() {
        const state = this._state.get();
        return state.type === LightBulbState.Type.Showing ? state.widgetPosition : null;
    }
    update(actions, trigger, atPosition) {
        if (actions.validActions.length <= 0) {
            this.gutterHide();
            return this.hide();
        }
        const hasTextFocus = this._editor.hasTextFocus();
        if (!hasTextFocus) {
            this.gutterHide();
            return this.hide();
        }
        const options = this._editor.getOptions();
        if (!options.get(EditorOption.lightbulb).enabled) {
            this.gutterHide();
            return this.hide();
        }
        const model = this._editor.getModel();
        if (!model) {
            this.gutterHide();
            return this.hide();
        }
        const {
            lineNumber,
            column
        } = model.validatePosition(atPosition);
        const tabSize = model.getOptions().tabSize;
        const fontInfo = this._editor.getOptions().get(EditorOption.fontInfo);
        const lineContent = model.getLineContent(lineNumber);
        const indent = computeIndentLevel(lineContent, tabSize);
        const lineHasSpace = fontInfo.spaceWidth * indent > 22;
        const isFolded = lineNumber => {
            return lineNumber > 2 && this._editor.getTopForLineNumber(lineNumber) === this._editor.getTopForLineNumber(lineNumber - 1);
        };
        const currLineDecorations = this._editor.getLineDecorations(lineNumber);
        let hasDecoration = false;
        if (currLineDecorations) {
            for (const decoration of currLineDecorations) {
                const glyphClass = decoration.options.glyphMarginClassName;
                if (glyphClass && !( this.lightbulbClasses.some(className => glyphClass.includes(className)))) {
                    hasDecoration = true;
                    break;
                }
            }
        }
        let effectiveLineNumber = lineNumber;
        let effectiveColumnNumber = 1;
        if (!lineHasSpace) {
            const isLineEmptyOrIndented = lineNumber => {
                const lineContent = model.getLineContent(lineNumber);
                return /^\s*$|^\s+/.test(lineContent) || lineContent.length <= effectiveColumnNumber;
            };
            if (lineNumber > 1 && !isFolded(lineNumber - 1)) {
                const lineCount = model.getLineCount();
                const endLine = lineNumber === lineCount;
                const prevLineEmptyOrIndented = lineNumber > 1 && isLineEmptyOrIndented(lineNumber - 1);
                const nextLineEmptyOrIndented = !endLine && isLineEmptyOrIndented(lineNumber + 1);
                const currLineEmptyOrIndented = isLineEmptyOrIndented(lineNumber);
                const notEmpty = !nextLineEmptyOrIndented && !prevLineEmptyOrIndented;
                if (!nextLineEmptyOrIndented && !prevLineEmptyOrIndented && !hasDecoration) {
                    this._gutterState.set(new LightBulbState.Showing(actions, trigger, atPosition, {
                        position: {
                            lineNumber: effectiveLineNumber,
                            column: effectiveColumnNumber
                        },
                        preference: LightBulbWidget_1._posPref
                    }), undefined);
                    this.renderGutterLightbub();
                    return this.hide();
                } else if (prevLineEmptyOrIndented || endLine || (prevLineEmptyOrIndented && !currLineEmptyOrIndented)) {
                    effectiveLineNumber -= 1;
                } else if (nextLineEmptyOrIndented || (notEmpty && currLineEmptyOrIndented)) {
                    effectiveLineNumber += 1;
                }
            } else if (lineNumber === 1 && (lineNumber === model.getLineCount() || !isLineEmptyOrIndented(lineNumber + 1) && !isLineEmptyOrIndented(lineNumber))) {
                this._gutterState.set(new LightBulbState.Showing(actions, trigger, atPosition, {
                    position: {
                        lineNumber: effectiveLineNumber,
                        column: effectiveColumnNumber
                    },
                    preference: LightBulbWidget_1._posPref
                }), undefined);
                if (hasDecoration) {
                    this.gutterHide();
                } else {
                    this.renderGutterLightbub();
                    return this.hide();
                }
            } else if ((lineNumber < model.getLineCount()) && !isFolded(lineNumber + 1)) {
                effectiveLineNumber += 1;
            } else if (column * fontInfo.spaceWidth < 22) {
                return this.hide();
            }
            effectiveColumnNumber = /^\S\s*$/.test(model.getLineContent(effectiveLineNumber)) ? 2 : 1;
        }
        this._state.set(new LightBulbState.Showing(actions, trigger, atPosition, {
            position: {
                lineNumber: effectiveLineNumber,
                column: effectiveColumnNumber
            },
            preference: LightBulbWidget_1._posPref
        }), undefined);
        if (this._gutterDecorationID) {
            this._removeGutterDecoration(this._gutterDecorationID);
            this.gutterHide();
        }
        const validActions = actions.validActions;
        const actionKind = actions.validActions[0].action.kind;
        if (validActions.length !== 1 || !actionKind) {
            this._editor.layoutContentWidget(this);
            return;
        }
        this._editor.layoutContentWidget(this);
    }
    hide() {
        if (this._state.get() === LightBulbState.Hidden) {
            return;
        }
        this._state.set(LightBulbState.Hidden, undefined);
        this._editor.layoutContentWidget(this);
    }
    gutterHide() {
        if (this._gutterState.get() === LightBulbState.Hidden) {
            return;
        }
        if (this._gutterDecorationID) {
            this._removeGutterDecoration(this._gutterDecorationID);
        }
        this._gutterState.set(LightBulbState.Hidden, undefined);
    }
    _updateLightBulbTitleAndIcon(info) {
        this._domNode.classList.remove(...this._iconClasses);
        this._iconClasses = [];
        if (!info || info.isGutter) {
            return;
        }
        this._domNode.title = info.title;
        this._iconClasses = ThemeIcon.asClassNameArray(info.icon);
        this._domNode.classList.add(...this._iconClasses);
    }
    _updateGutterDecorationOptions(info) {
        if (!info || !info.isGutter) {
            return;
        }
        this.gutterDecoration = ModelDecorationOptions.register({
            description: "codicon-gutter-lightbulb-decoration",
            glyphMarginClassName: ThemeIcon.asClassName(info.icon),
            glyphMargin: {
                position: GlyphMarginLane.Left
            },
            stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
        });
    }
    renderGutterLightbub() {
        const selection = this._editor.getSelection();
        if (!selection) {
            return;
        }
        if (this._gutterDecorationID === undefined) {
            this._addGutterDecoration(selection.startLineNumber);
        } else {
            this._updateGutterDecoration(this._gutterDecorationID, selection.startLineNumber);
        }
    }
    _addGutterDecoration(lineNumber) {
        this._editor.changeDecorations(accessor => {
            this._gutterDecorationID = accessor.addDecoration(( new Range(lineNumber, 0, lineNumber, 0)), this.gutterDecoration);
        });
    }
    _removeGutterDecoration(decorationId) {
        this._editor.changeDecorations(accessor => {
            accessor.removeDecoration(decorationId);
            this._gutterDecorationID = undefined;
        });
    }
    _updateGutterDecoration(decorationId, lineNumber) {
        this._editor.changeDecorations(accessor => {
            accessor.changeDecoration(decorationId, ( new Range(lineNumber, 0, lineNumber, 0)));
            accessor.changeDecorationOptions(decorationId, this.gutterDecoration);
        });
    }
};
LightBulbWidget = LightBulbWidget_1 = ( __decorate([( __param(1, IKeybindingService))], LightBulbWidget));

export { LightBulbWidget };
