"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.evalDeclarationValue = exports.processDeclarationValue = exports.resolveArgumentsValue = exports.functionDiagnostics = exports.StylableEvaluator = void 0;
const path_1 = require("path");
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
const diagnostics_1 = require("./diagnostics");
const native_reserved_lists_1 = require("./native-reserved-lists");
const stylable_assets_1 = require("./stylable-assets");
const stylable_resolver_1 = require("./stylable-resolver");
const value_1 = require("./helpers/value");
const escape_1 = require("./helpers/escape");
const features_1 = require("./features");
const custom_values_1 = require("./custom-values");
class StylableEvaluator {
    constructor(options) {
        this.valueHook = options.valueHook;
        this.stVarOverride = options.stVarOverride;
        this.getResolvedSymbols = options.getResolvedSymbols;
    }
    evaluateValue(context, data) {
        return processDeclarationValue(context.resolver, this.getResolvedSymbols, data.value, data.meta, data.node, data.stVarOverride || this.stVarOverride, this.valueHook, context.diagnostics, context.passedThrough, data.args, data.rootArgument, data.initialNode);
    }
}
exports.StylableEvaluator = StylableEvaluator;
// old API
exports.functionDiagnostics = {
    FAIL_TO_EXECUTE_FORMATTER: (0, diagnostics_1.createDiagnosticReporter)('15001', 'error', (resolvedValue, message) => `failed to execute formatter "${resolvedValue}" with error: "${message}"`),
    UNKNOWN_FORMATTER: (0, diagnostics_1.createDiagnosticReporter)('15002', 'error', (name) => `cannot find native function or custom formatter called ${name}`),
};
function resolveArgumentsValue(options, transformer, meta, diagnostics, node, variableOverride, path) {
    const resolvedArgs = {};
    for (const k in options) {
        resolvedArgs[k] = evalDeclarationValue(transformer.resolver, (0, escape_1.unescapeCSS)(options[k]), meta, node, variableOverride, transformer.replaceValueHook, diagnostics, path, undefined);
    }
    return resolvedArgs;
}
exports.resolveArgumentsValue = resolveArgumentsValue;
function processDeclarationValue(resolver, getResolvedSymbols, value, meta, node, variableOverride, valueHook, diagnostics = new diagnostics_1.Diagnostics(), passedThrough = [], args = [], rootArgument, initialNode) {
    const evaluator = new StylableEvaluator({
        stVarOverride: variableOverride,
        valueHook,
        getResolvedSymbols,
    });
    const resolvedSymbols = getResolvedSymbols(meta);
    const parsedValue = (0, postcss_value_parser_1.default)(value);
    parsedValue.walk((parsedNode) => {
        const { type, value } = parsedNode;
        if (type === `function`) {
            if (value === 'value') {
                features_1.STVar.hooks.transformValue({
                    context: {
                        meta,
                        diagnostics,
                        resolver,
                        evaluator,
                        getResolvedSymbols,
                        passedThrough,
                    },
                    data: {
                        value,
                        node,
                        meta,
                        stVarOverride: variableOverride,
                        args,
                        rootArgument,
                        initialNode,
                    },
                    node: parsedNode,
                });
            }
            else if (value === '') {
                parsedNode.resolvedValue = (0, value_1.stringifyFunction)(value, parsedNode);
            }
            else if (resolvedSymbols.customValues[value]) {
                // no op resolved at the bottom
            }
            else if (value === 'url') {
                // postcss-value-parser treats url differently:
                // https://github.com/TrySound/postcss-value-parser/issues/34
                const url = parsedNode.nodes[0];
                if ((url.type === 'word' || url.type === 'string') && url.value.startsWith('~')) {
                    const sourceDir = (0, path_1.dirname)(meta.source);
                    url.value = (0, stylable_assets_1.assureRelativeUrlPrefix)((0, path_1.relative)(sourceDir, resolver.resolvePath(sourceDir, url.value.slice(1))).replace(/\\/gm, '/'));
                }
            }
            else if (value === 'format') {
                // preserve native format function arg quotation
                parsedNode.resolvedValue = (0, value_1.stringifyFunction)(value, parsedNode, true);
            }
            else if (resolvedSymbols.js[value]) {
                const formatter = resolvedSymbols.js[value];
                const formatterArgs = (0, value_1.getFormatterArgs)(parsedNode);
                try {
                    // ToDo: check if function instead of calling on a non function
                    parsedNode.resolvedValue = formatter.symbol(...formatterArgs);
                    if (evaluator.valueHook && typeof parsedNode.resolvedValue === 'string') {
                        parsedNode.resolvedValue = evaluator.valueHook(parsedNode.resolvedValue, { name: parsedNode.value, args: formatterArgs }, true, passedThrough);
                    }
                }
                catch (error) {
                    parsedNode.resolvedValue = (0, value_1.stringifyFunction)(value, parsedNode);
                    if (diagnostics && node) {
                        diagnostics.report(exports.functionDiagnostics.FAIL_TO_EXECUTE_FORMATTER(parsedNode.resolvedValue, error?.message), {
                            node,
                            word: node.value,
                        });
                    }
                }
            }
            else if (value === 'var') {
                features_1.CSSCustomProperty.hooks.transformValue({
                    context: {
                        meta,
                        diagnostics,
                        resolver,
                        evaluator,
                        getResolvedSymbols,
                        passedThrough,
                    },
                    data: {
                        value,
                        node,
                        meta,
                        stVarOverride: variableOverride,
                        args,
                        rootArgument,
                        initialNode,
                    },
                    node: parsedNode,
                });
            }
            else if (native_reserved_lists_1.nativeFunctionsDic[value]) {
                const { preserveQuotes } = native_reserved_lists_1.nativeFunctionsDic[value];
                parsedNode.resolvedValue = (0, value_1.stringifyFunction)(value, parsedNode, preserveQuotes);
            }
            else if (node) {
                parsedNode.resolvedValue = (0, value_1.stringifyFunction)(value, parsedNode);
                diagnostics.report(exports.functionDiagnostics.UNKNOWN_FORMATTER(value), {
                    node,
                    word: value,
                });
            }
        }
    }, true);
    let outputValue = '';
    let topLevelType = null;
    let runtimeValue = null;
    let typeError = undefined;
    for (const n of parsedValue.nodes) {
        if (n.type === 'function') {
            const matchingType = resolvedSymbols.customValues[n.value];
            if (matchingType) {
                try {
                    topLevelType = matchingType.evalVarAst(n, resolvedSymbols.customValues, true);
                    runtimeValue = (0, custom_values_1.unbox)(topLevelType, true, resolvedSymbols.customValues, n);
                    try {
                        outputValue += matchingType.getValue(args, topLevelType, n, resolvedSymbols.customValues);
                    }
                    catch (error) {
                        if (error instanceof custom_values_1.CustomValueError) {
                            outputValue += error.fallbackValue;
                        }
                        else {
                            throw error;
                        }
                    }
                }
                catch (e) {
                    typeError = e;
                    const invalidNode = initialNode || node;
                    if (invalidNode) {
                        diagnostics.report(features_1.STVar.diagnostics.COULD_NOT_RESOLVE_VALUE([...(rootArgument ? [rootArgument] : []), ...args].join(', ')), {
                            node: invalidNode,
                            word: value,
                        });
                    }
                    else {
                        // TODO: catch broken variable resolutions without a node
                    }
                }
            }
            else {
                outputValue += (0, value_1.getStringValue)([n]);
            }
        }
        else {
            outputValue += (0, value_1.getStringValue)([n]);
        }
    }
    return { outputValue, topLevelType, typeError, runtimeValue };
}
exports.processDeclarationValue = processDeclarationValue;
function evalDeclarationValue(resolver, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough = [], args = [], getResolvedSymbols = (0, stylable_resolver_1.createSymbolResolverWithCache)(resolver, diagnostics || new diagnostics_1.Diagnostics())) {
    return processDeclarationValue(resolver, getResolvedSymbols, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough, args).outputValue;
}
exports.evalDeclarationValue = evalDeclarationValue;
//# sourceMappingURL=functions.js.map