/*---------------------------------------------------------------------------------------------
 *  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_MODE_ID } 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.languageId = this._selector();
        let listener;
        this._onDidChange = new Emitter({
            onFirstListenerAdd: () => {
                listener = onLanguagesMaybeChanged(() => this._evaluate());
            },
            onLastListenerRemove: () => {
                listener.dispose();
            }
        });
        this.onDidChange = this._onDidChange.event;
    }
    _evaluate() {
        const languageId = this._selector();
        if (languageId === this.languageId) {
            // no change
            return;
        }
        this.languageId = languageId;
        this._onDidChange.fire(this.languageId);
    }
}
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;
        ModeServiceImpl.instanceCount++;
        this._encounteredLanguages = new Set();
        this._registry = this._register(new LanguagesRegistry(true, warnOnOverwrite));
        this.languageIdCodec = this._registry.languageIdCodec;
        this._register(this._registry.onDidChange(() => this._onLanguagesMaybeChanged.fire()));
    }
    dispose() {
        ModeServiceImpl.instanceCount--;
        super.dispose();
    }
    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(languageId) {
        return this._registry.getMimeForMode(languageId);
    }
    getLanguageName(languageId) {
        return this._registry.getLanguageName(languageId);
    }
    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);
    }
    validateLanguageId(languageId) {
        return this._registry.validateLanguageId(languageId);
    }
    getConfigurationFiles(languageId) {
        return this._registry.getConfigurationFiles(languageId);
    }
    // --- instantiation
    create(commaSeparatedMimetypesOrCommaSeparatedIds) {
        return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
            const languageId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
            return this._createModeAndGetLanguageIdentifier(languageId);
        });
    }
    createByLanguageName(languageName) {
        return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
            const languageId = this._getModeIdByLanguageName(languageName);
            return this._createModeAndGetLanguageIdentifier(languageId);
        });
    }
    createByFilepathOrFirstLine(resource, firstLine) {
        return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
            const languageId = this.getModeIdByFilepathOrFirstLine(resource, firstLine);
            return this._createModeAndGetLanguageIdentifier(languageId);
        });
    }
    _createModeAndGetLanguageIdentifier(languageId) {
        // Fall back to plain text if no mode was found
        const validLanguageId = this.validateLanguageId(languageId || 'plaintext') || NULL_MODE_ID;
        this._getOrCreateMode(validLanguageId);
        return validLanguageId;
    }
    triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds) {
        const languageId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
        // Fall back to plain text if no mode was found
        this._getOrCreateMode(languageId || 'plaintext');
    }
    _getModeIdByLanguageName(languageName) {
        return this._registry.getModeIdFromLanguageName(languageName);
    }
    _getOrCreateMode(languageId) {
        if (!this._encounteredLanguages.has(languageId)) {
            this._encounteredLanguages.add(languageId);
            const validLanguageId = this.validateLanguageId(languageId) || NULL_MODE_ID;
            this._onDidEncounterLanguage.fire(validLanguageId);
        }
    }
}
ModeServiceImpl.instanceCount = 0;
