import { __decorate, __param } from '../../../../../../external/tslib/tslib.es6.js';
import { Disposable, toDisposable } from '../../../base/common/lifecycle.js';
import { FileAccess } from '../../../base/common/network.js';
import { IAccessibilityService } from '../../accessibility/common/accessibility.service.js';
import { IConfigurationService } from '../../configuration/common/configuration.service.js';
import { Event } from '../../../base/common/event.js';
import { localizeWithPath } from '../../../nls.js';
import { derived } from '../../../base/common/observableInternal/derived.js';
import '../../../base/common/observableInternal/autorun.js';
import { observableFromEvent } from '../../../base/common/observableInternal/utils.js';
import '../../../base/common/cancellation.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.service.js';
const _moduleId = "vs/platform/accessibilitySignal/browser/accessibilitySignalService";
let AccessibilitySignalService = class AccessibilitySignalService extends Disposable {
    constructor(configurationService, accessibilityService, telemetryService) {
        super();
        this.configurationService = configurationService;
        this.accessibilityService = accessibilityService;
        this.telemetryService = telemetryService;
        this.sounds = ( (new Map()));
        this.screenReaderAttached = observableFromEvent(this.accessibilityService.onDidChangeScreenReaderOptimized, () =>  this.accessibilityService.isScreenReaderOptimized());
        this.sentTelemetry = ( (new Set()));
        this.playingSounds = ( (new Set()));
        this.obsoleteAccessibilitySignalsEnabled = observableFromEvent(Event.filter(this.configurationService.onDidChangeConfiguration, (e) => e.affectsConfiguration('accessibilitySignals.enabled')), () =>  this.configurationService.getValue('accessibilitySignals.enabled'));
        this.isSoundEnabledCache = ( (new Cache((event) => {
            const settingObservable = observableFromEvent(Event.filter(this.configurationService.onDidChangeConfiguration, (e) => e.affectsConfiguration(event.signal.legacySoundSettingsKey) || e.affectsConfiguration(event.signal.settingsKey)), () => this.configurationService.getValue(event.signal.settingsKey + '.sound'));
            return derived(reader => {
                const setting = settingObservable.read(reader);
                if (setting === 'on' ||
                    (setting === 'auto' && this.screenReaderAttached.read(reader))) {
                    return true;
                }
                else if (setting === 'always' || setting === 'userGesture' && event.userGesture) {
                    return true;
                }
                const obsoleteSetting = this.obsoleteAccessibilitySignalsEnabled.read(reader);
                if (obsoleteSetting === 'on' ||
                    (obsoleteSetting === 'auto' && this.screenReaderAttached.read(reader))) {
                    return true;
                }
                return false;
            });
        }, JSON.stringify)));
        this.isAnnouncementEnabledCache = ( (new Cache((event) => {
            const settingObservable = observableFromEvent(Event.filter(this.configurationService.onDidChangeConfiguration, (e) => e.affectsConfiguration(event.signal.legacyAnnouncementSettingsKey) || e.affectsConfiguration(event.signal.settingsKey)), () => event.signal.announcementMessage ? this.configurationService.getValue(event.signal.settingsKey + '.announcement') : false);
            return derived(reader => {
                const setting = settingObservable.read(reader);
                if (!this.screenReaderAttached.read(reader)) {
                    return false;
                }
                return setting === 'auto' || setting === 'always' || setting === 'userGesture' && event.userGesture;
            });
        }, JSON.stringify)));
    }
    async playSignal(signal, options = {}) {
        const announcementMessage = signal.announcementMessage;
        if (this.isAnnouncementEnabled(signal, options.userGesture) && announcementMessage) {
            this.accessibilityService.status(announcementMessage);
        }
        if (this.isSoundEnabled(signal, options.userGesture)) {
            this.sendSignalTelemetry(signal, options.source);
            await this.playSound(signal.sound.getSound(), options.allowManyInParallel);
        }
    }
    async playSignals(signals) {
        for (const signal of signals) {
            this.sendSignalTelemetry('signal' in signal ? signal.signal : signal, 'source' in signal ? signal.source : undefined);
        }
        const signalArray = ( (signals.map(s => 'signal' in s ? s.signal : s)));
        const announcements = ( (signalArray.filter(signal => this.isAnnouncementEnabled(signal)).map(s => s.announcementMessage)));
        if (announcements.length) {
            this.accessibilityService.status(announcements.join(', '));
        }
        const sounds = ( (new Set(
             (signalArray.filter(signal => this.isSoundEnabled(signal)).map(signal => signal.sound.getSound()))
        )));
        await Promise.all(( (Array.from(sounds).map(sound => this.playSound(sound, true)))));
    }
    sendSignalTelemetry(signal, source) {
        const isScreenReaderOptimized = this.accessibilityService.isScreenReaderOptimized();
        const key = signal.name + (source ? `::${source}` : '') + (isScreenReaderOptimized ? '{screenReaderOptimized}' : '');
        if (( (this.sentTelemetry.has(key))) || this.getVolumeInPercent() === 0) {
            return;
        }
        this.sentTelemetry.add(key);
        this.telemetryService.publicLog2('signal.played', {
            signal: signal.name,
            source: source ?? '',
            isScreenReaderOptimized,
        });
    }
    getVolumeInPercent() {
        const volume = this.configurationService.getValue('accessibilitySignals.volume');
        if (typeof volume !== 'number') {
            return 50;
        }
        return Math.max(Math.min(volume, 100), 0);
    }
    async playSound(sound, allowManyInParallel = false) {
        if (!allowManyInParallel && ( (this.playingSounds.has(sound)))) {
            return;
        }
        this.playingSounds.add(sound);
        const url = ( (( (FileAccess.asBrowserUri(`vs/platform/accessibilitySignal/browser/media/${sound.fileName}`))).toString(true)));
        try {
            const sound = this.sounds.get(url);
            if (sound) {
                sound.volume = this.getVolumeInPercent() / 100;
                sound.currentTime = 0;
                await sound.play();
            }
            else {
                const playedSound = await playAudio(url, this.getVolumeInPercent() / 100);
                this.sounds.set(url, playedSound);
            }
        }
        catch (e) {
            if (!e.message.includes('play() can only be initiated by a user gesture')) {
                console.error('Error while playing sound', e);
            }
        }
        finally {
            this.playingSounds.delete(sound);
        }
    }
    playSignalLoop(signal, milliseconds) {
        let playing = true;
        const playSound = () => {
            if (playing) {
                this.playSignal(signal, { allowManyInParallel: true }).finally(() => {
                    setTimeout(() => {
                        if (playing) {
                            playSound();
                        }
                    }, milliseconds);
                });
            }
        };
        playSound();
        return toDisposable(() => playing = false);
    }
    isAnnouncementEnabled(signal, userGesture) {
        if (!signal.announcementMessage) {
            return false;
        }
        return this.isAnnouncementEnabledCache.get({ signal, userGesture }).get() ?? false;
    }
    isSoundEnabled(signal, userGesture) {
        return this.isSoundEnabledCache.get({ signal, userGesture }).get() ?? false;
    }
    onSoundEnabledChanged(signal) {
        return Event.fromObservableLight(this.isSoundEnabledCache.get({ signal }));
    }
    onAnnouncementEnabledChanged(signal) {
        return Event.fromObservableLight(this.isAnnouncementEnabledCache.get({ signal }));
    }
};
AccessibilitySignalService = ( (__decorate([
    ( (__param(0, IConfigurationService))),
    ( (__param(1, IAccessibilityService))),
    ( (__param(2, ITelemetryService)))
], AccessibilitySignalService)));
function playAudio(url, volume) {
    return (
         (new Promise((resolve, reject) => {
            const audio = ( (new Audio(url)));
            audio.volume = volume;
            audio.addEventListener('ended', () => {
                resolve(audio);
            });
            audio.addEventListener('error', (e) => {
                reject(e.error);
            });
            audio.play().catch(e => {
                reject(e);
            });
        }))
    );
}
class Cache {
    constructor(getValue, getKey) {
        this.getValue = getValue;
        this.getKey = getKey;
        this.map = ( (new Map()));
    }
    get(arg) {
        if (( (this.map.has(arg)))) {
            return this.map.get(arg);
        }
        const value = this.getValue(arg);
        const key = this.getKey(arg);
        this.map.set(key, value);
        return value;
    }
}
class Sound {
    static register(options) {
        const sound = ( (new Sound(options.fileName)));
        return sound;
    }
    static { this.error = Sound.register({ fileName: 'error.mp3' }); }
    static { this.warning = Sound.register({ fileName: 'warning.mp3' }); }
    static { this.foldedArea = Sound.register({ fileName: 'foldedAreas.mp3' }); }
    static { this.break = Sound.register({ fileName: 'break.mp3' }); }
    static { this.quickFixes = Sound.register({ fileName: 'quickFixes.mp3' }); }
    static { this.taskCompleted = Sound.register({ fileName: 'taskCompleted.mp3' }); }
    static { this.taskFailed = Sound.register({ fileName: 'taskFailed.mp3' }); }
    static { this.terminalBell = Sound.register({ fileName: 'terminalBell.mp3' }); }
    static { this.diffLineInserted = Sound.register({ fileName: 'diffLineInserted.mp3' }); }
    static { this.diffLineDeleted = Sound.register({ fileName: 'diffLineDeleted.mp3' }); }
    static { this.diffLineModified = Sound.register({ fileName: 'diffLineModified.mp3' }); }
    static { this.chatRequestSent = Sound.register({ fileName: 'chatRequestSent.mp3' }); }
    static { this.chatResponsePending = Sound.register({ fileName: 'chatResponsePending.mp3' }); }
    static { this.chatResponseReceived1 = Sound.register({ fileName: 'chatResponseReceived1.mp3' }); }
    static { this.chatResponseReceived2 = Sound.register({ fileName: 'chatResponseReceived2.mp3' }); }
    static { this.chatResponseReceived3 = Sound.register({ fileName: 'chatResponseReceived3.mp3' }); }
    static { this.chatResponseReceived4 = Sound.register({ fileName: 'chatResponseReceived4.mp3' }); }
    static { this.clear = Sound.register({ fileName: 'clear.mp3' }); }
    static { this.save = Sound.register({ fileName: 'save.mp3' }); }
    static { this.format = Sound.register({ fileName: 'format.mp3' }); }
    static { this.voiceRecordingStarted = Sound.register({ fileName: 'voiceRecordingStarted.mp3' }); }
    static { this.voiceRecordingStopped = Sound.register({ fileName: 'voiceRecordingStopped.mp3' }); }
    constructor(fileName) {
        this.fileName = fileName;
    }
}
class SoundSource {
    constructor(randomOneOf) {
        this.randomOneOf = randomOneOf;
    }
    getSound(deterministic = false) {
        if (deterministic || this.randomOneOf.length === 1) {
            return this.randomOneOf[0];
        }
        else {
            const index = Math.floor(Math.random() * this.randomOneOf.length);
            return this.randomOneOf[index];
        }
    }
}
class AccessibilitySignal {
    static { this._signals = ( (new Set())); }
    static register(options) {
        const soundSource = ( (new SoundSource(
            'randomOneOf' in options.sound ? options.sound.randomOneOf : [options.sound]
        )));
        const signal = ( (new AccessibilitySignal(
            soundSource,
            options.name,
            options.legacySoundSettingsKey,
            options.settingsKey,
            options.legacyAnnouncementSettingsKey,
            options.announcementMessage
        )));
        AccessibilitySignal._signals.add(signal);
        return signal;
    }
    static get allAccessibilitySignals() {
        return [...this._signals];
    }
    static { this.error = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 0, 'Error on Line')),
        sound: Sound.error,
        legacySoundSettingsKey: 'audioCues.lineHasError',
        legacyAnnouncementSettingsKey: "accessibility.alert.error" ,
        announcementMessage: ( localizeWithPath(_moduleId, 1, 'Error')),
        settingsKey: 'accessibility.signals.lineHasError'
    }); }
    static { this.warning = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 2, 'Warning on Line')),
        sound: Sound.warning,
        legacySoundSettingsKey: 'audioCues.lineHasWarning',
        legacyAnnouncementSettingsKey: "accessibility.alert.warning" ,
        announcementMessage: ( localizeWithPath(_moduleId, 3, 'Warning')),
        settingsKey: 'accessibility.signals.lineHasWarning'
    }); }
    static { this.foldedArea = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 4, 'Folded Area on Line')),
        sound: Sound.foldedArea,
        legacySoundSettingsKey: 'audioCues.lineHasFoldedArea',
        legacyAnnouncementSettingsKey: "accessibility.alert.foldedArea" ,
        announcementMessage: ( localizeWithPath(_moduleId, 5, 'Folded')),
        settingsKey: 'accessibility.signals.lineHasFoldedArea'
    }); }
    static { this.break = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 6, 'Breakpoint on Line')),
        sound: Sound.break,
        legacySoundSettingsKey: 'audioCues.lineHasBreakpoint',
        legacyAnnouncementSettingsKey: "accessibility.alert.breakpoint" ,
        announcementMessage: ( localizeWithPath(_moduleId, 7, 'Breakpoint')),
        settingsKey: 'accessibility.signals.lineHasBreakpoint'
    }); }
    static { this.inlineSuggestion = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 8, 'Inline Suggestion on Line')),
        sound: Sound.quickFixes,
        legacySoundSettingsKey: 'audioCues.lineHasInlineSuggestion',
        settingsKey: 'accessibility.signals.lineHasInlineSuggestion'
    }); }
    static { this.terminalQuickFix = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 9, 'Terminal Quick Fix')),
        sound: Sound.quickFixes,
        legacySoundSettingsKey: 'audioCues.terminalQuickFix',
        legacyAnnouncementSettingsKey: "accessibility.alert.terminalQuickFix" ,
        announcementMessage: ( localizeWithPath(_moduleId, 10, 'Quick Fix')),
        settingsKey: 'accessibility.signals.terminalQuickFix'
    }); }
    static { this.onDebugBreak = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 11, 'Debugger Stopped on Breakpoint')),
        sound: Sound.break,
        legacySoundSettingsKey: 'audioCues.onDebugBreak',
        legacyAnnouncementSettingsKey: "accessibility.alert.onDebugBreak" ,
        announcementMessage: ( localizeWithPath(_moduleId, 12, 'Breakpoint')),
        settingsKey: 'accessibility.signals.onDebugBreak'
    }); }
    static { this.noInlayHints = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 13, 'No Inlay Hints on Line')),
        sound: Sound.error,
        legacySoundSettingsKey: 'audioCues.noInlayHints',
        legacyAnnouncementSettingsKey: "accessibility.alert.noInlayHints" ,
        announcementMessage: ( localizeWithPath(_moduleId, 14, 'No Inlay Hints')),
        settingsKey: 'accessibility.signals.noInlayHints'
    }); }
    static { this.taskCompleted = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 15, 'Task Completed')),
        sound: Sound.taskCompleted,
        legacySoundSettingsKey: 'audioCues.taskCompleted',
        legacyAnnouncementSettingsKey: "accessibility.alert.taskCompleted" ,
        announcementMessage: ( localizeWithPath(_moduleId, 16, 'Task Completed')),
        settingsKey: 'accessibility.signals.taskCompleted'
    }); }
    static { this.taskFailed = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 17, 'Task Failed')),
        sound: Sound.taskFailed,
        legacySoundSettingsKey: 'audioCues.taskFailed',
        legacyAnnouncementSettingsKey: "accessibility.alert.taskFailed" ,
        announcementMessage: ( localizeWithPath(_moduleId, 18, 'Task Failed')),
        settingsKey: 'accessibility.signals.taskFailed'
    }); }
    static { this.terminalCommandFailed = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 19, 'Terminal Command Failed')),
        sound: Sound.error,
        legacySoundSettingsKey: 'audioCues.terminalCommandFailed',
        legacyAnnouncementSettingsKey: "accessibility.alert.terminalCommandFailed" ,
        announcementMessage: ( localizeWithPath(_moduleId, 20, 'Command Failed')),
        settingsKey: 'accessibility.signals.terminalCommandFailed'
    }); }
    static { this.terminalBell = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 21, 'Terminal Bell')),
        sound: Sound.terminalBell,
        legacySoundSettingsKey: 'audioCues.terminalBell',
        legacyAnnouncementSettingsKey: "accessibility.alert.terminalBell" ,
        announcementMessage: ( localizeWithPath(_moduleId, 22, 'Terminal Bell')),
        settingsKey: 'accessibility.signals.terminalBell'
    }); }
    static { this.notebookCellCompleted = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 23, 'Notebook Cell Completed')),
        sound: Sound.taskCompleted,
        legacySoundSettingsKey: 'audioCues.notebookCellCompleted',
        legacyAnnouncementSettingsKey: "accessibility.alert.notebookCellCompleted" ,
        announcementMessage: ( localizeWithPath(_moduleId, 24, 'Notebook Cell Completed')),
        settingsKey: 'accessibility.signals.notebookCellCompleted'
    }); }
    static { this.notebookCellFailed = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 25, 'Notebook Cell Failed')),
        sound: Sound.taskFailed,
        legacySoundSettingsKey: 'audioCues.notebookCellFailed',
        legacyAnnouncementSettingsKey: "accessibility.alert.notebookCellFailed" ,
        announcementMessage: ( localizeWithPath(_moduleId, 26, 'Notebook Cell Failed')),
        settingsKey: 'accessibility.signals.notebookCellFailed'
    }); }
    static { this.diffLineInserted = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 27, 'Diff Line Inserted')),
        sound: Sound.diffLineInserted,
        legacySoundSettingsKey: 'audioCues.diffLineInserted',
        settingsKey: 'accessibility.signals.diffLineInserted'
    }); }
    static { this.diffLineDeleted = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 28, 'Diff Line Deleted')),
        sound: Sound.diffLineDeleted,
        legacySoundSettingsKey: 'audioCues.diffLineDeleted',
        settingsKey: 'accessibility.signals.diffLineDeleted'
    }); }
    static { this.diffLineModified = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 29, 'Diff Line Modified')),
        sound: Sound.diffLineModified,
        legacySoundSettingsKey: 'audioCues.diffLineModified',
        settingsKey: 'accessibility.signals.diffLineModified'
    }); }
    static { this.chatRequestSent = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 30, 'Chat Request Sent')),
        sound: Sound.chatRequestSent,
        legacySoundSettingsKey: 'audioCues.chatRequestSent',
        legacyAnnouncementSettingsKey: "accessibility.alert.chatRequestSent" ,
        announcementMessage: ( localizeWithPath(_moduleId, 31, 'Chat Request Sent')),
        settingsKey: 'accessibility.signals.chatRequestSent'
    }); }
    static { this.chatResponseReceived = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 32, 'Chat Response Received')),
        legacySoundSettingsKey: 'audioCues.chatResponseReceived',
        sound: {
            randomOneOf: [
                Sound.chatResponseReceived1,
                Sound.chatResponseReceived2,
                Sound.chatResponseReceived3,
                Sound.chatResponseReceived4
            ]
        },
        settingsKey: 'accessibility.signals.chatResponseReceived'
    }); }
    static { this.chatResponsePending = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 33, 'Chat Response Pending')),
        sound: Sound.chatResponsePending,
        legacySoundSettingsKey: 'audioCues.chatResponsePending',
        legacyAnnouncementSettingsKey: "accessibility.alert.chatResponsePending" ,
        announcementMessage: ( localizeWithPath(_moduleId, 34, 'Chat Response Pending')),
        settingsKey: 'accessibility.signals.chatResponsePending'
    }); }
    static { this.clear = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 35, 'Clear')),
        sound: Sound.clear,
        legacySoundSettingsKey: 'audioCues.clear',
        legacyAnnouncementSettingsKey: "accessibility.alert.clear" ,
        announcementMessage: ( localizeWithPath(_moduleId, 36, 'Clear')),
        settingsKey: 'accessibility.signals.clear'
    }); }
    static { this.save = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 37, 'Save')),
        sound: Sound.save,
        legacySoundSettingsKey: 'audioCues.save',
        legacyAnnouncementSettingsKey: "accessibility.alert.save" ,
        announcementMessage: ( localizeWithPath(_moduleId, 38, 'Save')),
        settingsKey: 'accessibility.signals.save'
    }); }
    static { this.format = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 39, 'Format')),
        sound: Sound.format,
        legacySoundSettingsKey: 'audioCues.format',
        legacyAnnouncementSettingsKey: "accessibility.alert.format" ,
        announcementMessage: ( localizeWithPath(_moduleId, 40, 'Format')),
        settingsKey: 'accessibility.signals.format'
    }); }
    static { this.voiceRecordingStarted = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 41, 'Voice Recording Started')),
        sound: Sound.voiceRecordingStarted,
        legacySoundSettingsKey: 'audioCues.voiceRecordingStarted',
        settingsKey: 'accessibility.signals.voiceRecordingStarted'
    }); }
    static { this.voiceRecordingStopped = AccessibilitySignal.register({
        name: ( localizeWithPath(_moduleId, 42, 'Voice Recording Stopped')),
        sound: Sound.voiceRecordingStopped,
        legacySoundSettingsKey: 'audioCues.voiceRecordingStopped',
        settingsKey: 'accessibility.signals.voiceRecordingStopped'
    }); }
    constructor(sound, name, legacySoundSettingsKey, settingsKey, legacyAnnouncementSettingsKey, announcementMessage) {
        this.sound = sound;
        this.name = name;
        this.legacySoundSettingsKey = legacySoundSettingsKey;
        this.settingsKey = settingsKey;
        this.legacyAnnouncementSettingsKey = legacyAnnouncementSettingsKey;
        this.announcementMessage = announcementMessage;
    }
}
export { AccessibilitySignal, AccessibilitySignalService, Sound, SoundSource };
