/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import { Emitter } from '../../../base/common/event.js';
import { Disposable } from '../../../base/common/lifecycle.js';
import { NULL_LANGUAGE_IDENTIFIER } from '../modes/nullMode.js';
import { LanguagesRegistry } from './languagesRegistry.js';
import { firstOrDefault } from '../../../base/common/arrays.js';
class LanguageSelection {
    constructor(onLanguagesMaybeChanged, selector) {
        this._selector = selector;
        this.languageIdentifier = this._selector();
        let listener;
        this._onDidChange = new Emitter({
            onFirstListenerAdd: () => {
                listener = onLanguagesMaybeChanged(() => this._evaluate());
            },
            onLastListenerRemove: () => {
                listener.dispose();
            }
        });
        this.onDidChange = this._onDidChange.event;
    }
    _evaluate() {
        let languageIdentifier = this._selector();
        if (languageIdentifier.id === this.languageIdentifier.id) {
            // no change
            return;
        }
        this.languageIdentifier = languageIdentifier;
        this._onDidChange.fire(this.languageIdentifier);
    }
}
export class ModeServiceImpl extends Disposable {
    constructor(warnOnOverwrite = false) {
        super();
        this._onDidEncounterLanguage = this._register(new Emitter());
        this.onDidEncounterLanguage = this._onDidEncounterLanguage.event;
        this._onLanguagesMaybeChanged = this._register(new Emitter({ leakWarningThreshold: 200 /* https://github.com/microsoft/vscode/issues/119968 */ }));
        this.onLanguagesMaybeChanged = this._onLanguagesMaybeChanged.event;
        this._encounteredLanguages = new Set();
        this._registry = this._register(new LanguagesRegistry(true, warnOnOverwrite));
        this._register(this._registry.onDidChange(() => this._onLanguagesMaybeChanged.fire()));
    }
    isRegisteredMode(mimetypeOrModeId) {
        return this._registry.isRegisteredMode(mimetypeOrModeId);
    }
    getRegisteredModes() {
        return this._registry.getRegisteredModes();
    }
    getRegisteredLanguageNames() {
        return this._registry.getRegisteredLanguageNames();
    }
    getExtensions(alias) {
        return this._registry.getExtensions(alias);
    }
    getFilenames(alias) {
        return this._registry.getFilenames(alias);
    }
    getMimeForMode(modeId) {
        return this._registry.getMimeForMode(modeId);
    }
    getLanguageName(modeId) {
        return this._registry.getLanguageName(modeId);
    }
    getModeIdForLanguageName(alias) {
        return this._registry.getModeIdForLanguageNameLowercase(alias);
    }
    getModeIdByFilepathOrFirstLine(resource, firstLine) {
        const modeIds = this._registry.getModeIdsFromFilepathOrFirstLine(resource, firstLine);
        return firstOrDefault(modeIds, null);
    }
    getModeId(commaSeparatedMimetypesOrCommaSeparatedIds) {
        const modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds);
        return firstOrDefault(modeIds, null);
    }
    getLanguageIdentifier(modeId) {
        return this._registry.getLanguageIdentifier(modeId);
    }
    getConfigurationFiles(modeId) {
        return this._registry.getConfigurationFiles(modeId);
    }
    // --- instantiation
    create(commaSeparatedMimetypesOrCommaSeparatedIds) {
        return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
            const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
            return this._createModeAndGetLanguageIdentifier(modeId);
        });
    }
    createByLanguageName(languageName) {
        return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
            const modeId = this._getModeIdByLanguageName(languageName);
            return this._createModeAndGetLanguageIdentifier(modeId);
        });
    }
    createByFilepathOrFirstLine(resource, firstLine) {
        return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
            const modeId = this.getModeIdByFilepathOrFirstLine(resource, firstLine);
            return this._createModeAndGetLanguageIdentifier(modeId);
        });
    }
    _createModeAndGetLanguageIdentifier(modeId) {
        // Fall back to plain text if no mode was found
        const languageIdentifier = this.getLanguageIdentifier(modeId || 'plaintext') || NULL_LANGUAGE_IDENTIFIER;
        this._getOrCreateMode(languageIdentifier.language);
        return languageIdentifier;
    }
    triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds) {
        const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
        // Fall back to plain text if no mode was found
        this._getOrCreateMode(modeId || 'plaintext');
    }
    _getModeIdByLanguageName(languageName) {
        const modeIds = this._registry.getModeIdsFromLanguageName(languageName);
        return firstOrDefault(modeIds, null);
    }
    _getOrCreateMode(modeId) {
        if (!this._encounteredLanguages.has(modeId)) {
            this._encounteredLanguages.add(modeId);
            const languageIdentifier = this.getLanguageIdentifier(modeId) || NULL_LANGUAGE_IDENTIFIER;
            this._onDidEncounterLanguage.fire(languageIdentifier);
        }
    }
}
