
import { basename } from '../../../../base/common/path.js';
import { parse, getNodeType } from '../../../../base/common/json.js';
import { Color } from '../../../../base/common/color.js';
import { THEME_SCOPE_OPEN_PAREN, THEME_SCOPE_CLOSE_PAREN, THEME_SCOPE_WILDCARD, themeScopeRegex, ExtensionData } from './workbenchThemeService.js';
import { convertSettings } from './themeCompatibility.js';
import { localize } from '../../../../nls.js';
import { isString, isObject, isBoolean } from '../../../../base/common/types.js';
import { extname, joinPath, dirname } from '../../../../base/common/resources.js';
import { Extensions, DEFAULT_COLOR_CONFIG_VALUE } from '../../../../platform/theme/common/colorUtils.js';
import '../../../../platform/theme/common/colors/baseColors.js';
import '../../../../platform/theme/common/colors/chartsColors.js';
import { editorForeground, editorBackground } from '../../../../platform/theme/common/colors/editorColors.js';
import '../../../../platform/theme/common/colors/inputColors.js';
import '../../../../platform/theme/common/colors/listColors.js';
import '../../../../platform/theme/common/colors/menuColors.js';
import '../../../../platform/theme/common/colors/minimapColors.js';
import '../../../../platform/theme/common/colors/miscColors.js';
import '../../../../platform/theme/common/colors/quickpickColors.js';
import '../../../../platform/theme/common/colors/searchColors.js';
import { getThemeTypeSelector } from '../../../../platform/theme/common/themeService.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { getParseErrorMessage } from '../../../../base/common/jsonErrorMessages.js';
import { parse as parse$1 } from './plistParser.js';
import { getTokenClassificationRegistry, TokenStyle, parseClassifierString, SemanticTokenRule } from '../../../../platform/theme/common/tokenClassificationRegistry.js';
import { createMatchers } from './textMateScopeMatcher.js';
import { CharCode } from '../../../../base/common/charCode.js';
import { StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { ColorScheme, ThemeTypeSelector } from '../../../../platform/theme/common/theme.js';
import { MetadataConsts, FontStyle, ColorId } from '../../../../editor/common/encodedTokenAttributes.js';
import { toStandardTokenType } from '../../../../editor/common/languages/supports/tokenization.js';

const colorRegistry = ( Registry.as(Extensions.ColorContribution));
const tokenClassificationRegistry = getTokenClassificationRegistry();
const tokenGroupToScopesMap = {
    comments: ["comment", "punctuation.definition.comment"],
    strings: ["string", "meta.embedded.assembly"],
    keywords: ["keyword - keyword.operator", "keyword.control", "storage", "storage.type"],
    numbers: ["constant.numeric"],
    types: ["entity.name.type", "entity.name.class", "support.type", "support.class"],
    functions: ["entity.name.function", "support.function"],
    variables: ["variable", "entity.name.variable"]
};
class ColorThemeData {
    static {
        this.STORAGE_KEY = "colorThemeData";
    }
    constructor(id, label, settingsId) {
        this.themeTokenColors = [];
        this.customTokenColors = [];
        this.colorMap = {};
        this.customColorMap = {};
        this.semanticTokenRules = [];
        this.customSemanticTokenRules = [];
        this.textMateThemingRules = undefined;
        this.tokenColorIndex = undefined;
        this.tokenFontIndex = undefined;
        this.id = id;
        this.label = label;
        this.settingsId = settingsId;
        this.isLoaded = false;
    }
    get semanticHighlighting() {
        if (this.customSemanticHighlighting !== undefined) {
            return this.customSemanticHighlighting;
        }
        if (this.customSemanticHighlightingDeprecated !== undefined) {
            return this.customSemanticHighlightingDeprecated;
        }
        return !!this.themeSemanticHighlighting;
    }
    get tokenColors() {
        if (!this.textMateThemingRules) {
            const result = [];
            const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground);
            const background = this.getColor(editorBackground) || this.getDefault(editorBackground);
            result.push({
                settings: {
                    foreground: normalizeColor(foreground),
                    background: normalizeColor(background)
                }
            });
            let hasDefaultTokens = false;
            function addRule(rule) {
                if (rule.scope && rule.settings) {
                    if (rule.scope === "token.info-token") {
                        hasDefaultTokens = true;
                    }
                    const ruleSettings = rule.settings;
                    result.push({
                        scope: rule.scope,
                        settings: {
                            foreground: normalizeColor(ruleSettings.foreground),
                            background: normalizeColor(ruleSettings.background),
                            fontStyle: ruleSettings.fontStyle,
                            fontSize: ruleSettings.fontSize,
                            fontFamily: ruleSettings.fontFamily,
                            lineHeight: ruleSettings.lineHeight
                        }
                    });
                }
            }
            this.themeTokenColors.forEach(addRule);
            this.customTokenColors.forEach(addRule);
            if (!hasDefaultTokens) {
                defaultThemeColors[this.type].forEach(addRule);
            }
            this.textMateThemingRules = result;
        }
        return this.textMateThemingRules;
    }
    getColor(colorId, useDefault) {
        const customColor = this.customColorMap[colorId];
        if (customColor instanceof Color) {
            return customColor;
        }
        if (customColor === undefined) {
            const color = this.colorMap[colorId];
            if (color !== undefined) {
                return color;
            }
        }
        if (useDefault !== false) {
            return this.getDefault(colorId);
        }
        return undefined;
    }
    getTokenStyle(type, modifiers, language, useDefault = true, definitions = {}) {
        const result = {
            foreground: undefined,
            bold: undefined,
            underline: undefined,
            strikethrough: undefined,
            italic: undefined
        };
        const score = {
            foreground: -1,
            bold: -1,
            underline: -1,
            strikethrough: -1,
            italic: -1,
            fontFamily: -1,
            fontSize: -1,
            lineHeight: -1
        };
        function _processStyle(matchScore, style, definition) {
            if (style.foreground && score.foreground <= matchScore) {
                score.foreground = matchScore;
                result.foreground = style.foreground;
                definitions.foreground = definition;
            }
            for (const p of ["bold", "underline", "strikethrough", "italic"]) {
                const property = p;
                const info = style[property];
                if (info !== undefined) {
                    if (score[property] <= matchScore) {
                        score[property] = matchScore;
                        result[property] = info;
                        definitions[property] = definition;
                    }
                }
            }
        }
        function _processSemanticTokenRule(rule) {
            const matchScore = rule.selector.match(type, modifiers, language);
            if (matchScore >= 0) {
                _processStyle(matchScore, rule.style, rule);
            }
        }
        this.semanticTokenRules.forEach(_processSemanticTokenRule);
        this.customSemanticTokenRules.forEach(_processSemanticTokenRule);
        let hasUndefinedStyleProperty = false;
        for (const k in score) {
            const key = k;
            if (score[key] === -1) {
                hasUndefinedStyleProperty = true;
            } else {
                score[key] = Number.MAX_VALUE;
            }
        }
        if (hasUndefinedStyleProperty) {
            for (const rule of tokenClassificationRegistry.getTokenStylingDefaultRules()) {
                const matchScore = rule.selector.match(type, modifiers, language);
                if (matchScore >= 0) {
                    let style;
                    if (rule.defaults.scopesToProbe) {
                        style = this.resolveScopes(rule.defaults.scopesToProbe);
                        if (style) {
                            _processStyle(matchScore, style, rule.defaults.scopesToProbe);
                        }
                    }
                    if (!style && useDefault !== false) {
                        const tokenStyleValue = rule.defaults[this.type];
                        style = this.resolveTokenStyleValue(tokenStyleValue);
                        if (style) {
                            _processStyle(matchScore, style, tokenStyleValue);
                        }
                    }
                }
            }
        }
        return TokenStyle.fromData(result);
    }
    resolveTokenStyleValue(tokenStyleValue) {
        if (tokenStyleValue === undefined) {
            return undefined;
        } else if (typeof tokenStyleValue === "string") {
            const {
                type,
                modifiers,
                language
            } = parseClassifierString(tokenStyleValue, "");
            return this.getTokenStyle(type, modifiers, language);
        } else if (typeof tokenStyleValue === "object") {
            return tokenStyleValue;
        }
        return undefined;
    }
    getTokenColorIndex() {
        if (!this.tokenColorIndex) {
            const index = ( new TokenColorIndex());
            this.tokenColors.forEach(rule => {
                index.add(rule.settings.foreground);
                index.add(rule.settings.background);
            });
            this.semanticTokenRules.forEach(r => index.add(r.style.foreground));
            tokenClassificationRegistry.getTokenStylingDefaultRules().forEach(r => {
                const defaultColor = r.defaults[this.type];
                if (defaultColor && typeof defaultColor === "object") {
                    index.add(defaultColor.foreground);
                }
            });
            this.customSemanticTokenRules.forEach(r => index.add(r.style.foreground));
            this.tokenColorIndex = index;
        }
        return this.tokenColorIndex;
    }
    getTokenFontIndex() {
        if (!this.tokenFontIndex) {
            const index = ( new TokenFontIndex());
            this.tokenColors.forEach(
                r => index.add(r.settings.fontFamily, r.settings.fontSize, r.settings.lineHeight)
            );
            this.tokenFontIndex = index;
        }
        return this.tokenFontIndex;
    }
    get tokenColorMap() {
        return this.getTokenColorIndex().asArray();
    }
    get tokenFontMap() {
        return this.getTokenFontIndex().asArray();
    }
    getTokenStyleMetadata(
        typeWithLanguage,
        modifiers,
        defaultLanguage,
        useDefault = true,
        definitions = {}
    ) {
        const {
            type,
            language
        } = parseClassifierString(typeWithLanguage, defaultLanguage);
        const style = this.getTokenStyle(type, modifiers, language, useDefault, definitions);
        if (!style) {
            return undefined;
        }
        return {
            foreground: this.getTokenColorIndex().get(style.foreground),
            bold: style.bold,
            underline: style.underline,
            strikethrough: style.strikethrough,
            italic: style.italic
        };
    }
    getTokenStylingRuleScope(rule) {
        if (this.customSemanticTokenRules.indexOf(rule) !== -1) {
            return "setting";
        }
        if (this.semanticTokenRules.indexOf(rule) !== -1) {
            return "theme";
        }
        return undefined;
    }
    getDefault(colorId) {
        return colorRegistry.resolveDefaultColor(colorId, this);
    }
    resolveScopes(scopes, definitions) {
        if (!this.themeTokenScopeMatchers) {
            this.themeTokenScopeMatchers = ( this.themeTokenColors.map(getScopeMatcher));
        }
        if (!this.customTokenScopeMatchers) {
            this.customTokenScopeMatchers = ( this.customTokenColors.map(getScopeMatcher));
        }
        for (const scope of scopes) {
            let foreground = undefined;
            let fontStyle = undefined;
            let foregroundScore = -1;
            let fontStyleScore = -1;
            let fontStyleThemingRule = undefined;
            let foregroundThemingRule = undefined;
            function findTokenStyleForScopeInScopes(scopeMatchers, themingRules) {
                for (let i = 0; i < scopeMatchers.length; i++) {
                    const score = scopeMatchers[i](scope);
                    if (score >= 0) {
                        const themingRule = themingRules[i];
                        const settings = themingRules[i].settings;
                        if (score >= foregroundScore && settings.foreground) {
                            foreground = settings.foreground;
                            foregroundScore = score;
                            foregroundThemingRule = themingRule;
                        }
                        if (score >= fontStyleScore && isString(settings.fontStyle)) {
                            fontStyle = settings.fontStyle;
                            fontStyleScore = score;
                            fontStyleThemingRule = themingRule;
                        }
                    }
                }
            }
            findTokenStyleForScopeInScopes(this.themeTokenScopeMatchers, this.themeTokenColors);
            findTokenStyleForScopeInScopes(this.customTokenScopeMatchers, this.customTokenColors);
            if (foreground !== undefined || fontStyle !== undefined) {
                if (definitions) {
                    definitions.foreground = foregroundThemingRule;
                    definitions.bold = definitions.italic = definitions.underline = definitions.strikethrough = fontStyleThemingRule;
                    definitions.scope = scope;
                }
                return TokenStyle.fromSettings(foreground, fontStyle);
            }
        }
        return undefined;
    }
    defines(colorId) {
        const customColor = this.customColorMap[colorId];
        if (customColor instanceof Color) {
            return true;
        }
        return customColor === undefined  && this.colorMap.hasOwnProperty(colorId);
    }
    setCustomizations(settings) {
        this.setCustomColors(settings.colorCustomizations);
        this.setCustomTokenColors(settings.tokenColorCustomizations);
        this.setCustomSemanticTokenColors(settings.semanticTokenColorCustomizations);
    }
    setCustomColors(colors) {
        this.customColorMap = {};
        this.overwriteCustomColors(colors);
        const themeSpecificColors = this.getThemeSpecificColors(colors);
        if (isObject(themeSpecificColors)) {
            this.overwriteCustomColors(themeSpecificColors);
        }
        this.tokenColorIndex = undefined;
        this.tokenFontIndex = undefined;
        this.textMateThemingRules = undefined;
        this.customTokenScopeMatchers = undefined;
    }
    overwriteCustomColors(colors) {
        for (const id in colors) {
            const colorVal = colors[id];
            if (colorVal === DEFAULT_COLOR_CONFIG_VALUE) {
                this.customColorMap[id] = DEFAULT_COLOR_CONFIG_VALUE;
            } else if (typeof colorVal === "string") {
                this.customColorMap[id] = ( Color.fromHex(colorVal));
            }
        }
    }
    setCustomTokenColors(customTokenColors) {
        this.customTokenColors = [];
        this.customSemanticHighlightingDeprecated = undefined;
        this.addCustomTokenColors(customTokenColors);
        const themeSpecificTokenColors = this.getThemeSpecificColors(customTokenColors);
        if (isObject(themeSpecificTokenColors)) {
            this.addCustomTokenColors(themeSpecificTokenColors);
        }
        this.tokenColorIndex = undefined;
        this.tokenFontIndex = undefined;
        this.textMateThemingRules = undefined;
        this.customTokenScopeMatchers = undefined;
    }
    setCustomSemanticTokenColors(semanticTokenColors) {
        this.customSemanticTokenRules = [];
        this.customSemanticHighlighting = undefined;
        if (semanticTokenColors) {
            this.customSemanticHighlighting = semanticTokenColors.enabled;
            if (semanticTokenColors.rules) {
                this.readSemanticTokenRules(semanticTokenColors.rules);
            }
            const themeSpecificColors = this.getThemeSpecificColors(semanticTokenColors);
            if (isObject(themeSpecificColors)) {
                if (themeSpecificColors.enabled !== undefined) {
                    this.customSemanticHighlighting = themeSpecificColors.enabled;
                }
                if (themeSpecificColors.rules) {
                    this.readSemanticTokenRules(themeSpecificColors.rules);
                }
            }
        }
        this.tokenColorIndex = undefined;
        this.tokenFontIndex = undefined;
        this.textMateThemingRules = undefined;
    }
    isThemeScope(key) {
        return key.charAt(0) === THEME_SCOPE_OPEN_PAREN && key.charAt(key.length - 1) === THEME_SCOPE_CLOSE_PAREN;
    }
    isThemeScopeMatch(themeId) {
        const themeIdFirstChar = themeId.charAt(0);
        const themeIdLastChar = themeId.charAt(themeId.length - 1);
        const themeIdPrefix = themeId.slice(0, -1);
        const themeIdInfix = themeId.slice(1, -1);
        const themeIdSuffix = themeId.slice(1);
        return themeId === this.settingsId || (this.settingsId.includes(themeIdInfix) && themeIdFirstChar === THEME_SCOPE_WILDCARD && themeIdLastChar === THEME_SCOPE_WILDCARD) || (this.settingsId.startsWith(themeIdPrefix) && themeIdLastChar === THEME_SCOPE_WILDCARD) || (this.settingsId.endsWith(themeIdSuffix) && themeIdFirstChar === THEME_SCOPE_WILDCARD);
    }
    getThemeSpecificColors(colors) {
        let themeSpecificColors;
        for (const key in colors) {
            const scopedColors = colors[key];
            if (this.isThemeScope(key) && scopedColors instanceof Object && !Array.isArray(scopedColors)) {
                const themeScopeList = key.match(themeScopeRegex) || [];
                for (const themeScope of themeScopeList) {
                    const themeId = themeScope.substring(1, themeScope.length - 1);
                    if (this.isThemeScopeMatch(themeId)) {
                        if (!themeSpecificColors) {
                            themeSpecificColors = {};
                        }
                        const scopedThemeSpecificColors = scopedColors;
                        for (const subkey in scopedThemeSpecificColors) {
                            const originalColors = themeSpecificColors[subkey];
                            const overrideColors = scopedThemeSpecificColors[subkey];
                            if (Array.isArray(originalColors) && Array.isArray(overrideColors)) {
                                themeSpecificColors[subkey] = originalColors.concat(overrideColors);
                            } else if (overrideColors) {
                                themeSpecificColors[subkey] = overrideColors;
                            }
                        }
                    }
                }
            }
        }
        return themeSpecificColors;
    }
    readSemanticTokenRules(tokenStylingRuleSection) {
        for (const key in tokenStylingRuleSection) {
            if (!this.isThemeScope(key)) {
                try {
                    const rule = readSemanticTokenRule(key, tokenStylingRuleSection[key]);
                    if (rule) {
                        this.customSemanticTokenRules.push(rule);
                    }
                } catch (e) {}
            }
        }
    }
    addCustomTokenColors(customTokenColors) {
        for (const tokenGroup in tokenGroupToScopesMap) {
            const group = tokenGroup;
            const value = customTokenColors[group];
            if (value) {
                const settings = typeof value === "string" ? {
                    foreground: value
                } : value;
                const scopes = tokenGroupToScopesMap[group];
                for (const scope of scopes) {
                    this.customTokenColors.push({
                        scope,
                        settings
                    });
                }
            }
        }
        if (Array.isArray(customTokenColors.textMateRules)) {
            for (const rule of customTokenColors.textMateRules) {
                if (rule.scope && rule.settings) {
                    this.customTokenColors.push(rule);
                }
            }
        }
        if (customTokenColors.semanticHighlighting !== undefined) {
            this.customSemanticHighlightingDeprecated = customTokenColors.semanticHighlighting;
        }
    }
    ensureLoaded(extensionResourceLoaderService) {
        return !this.isLoaded ? this.load(extensionResourceLoaderService) : Promise.resolve(undefined);
    }
    reload(extensionResourceLoaderService) {
        return this.load(extensionResourceLoaderService);
    }
    load(extensionResourceLoaderService) {
        if (!this.location) {
            return Promise.resolve(undefined);
        }
        this.themeTokenColors = [];
        this.clearCaches();
        const result = {
            colors: {},
            textMateRules: [],
            semanticTokenRules: [],
            semanticHighlighting: false
        };
        return _loadColorTheme(extensionResourceLoaderService, this.location, result).then(_ => {
            this.isLoaded = true;
            this.semanticTokenRules = result.semanticTokenRules;
            this.colorMap = result.colors;
            this.themeTokenColors = result.textMateRules;
            this.themeSemanticHighlighting = result.semanticHighlighting;
        });
    }
    clearCaches() {
        this.tokenColorIndex = undefined;
        this.tokenFontIndex = undefined;
        this.textMateThemingRules = undefined;
        this.themeTokenScopeMatchers = undefined;
        this.customTokenScopeMatchers = undefined;
    }
    toStorage(storageService) {
        const colorMapData = {};
        for (const key in this.colorMap) {
            colorMapData[key] = Color.Format.CSS.formatHexA(this.colorMap[key], true);
        }
        const value = JSON.stringify({
            id: this.id,
            label: this.label,
            settingsId: this.settingsId,
            themeTokenColors: ( this.themeTokenColors.map(tc => ({
                settings: tc.settings,
                scope: tc.scope
            }))),
            semanticTokenRules: ( this.semanticTokenRules.map(SemanticTokenRule.toJSONObject)),
            extensionData: ExtensionData.toJSONObject(this.extensionData),
            themeSemanticHighlighting: this.themeSemanticHighlighting,
            colorMap: colorMapData,
            watch: this.watch
        });
        storageService.store(
            ColorThemeData.STORAGE_KEY,
            value,
            StorageScope.PROFILE,
            StorageTarget.USER
        );
    }
    get themeTypeSelector() {
        return this.classNames[0];
    }
    get classNames() {
        return this.id.split(" ");
    }
    get type() {
        switch (this.themeTypeSelector) {
        case ThemeTypeSelector.VS:
            return ColorScheme.LIGHT;
        case ThemeTypeSelector.HC_BLACK:
            return ColorScheme.HIGH_CONTRAST_DARK;
        case ThemeTypeSelector.HC_LIGHT:
            return ColorScheme.HIGH_CONTRAST_LIGHT;
        default:
            return ColorScheme.DARK;
        }
    }
    static createUnloadedThemeForThemeType(themeType, colorMap) {
        return ColorThemeData.createUnloadedTheme(getThemeTypeSelector(themeType), colorMap);
    }
    static createUnloadedTheme(id, colorMap) {
        const themeData = ( new ColorThemeData(id, "", "__" + id));
        themeData.isLoaded = false;
        themeData.themeTokenColors = [];
        themeData.watch = false;
        if (colorMap) {
            for (const id in colorMap) {
                themeData.colorMap[id] = ( Color.fromHex(colorMap[id]));
            }
        }
        return themeData;
    }
    static createLoadedEmptyTheme(id, settingsId) {
        const themeData = ( new ColorThemeData(id, "", settingsId));
        themeData.isLoaded = true;
        themeData.themeTokenColors = [];
        themeData.watch = false;
        return themeData;
    }
    static fromStorageData(storageService) {
        const input = storageService.get(ColorThemeData.STORAGE_KEY, StorageScope.PROFILE);
        if (!input) {
            return undefined;
        }
        try {
            const data = JSON.parse(input);
            const theme = ( new ColorThemeData("", "", ""));
            for (const key in data) {
                switch (key) {
                case "colorMap":
                    {
                        const colorMapData = data[key];
                        for (const id in colorMapData) {
                            theme.colorMap[id] = ( Color.fromHex(colorMapData[id]));
                        }
                        break;
                    }
                case "themeTokenColors":
                case "id":
                case "label":
                case "settingsId":
                case "watch":
                case "themeSemanticHighlighting":
                    theme[key] = data[key];
                    break;
                case "semanticTokenRules":
                    {
                        const rulesData = data[key];
                        if (Array.isArray(rulesData)) {
                            for (const d of rulesData) {
                                const rule = SemanticTokenRule.fromJSONObject(tokenClassificationRegistry, d);
                                if (rule) {
                                    theme.semanticTokenRules.push(rule);
                                }
                            }
                        }
                        break;
                    }
                case "location":
                    break;
                case "extensionData":
                    theme.extensionData = ExtensionData.fromJSONObject(data.extensionData);
                    break;
                }
            }
            if (!theme.id || !theme.settingsId) {
                return undefined;
            }
            return theme;
        } catch (e) {
            return undefined;
        }
    }
    static fromExtensionTheme(theme, colorThemeLocation, extensionData) {
        const baseTheme = theme["uiTheme"] || "vs-dark";
        const themeSelector = toCSSSelector(extensionData.extensionId, theme.path);
        const id = `${baseTheme} ${themeSelector}`;
        const label = theme.label || basename(theme.path);
        const settingsId = theme.id || label;
        const themeData = ( new ColorThemeData(id, label, settingsId));
        themeData.description = theme.description;
        themeData.watch = theme._watch === true;
        themeData.location = colorThemeLocation;
        themeData.extensionData = extensionData;
        themeData.isLoaded = false;
        return themeData;
    }
}
function toCSSSelector(extensionId, path) {
    if (path.startsWith("./")) {
        path = path.substr(2);
    }
    let str = `${extensionId}-${path}`;
    str = str.replace(/[^_a-zA-Z0-9-]/g, "-");
    if (str.charAt(0).match(/[0-9-]/)) {
        str = "_" + str;
    }
    return str;
}
async function _loadColorTheme(extensionResourceLoaderService, themeLocation, result) {
    if (extname(themeLocation) === ".json") {
        const content = await extensionResourceLoaderService.readExtensionResource(themeLocation);
        const errors = [];
        const contentValue = parse(content, errors);
        if (errors.length > 0) {
            return Promise.reject(( new Error(( localize(
                16364,
                "Problems parsing JSON theme file: {0}",
                ( errors.map(e => getParseErrorMessage(e.error))).join(", ")
            )))));
        } else if (getNodeType(contentValue) !== "object") {
            return Promise.reject(( new Error(( localize(16365, "Invalid format for JSON theme file: Object expected.")))));
        }
        if (contentValue.include) {
            await _loadColorTheme(
                extensionResourceLoaderService,
                joinPath(dirname(themeLocation), contentValue.include),
                result
            );
        }
        if (Array.isArray(contentValue.settings)) {
            convertSettings(contentValue.settings, result);
            return null;
        }
        result.semanticHighlighting = result.semanticHighlighting || contentValue.semanticHighlighting;
        const colors = contentValue.colors;
        if (colors) {
            if (typeof colors !== "object") {
                return Promise.reject(( new Error(( localize(
                    16366,
                    "Problem parsing color theme file: {0}. Property 'colors' is not of type 'object'.",
                    (themeLocation.toString())
                )))));
            }
            for (const colorId in colors) {
                const colorVal = colors[colorId];
                if (colorVal === DEFAULT_COLOR_CONFIG_VALUE) {
                    delete result.colors[colorId];
                } else if (typeof colorVal === "string") {
                    result.colors[colorId] = ( Color.fromHex(colors[colorId]));
                }
            }
        }
        const tokenColors = contentValue.tokenColors;
        if (tokenColors) {
            if (Array.isArray(tokenColors)) {
                result.textMateRules.push(...tokenColors);
            } else if (typeof tokenColors === "string") {
                await _loadSyntaxTokens(
                    extensionResourceLoaderService,
                    joinPath(dirname(themeLocation), tokenColors),
                    result
                );
            } else {
                return Promise.reject(( new Error(( localize(
                    16367,
                    "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file",
                    (themeLocation.toString())
                )))));
            }
        }
        const semanticTokenColors = contentValue.semanticTokenColors;
        if (semanticTokenColors && typeof semanticTokenColors === "object") {
            for (const key in semanticTokenColors) {
                try {
                    const rule = readSemanticTokenRule(key, semanticTokenColors[key]);
                    if (rule) {
                        result.semanticTokenRules.push(rule);
                    }
                } catch (e) {
                    return Promise.reject(( new Error(( localize(
                        16368,
                        "Problem parsing color theme file: {0}. Property 'semanticTokenColors' contains a invalid selector",
                        (themeLocation.toString())
                    )))));
                }
            }
        }
    } else {
        return _loadSyntaxTokens(extensionResourceLoaderService, themeLocation, result);
    }
}
function _loadSyntaxTokens(extensionResourceLoaderService, themeLocation, result) {
    return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => {
        try {
            const contentValue = parse$1(content);
            const settings = contentValue.settings;
            if (!Array.isArray(settings)) {
                return Promise.reject(( new Error(( localize(16369, "Problem parsing tmTheme file: {0}. 'settings' is not array.")))));
            }
            convertSettings(settings, result);
            return Promise.resolve(null);
        } catch (e) {
            return Promise.reject(( new Error(( localize(16370, "Problems parsing tmTheme file: {0}", e.message)))));
        }
    }, error => {
        return Promise.reject(( new Error(( localize(
            16371,
            "Problems loading tmTheme file {0}: {1}",
            (themeLocation.toString()),
            error.message
        )))));
    });
}
const defaultThemeColors = {
    "light": [{
        scope: "token.info-token",
        settings: {
            foreground: "#316bcd"
        }
    }, {
        scope: "token.warn-token",
        settings: {
            foreground: "#cd9731"
        }
    }, {
        scope: "token.error-token",
        settings: {
            foreground: "#cd3131"
        }
    }, {
        scope: "token.debug-token",
        settings: {
            foreground: "#800080"
        }
    }],
    "dark": [{
        scope: "token.info-token",
        settings: {
            foreground: "#6796e6"
        }
    }, {
        scope: "token.warn-token",
        settings: {
            foreground: "#cd9731"
        }
    }, {
        scope: "token.error-token",
        settings: {
            foreground: "#f44747"
        }
    }, {
        scope: "token.debug-token",
        settings: {
            foreground: "#b267e6"
        }
    }],
    "hcLight": [{
        scope: "token.info-token",
        settings: {
            foreground: "#316bcd"
        }
    }, {
        scope: "token.warn-token",
        settings: {
            foreground: "#cd9731"
        }
    }, {
        scope: "token.error-token",
        settings: {
            foreground: "#cd3131"
        }
    }, {
        scope: "token.debug-token",
        settings: {
            foreground: "#800080"
        }
    }],
    "hcDark": [{
        scope: "token.info-token",
        settings: {
            foreground: "#6796e6"
        }
    }, {
        scope: "token.warn-token",
        settings: {
            foreground: "#008000"
        }
    }, {
        scope: "token.error-token",
        settings: {
            foreground: "#FF0000"
        }
    }, {
        scope: "token.debug-token",
        settings: {
            foreground: "#b267e6"
        }
    }]
};
const noMatch = _scope => -1;
function nameMatcher(identifiers, scopes) {
    if (scopes.length < identifiers.length) {
        return -1;
    }
    let score = undefined;
    const every = identifiers.every(identifier => {
        for (let i = scopes.length - 1; i >= 0; i--) {
            if (scopesAreMatching(scopes[i], identifier)) {
                score = (i + 1) * 0x10000 + identifier.length;
                return true;
            }
        }
        return false;
    });
    return every && score !== undefined ? score : -1;
}
function scopesAreMatching(thisScopeName, scopeName) {
    if (!thisScopeName) {
        return false;
    }
    if (thisScopeName === scopeName) {
        return true;
    }
    const len = scopeName.length;
    return thisScopeName.length > len && thisScopeName.substr(0, len) === scopeName && thisScopeName[len] === ".";
}
function getScopeMatcher(rule) {
    const ruleScope = rule.scope;
    if (!ruleScope || !rule.settings) {
        return noMatch;
    }
    const matchers = [];
    if (Array.isArray(ruleScope)) {
        for (const rs of ruleScope) {
            createMatchers(rs, nameMatcher, matchers);
        }
    } else {
        createMatchers(ruleScope, nameMatcher, matchers);
    }
    if (matchers.length === 0) {
        return noMatch;
    }
    return scope => {
        let max = matchers[0].matcher(scope);
        for (let i = 1; i < matchers.length; i++) {
            max = Math.max(max, matchers[i].matcher(scope));
        }
        return max;
    };
}
function readSemanticTokenRule(selectorString, settings) {
    const selector = tokenClassificationRegistry.parseTokenSelector(selectorString);
    let style;
    if (typeof settings === "string") {
        style = TokenStyle.fromSettings(settings, undefined);
    } else if (isSemanticTokenColorizationSetting(settings)) {
        style = TokenStyle.fromSettings(
            settings.foreground,
            settings.fontStyle,
            settings.bold,
            settings.underline,
            settings.strikethrough,
            settings.italic
        );
    }
    if (style) {
        return {
            selector,
            style
        };
    }
    return undefined;
}
function isSemanticTokenColorizationSetting(style) {
    return style && (isString(style.foreground) || isString(style.fontStyle) || isBoolean(style.italic) || isBoolean(style.underline) || isBoolean(style.strikethrough) || isBoolean(style.bold));
}
function findMetadata(colorThemeData, captureNames, languageId, bracket) {
    let metadata = 0;
    metadata |= (languageId << MetadataConsts.LANGUAGEID_OFFSET);
    const definitions = {};
    const tokenStyle = colorThemeData.resolveScopes([captureNames], definitions);
    if (captureNames.length > 0) {
        const standardToken = toStandardTokenType(captureNames[captureNames.length - 1]);
        metadata |= (standardToken << MetadataConsts.TOKEN_TYPE_OFFSET);
    }
    const fontStyle = definitions.foreground?.settings.fontStyle || definitions.bold?.settings.fontStyle;
    if (fontStyle?.includes("italic")) {
        metadata |= FontStyle.Italic | MetadataConsts.ITALIC_MASK;
    }
    if (fontStyle?.includes("bold")) {
        metadata |= FontStyle.Bold | MetadataConsts.BOLD_MASK;
    }
    if (fontStyle?.includes("underline")) {
        metadata |= FontStyle.Underline | MetadataConsts.UNDERLINE_MASK;
    }
    if (fontStyle?.includes("strikethrough")) {
        metadata |= FontStyle.Strikethrough | MetadataConsts.STRIKETHROUGH_MASK;
    }
    const foreground = tokenStyle?.foreground;
    const tokenStyleForeground = (foreground !== undefined) ? colorThemeData.getTokenColorIndex().get(foreground) : ColorId.DefaultForeground;
    metadata |= tokenStyleForeground << MetadataConsts.FOREGROUND_OFFSET;
    if (bracket) {
        metadata |= MetadataConsts.BALANCED_BRACKETS_MASK;
    }
    return metadata;
}
class TokenColorIndex {
    constructor() {
        this._lastColorId = 0;
        this._id2color = [];
        this._color2id = Object.create(null);
    }
    add(color) {
        color = normalizeColor(color);
        if (color === undefined) {
            return 0;
        }
        let value = this._color2id[color];
        if (value) {
            return value;
        }
        value = ++this._lastColorId;
        this._color2id[color] = value;
        this._id2color[value] = color;
        return value;
    }
    get(color) {
        color = normalizeColor(color);
        if (color === undefined) {
            return 0;
        }
        const value = this._color2id[color];
        if (value) {
            return value;
        }
        console.log(`Color ${color} not in index.`);
        return 0;
    }
    asArray() {
        return this._id2color.slice(0);
    }
}
class TokenFontIndex {
    constructor() {
        this._lastFontId = 0;
        this._id2font = [];
        this._font2id = ( new Map());
    }
    add(fontFamily, fontSizeMultiplier, lineHeightMultiplier) {
        const font = {
            fontFamily,
            fontSizeMultiplier,
            lineHeightMultiplier
        };
        let value = this._font2id.get(font);
        if (value) {
            return value;
        }
        value = ++this._lastFontId;
        this._font2id.set(font, value);
        this._id2font[value] = font;
        return value;
    }
    get(font) {
        const value = this._font2id.get(font);
        if (value) {
            return value;
        }
        return 0;
    }
    asArray() {
        return this._id2font.slice(0);
    }
}
function normalizeColor(color) {
    if (!color) {
        return undefined;
    }
    if (typeof color !== "string") {
        color = Color.Format.CSS.formatHexA(color, true);
    }
    const len = color.length;
    if (color.charCodeAt(0) !== CharCode.Hash || (len !== 4 && len !== 5 && len !== 7 && len !== 9)) {
        return undefined;
    }
    const result = [CharCode.Hash];
    for (let i = 1; i < len; i++) {
        const upper = hexUpper(color.charCodeAt(i));
        if (!upper) {
            return undefined;
        }
        result.push(upper);
        if (len === 4 || len === 5) {
            result.push(upper);
        }
    }
    if (result.length === 9 && result[7] === CharCode.F && result[8] === CharCode.F) {
        result.length = 7;
    }
    return String.fromCharCode(...result);
}
function hexUpper(charCode) {
    if (charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9 || charCode >= CharCode.A && charCode <= CharCode.F) {
        return charCode;
    } else if (charCode >= CharCode.a && charCode <= CharCode.f) {
        return charCode - CharCode.a + CharCode.A;
    }
    return 0;
}

export { ColorThemeData, findMetadata };
