/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
/**
 * Captures all bracket related configurations for a single language.
 * Immutable.
*/
export class LanguageBracketsConfiguration {
    constructor(languageId, config) {
        this.languageId = languageId;
        let brackets;
        // Prefer colorized bracket pairs, as they are more accurate.
        // TODO@hediet: Deprecate `colorizedBracketPairs` and increase accuracy for brackets.
        if (config.colorizedBracketPairs) {
            brackets = filterValidBrackets(config.colorizedBracketPairs.map(b => [b[0], b[1]]));
        }
        else if (config.brackets) {
            brackets = filterValidBrackets(config.brackets
                .map((b) => [b[0], b[1]])
                // Many languages set < ... > as bracket pair, even though they also use it as comparison operator.
                // This leads to problems when colorizing this bracket, so we exclude it by default.
                // Languages can still override this by configuring `colorizedBracketPairs`
                // https://github.com/microsoft/vscode/issues/132476
                .filter((p) => !(p[0] === '<' && p[1] === '>')));
        }
        else {
            brackets = [];
        }
        const openingBracketInfos = new LazyMap((bracket) => {
            const closing = new Set();
            return {
                info: new OpeningBracketKind(this, bracket, closing),
                closing,
            };
        });
        const closingBracketInfos = new LazyMap((bracket) => {
            const opening = new Set();
            return {
                info: new ClosingBracketKind(this, bracket, opening),
                opening,
            };
        });
        for (const [open, close] of brackets) {
            const opening = openingBracketInfos.get(open);
            const closing = closingBracketInfos.get(close);
            opening.closing.add(closing.info);
            closing.opening.add(opening.info);
        }
        this._openingBrackets = new Map([...openingBracketInfos.innerMap].map(([k, v]) => [k, v.info]));
        this._closingBrackets = new Map([...closingBracketInfos.innerMap].map(([k, v]) => [k, v.info]));
    }
    /**
     * No two brackets have the same bracket text.
    */
    get openingBrackets() {
        return [...this._openingBrackets.values()];
    }
    /**
     * No two brackets have the same bracket text.
    */
    get closingBrackets() {
        return [...this._closingBrackets.values()];
    }
    getOpeningBracketInfo(bracketText) {
        return this._openingBrackets.get(bracketText);
    }
    getClosingBracketInfo(bracketText) {
        return this._closingBrackets.get(bracketText);
    }
    getBracketInfo(bracketText) {
        return this.getOpeningBracketInfo(bracketText) || this.getClosingBracketInfo(bracketText);
    }
}
function filterValidBrackets(bracketPairs) {
    return bracketPairs.filter(([open, close]) => open !== '' && close !== '');
}
export class BracketKindBase {
    constructor(config, bracketText) {
        this.config = config;
        this.bracketText = bracketText;
    }
    get languageId() {
        return this.config.languageId;
    }
}
export class OpeningBracketKind extends BracketKindBase {
    constructor(config, bracketText, openedBrackets) {
        super(config, bracketText);
        this.openedBrackets = openedBrackets;
        this.isOpeningBracket = true;
    }
}
export class ClosingBracketKind extends BracketKindBase {
    constructor(config, bracketText, 
    /**
     * Non empty array of all opening brackets this bracket closes.
    */
    closedBrackets) {
        super(config, bracketText);
        this.closedBrackets = closedBrackets;
        this.isOpeningBracket = false;
    }
    /**
     * Checks if this bracket closes the given other bracket.
     * Brackets from other language configuration can be used (they will always return false).
     * If other is a bracket with the same language id, they have to be from the same configuration.
    */
    closes(other) {
        if (other.languageId === this.languageId) {
            if (other['config'] !== this.config) {
                throw new BugIndicatingError('Brackets from different language configuration cannot be used.');
            }
        }
        return this.closedBrackets.has(other);
    }
    getClosedBrackets() {
        return [...this.closedBrackets];
    }
}
// Utilities
/**
 * This error indicates a bug.
 */
class BugIndicatingError extends Error {
    constructor(message) {
        super(message);
        Object.setPrototypeOf(this, BugIndicatingError.prototype);
        // Because we know for sure only buggy code throws this,
        // we definitely want to break here and fix the bug.
        // eslint-disable-next-line no-debugger
        debugger;
    }
}
class LazyMap {
    constructor(initialize) {
        this.initialize = initialize;
        this._map = new Map();
    }
    get innerMap() {
        return this._map;
    }
    get(key) {
        if (this._map.has(key)) {
            return this._map.get(key);
        }
        const value = this.initialize(key);
        this._map.set(key, value);
        return value;
    }
}
