var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { Emitter } from '../../../../base/common/event.js';
import { TokenizationResult2 } from '../../../../editor/common/core/token.js';
import { TokenMetadata } from '../../../../editor/common/modes.js';
import { nullTokenize2 } from '../../../../editor/common/modes/nullMode.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
export function parseTextMateGrammar(grammar, modeService) {
    const embeddedLanguages = Object.create(null);
    if (grammar.embeddedLanguages) {
        let scopes = Object.keys(grammar.embeddedLanguages);
        for (let i = 0, len = scopes.length; i < len; i++) {
            let scope = scopes[i];
            let language = grammar.embeddedLanguages[scope];
            if (typeof language !== 'string') {
                // never hurts to be too careful
                continue;
            }
            const validLanguageId = modeService.validateLanguageId(language);
            if (validLanguageId) {
                embeddedLanguages[scope] = modeService.languageIdCodec.encodeLanguageId(validLanguageId);
            }
        }
    }
    const tokenTypes = Object.create(null);
    if (grammar.tokenTypes) {
        const scopes = Object.keys(grammar.tokenTypes);
        for (const scope of scopes) {
            const tokenType = grammar.tokenTypes[scope];
            switch (tokenType) {
                case 'string':
                    tokenTypes[scope] = 2 /* String */;
                    break;
                case 'other':
                    tokenTypes[scope] = 0 /* Other */;
                    break;
                case 'comment':
                    tokenTypes[scope] = 1 /* Comment */;
                    break;
            }
        }
    }
    let validLanguageId = null;
    if (grammar.language) {
        validLanguageId = modeService.validateLanguageId(grammar.language);
    }
    return {
        language: validLanguageId ? validLanguageId : undefined,
        scopeName: grammar.scopeName,
        embeddedLanguages: embeddedLanguages,
        tokenTypes: tokenTypes,
        injectTo: grammar.injectTo,
    };
}
let TMTokenizationSupport = class TMTokenizationSupport {
    constructor(languageId, encodedLanguageId, actual, _configurationService) {
        this._configurationService = _configurationService;
        this._languageId = languageId;
        this._encodedLanguageId = encodedLanguageId;
        this._actual = actual;
        this._maxTokenizationLineLength = this._configurationService.getValue('editor.maxTokenizationLineLength', {
            overrideIdentifier: this._languageId
        });
        this._configurationService.onDidChangeConfiguration(e => {
            if (e.affectsConfiguration('editor.maxTokenizationLineLength')) {
                this._maxTokenizationLineLength = this._configurationService.getValue('editor.maxTokenizationLineLength', {
                    overrideIdentifier: this._languageId
                });
            }
        });
    }
    getInitialState() {
        return this._actual.getInitialState();
    }
    tokenize(line, hasEOL, state, offsetDelta) {
        throw new Error('Not supported!');
    }
    tokenize2(line, hasEOL, state, offsetDelta) {
        if (offsetDelta !== 0) {
            throw new Error('Unexpected: offsetDelta should be 0.');
        }
        // Do not attempt to tokenize if a line is too long
        if (line.length >= this._maxTokenizationLineLength) {
            return nullTokenize2(this._encodedLanguageId, line, state, offsetDelta);
        }
        return this._actual.tokenize2(line, state);
    }
};
TMTokenizationSupport = __decorate([
    __param(3, IConfigurationService)
], TMTokenizationSupport);
export { TMTokenizationSupport };
export class TMTokenization extends Disposable {
    constructor(grammar, initialState, containsEmbeddedLanguages) {
        super();
        this._onDidEncounterLanguage = this._register(new Emitter());
        this.onDidEncounterLanguage = this._onDidEncounterLanguage.event;
        this._grammar = grammar;
        this._initialState = initialState;
        this._containsEmbeddedLanguages = containsEmbeddedLanguages;
        this._seenLanguages = [];
    }
    getInitialState() {
        return this._initialState;
    }
    tokenize2(line, state) {
        const textMateResult = this._grammar.tokenizeLine2(line, state, 500);
        if (textMateResult.stoppedEarly) {
            console.warn(`Time limit reached when tokenizing line: ${line.substring(0, 100)}`);
            // return the state at the beginning of the line
            return new TokenizationResult2(textMateResult.tokens, state);
        }
        if (this._containsEmbeddedLanguages) {
            let seenLanguages = this._seenLanguages;
            let tokens = textMateResult.tokens;
            // Must check if any of the embedded languages was hit
            for (let i = 0, len = (tokens.length >>> 1); i < len; i++) {
                let metadata = tokens[(i << 1) + 1];
                let languageId = TokenMetadata.getLanguageId(metadata);
                if (!seenLanguages[languageId]) {
                    seenLanguages[languageId] = true;
                    this._onDidEncounterLanguage.fire(languageId);
                }
            }
        }
        let endState;
        // try to save an object if possible
        if (state.equals(textMateResult.ruleStack)) {
            endState = state;
        }
        else {
            endState = textMateResult.ruleStack;
        }
        return new TokenizationResult2(textMateResult.tokens, endState);
    }
}
