import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { CachedFunction } from '../../../../base/common/cache.js';
import { Event } from '../../../../base/common/event.js';
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
import { derived, derivedOpts } from '../../../../base/common/observableInternal/derived.js';
import { autorun, autorunDelta } from '../../../../base/common/observableInternal/autorun.js';
import { observableFromEvent, debouncedObservable2, wasEventTriggeredRecently, observableFromPromise, observableSignalFromEvent } from '../../../../base/common/observableInternal/utils.js';
import '../../../../base/common/cancellation.js';
import { isDiffEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js';
import { FoldingController } from '../../../../editor/contrib/folding/browser/folding.js';
import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { MarkerSeverity, IMarkerService } from '../../../../platform/markers/common/markers.js';
import { IDebugService } from '../../debug/common/debug.js';
import { IEditorService } from '../../../services/editor/common/editorService.js';
let SignalLineFeatureContribution = class SignalLineFeatureContribution extends Disposable {
    constructor(editorService, instantiationService, accessibilitySignalService, _configurationService) {
        super();
        this.editorService = editorService;
        this.instantiationService = instantiationService;
        this.accessibilitySignalService = accessibilitySignalService;
        this._configurationService = _configurationService;
        this.store = this._register(( new DisposableStore()));
        this.features = [
            this.instantiationService.createInstance(MarkerLineFeature, AccessibilitySignal.error, MarkerSeverity.Error),
            this.instantiationService.createInstance(MarkerLineFeature, AccessibilitySignal.warning, MarkerSeverity.Warning),
            this.instantiationService.createInstance(FoldedAreaLineFeature),
            this.instantiationService.createInstance(BreakpointLineFeature),
        ];
        this.isEnabledCache = ( new CachedFunction(
            (cue) => observableFromEvent(Event.any(this.accessibilitySignalService.onSoundEnabledChanged(cue), this.accessibilitySignalService.onAnnouncementEnabledChanged(cue)), () => this.accessibilitySignalService.isSoundEnabled(cue) || this.accessibilitySignalService.isAnnouncementEnabled(cue))
        ));
        this._someAccessibilitySignalIsEnabled = derived(this, (reader) => ( this.features.some((feature) => this.isEnabledCache.get(feature.signal).read(reader))));
        this._activeEditorObservable = observableFromEvent(this.editorService.onDidActiveEditorChange, (_) => {
            const activeTextEditorControl = this.editorService.activeTextEditorControl;
            const editor = isDiffEditor(activeTextEditorControl)
                ? activeTextEditorControl.getOriginalEditor()
                : isCodeEditor(activeTextEditorControl)
                    ? activeTextEditorControl
                    : undefined;
            return editor && editor.hasModel() ? { editor, model: editor.getModel() } : undefined;
        });
        this._register(autorun(reader => {
            this.store.clear();
            if (!this._someAccessibilitySignalIsEnabled.read(reader)) {
                return;
            }
            const activeEditor = this._activeEditorObservable.read(reader);
            if (activeEditor) {
                this.registerAccessibilitySignalsForEditor(activeEditor.editor, activeEditor.model, this.store);
            }
        }));
    }
    registerAccessibilitySignalsForEditor(editor, editorModel, store) {
        const curPosition = observableFromEvent(editor.onDidChangeCursorPosition, (args) => {
            if (args &&
                args.reason !== 3  &&
                args.reason !== 0 ) {
                return undefined;
            }
            return editor.getPosition();
        });
        const debouncedPosition = debouncedObservable2(curPosition, this._configurationService.getValue('accessibility.signals.debouncePositionChanges') ? 300 : 0);
        const isTyping = wasEventTriggeredRecently(e => editorModel.onDidChangeContent(e), 1000, store);
        const featureStates = ( this.features.map((feature) => {
            const lineFeatureState = feature.createSource(editor, editorModel);
            const isFeaturePresent = derivedOpts({ debugName: `isPresentInLine:${feature.signal.name}` }, (reader) => {
                if (!this.isEnabledCache.get(feature.signal).read(reader)) {
                    return false;
                }
                const position = debouncedPosition.read(reader);
                if (!position) {
                    return false;
                }
                return lineFeatureState.isPresent(position, reader);
            });
            return derivedOpts({ debugName: `typingDebouncedFeatureState:\n${feature.signal.name}` }, (reader) => feature.debounceWhileTyping && isTyping.read(reader)
                ? (debouncedPosition.read(reader), isFeaturePresent.get())
                : isFeaturePresent.read(reader));
        }));
        const state = derived((reader) =>  ({
            lineNumber: debouncedPosition.read(reader),
            featureStates: ( new Map(( this.features.map((feature, idx) => [
                feature,
                featureStates[idx].read(reader),
            ])))),
        }));
        store.add(autorunDelta(state, ({ lastValue, newValue }) => {
            const newFeatures = this.features.filter(feature => newValue?.featureStates.get(feature) &&
                (!lastValue?.featureStates?.get(feature) || newValue.lineNumber !== lastValue.lineNumber));
            this.accessibilitySignalService.playSignals(( newFeatures.map(f => f.signal)));
        }));
    }
};
SignalLineFeatureContribution = ( __decorate([
    ( __param(0, IEditorService)),
    ( __param(1, IInstantiationService)),
    ( __param(2, IAccessibilitySignalService)),
    ( __param(3, IConfigurationService))
], SignalLineFeatureContribution));
let MarkerLineFeature = class MarkerLineFeature {
    constructor(signal, severity, markerService) {
        this.signal = signal;
        this.severity = severity;
        this.markerService = markerService;
        this.debounceWhileTyping = true;
        this._previousLine = 0;
    }
    createSource(editor, model) {
        const obs = observableSignalFromEvent('onMarkerChanged', this.markerService.onMarkerChanged);
        return {
            isPresent: (position, reader) => {
                obs.read(reader);
                const lineChanged = position.lineNumber !== this._previousLine;
                this._previousLine = position.lineNumber;
                const hasMarker = ( this.markerService
                    .read({ resource: model.uri })
                    .some((m) => {
                    const onLine = m.severity === this.severity && m.startLineNumber <= position.lineNumber && position.lineNumber <= m.endLineNumber;
                    return lineChanged ? onLine : onLine && (position.lineNumber <= m.endLineNumber && m.startColumn <= position.column && m.endColumn >= position.column);
                }));
                return hasMarker;
            },
        };
    }
};
MarkerLineFeature = ( __decorate([
    ( __param(2, IMarkerService))
], MarkerLineFeature));
class FoldedAreaLineFeature {
    constructor() {
        this.signal = AccessibilitySignal.foldedArea;
    }
    createSource(editor, _model) {
        const foldingController = FoldingController.get(editor);
        if (!foldingController) {
            return { isPresent: () => false, };
        }
        const foldingModel = observableFromPromise(foldingController.getFoldingModel() ?? Promise.resolve(undefined));
        return {
            isPresent: (position, reader) => {
                const m = foldingModel.read(reader);
                const regionAtLine = m.value?.getRegionAtLine(position.lineNumber);
                const hasFolding = !regionAtLine
                    ? false
                    : regionAtLine.isCollapsed &&
                        regionAtLine.startLineNumber === position.lineNumber;
                return hasFolding;
            },
        };
    }
}
let BreakpointLineFeature = class BreakpointLineFeature {
    constructor(debugService) {
        this.debugService = debugService;
        this.signal = AccessibilitySignal.break;
    }
    createSource(editor, model) {
        const signal = observableSignalFromEvent('onDidChangeBreakpoints', this.debugService.getModel().onDidChangeBreakpoints);
        return {
            isPresent: (position, reader) => {
                signal.read(reader);
                const breakpoints = this.debugService
                    .getModel()
                    .getBreakpoints({ uri: model.uri, lineNumber: position.lineNumber });
                const hasBreakpoints = breakpoints.length > 0;
                return hasBreakpoints;
            },
        };
    }
};
BreakpointLineFeature = ( __decorate([
    ( __param(0, IDebugService))
], BreakpointLineFeature));
export { SignalLineFeatureContribution };
