define(["require", "exports", "tslib", "./Stylesheet", "./transforms/kebabRules", "./transforms/prefixRules", "./transforms/provideUnits", "./transforms/rtlifyRules", "./tokenizeWithParentheses"], function (require, exports, tslib_1, Stylesheet_1, kebabRules_1, prefixRules_1, provideUnits_1, rtlifyRules_1, tokenizeWithParentheses_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.styleToClassName = exports.applyRegistration = exports.styleToRegistration = exports.serializeRuleEntries = void 0;
    var DISPLAY_NAME = 'displayName';
    function getDisplayName(rules) {
        var rootStyle = rules && rules['&'];
        return rootStyle ? rootStyle.displayName : undefined;
    }
    var globalSelectorRegExp = /\:global\((.+?)\)/g;
    /**
     * Finds comma separated selectors in a :global() e.g. ":global(.class1, .class2, .class3)"
     * and wraps them each in their own global ":global(.class1), :global(.class2), :global(.class3)"
     *
     * @param selectorWithGlobals The selector to process
     * @returns The updated selector
     */
    function expandCommaSeparatedGlobals(selectorWithGlobals) {
        // We the selector does not have a :global() we can shortcut
        if (!globalSelectorRegExp.test(selectorWithGlobals)) {
            return selectorWithGlobals;
        }
        var replacementInfo = [];
        var findGlobal = /\:global\((.+?)\)/g;
        var match = null;
        // Create a result list for global selectors so we can replace them.
        while ((match = findGlobal.exec(selectorWithGlobals))) {
            // Only if the found selector is a comma separated list we'll process it.
            if (match[1].indexOf(',') > -1) {
                replacementInfo.push([
                    match.index,
                    match.index + match[0].length,
                    // Wrap each of the found selectors in :global()
                    match[1]
                        .split(',')
                        .map(function (v) { return ":global(" + v.trim() + ")"; })
                        .join(', '),
                ]);
            }
        }
        // Replace the found selectors with their wrapped variants in reverse order
        return replacementInfo
            .reverse()
            .reduce(function (selector, _a) {
            var matchIndex = _a[0], matchEndIndex = _a[1], replacement = _a[2];
            var prefix = selector.slice(0, matchIndex);
            var suffix = selector.slice(matchEndIndex);
            return prefix + replacement + suffix;
        }, selectorWithGlobals);
    }
    function expandSelector(newSelector, currentSelector) {
        if (newSelector.indexOf(':global(') >= 0) {
            return newSelector.replace(globalSelectorRegExp, '$1');
        }
        else if (newSelector.indexOf(':') === 0) {
            return currentSelector + newSelector;
        }
        else if (newSelector.indexOf('&') < 0) {
            return currentSelector + ' ' + newSelector;
        }
        return newSelector;
    }
    function extractSelector(currentSelector, rules, selector, value) {
        if (rules === void 0) { rules = { __order: [] }; }
        if (selector.indexOf('@') === 0) {
            selector = selector + '{' + currentSelector;
            extractRules([value], rules, selector);
        }
        else if (selector.indexOf(',') > -1) {
            expandCommaSeparatedGlobals(selector)
                .split(',')
                .map(function (s) { return s.trim(); })
                .forEach(function (separatedSelector) {
                return extractRules([value], rules, expandSelector(separatedSelector, currentSelector));
            });
        }
        else {
            extractRules([value], rules, expandSelector(selector, currentSelector));
        }
    }
    function extractRules(args, rules, currentSelector) {
        if (rules === void 0) { rules = { __order: [] }; }
        if (currentSelector === void 0) { currentSelector = '&'; }
        var stylesheet = Stylesheet_1.Stylesheet.getInstance();
        var currentRules = rules[currentSelector];
        if (!currentRules) {
            currentRules = {};
            rules[currentSelector] = currentRules;
            rules.__order.push(currentSelector);
        }
        for (var _i = 0, args_1 = args; _i < args_1.length; _i++) {
            var arg = args_1[_i];
            // If the arg is a string, we need to look up the class map and merge.
            if (typeof arg === 'string') {
                var expandedRules = stylesheet.argsFromClassName(arg);
                if (expandedRules) {
                    extractRules(expandedRules, rules, currentSelector);
                }
                // Else if the arg is an array, we need to recurse in.
            }
            else if (Array.isArray(arg)) {
                extractRules(arg, rules, currentSelector);
            }
            else {
                for (var prop in arg) {
                    if (arg.hasOwnProperty(prop)) {
                        var propValue = arg[prop];
                        if (prop === 'selectors') {
                            // every child is a selector.
                            var selectors = arg.selectors;
                            for (var newSelector in selectors) {
                                if (selectors.hasOwnProperty(newSelector)) {
                                    extractSelector(currentSelector, rules, newSelector, selectors[newSelector]);
                                }
                            }
                        }
                        else if (typeof propValue === 'object') {
                            // prop is a selector.
                            if (propValue !== null) {
                                extractSelector(currentSelector, rules, prop, propValue);
                            }
                        }
                        else {
                            if (propValue !== undefined) {
                                // Else, add the rule to the currentSelector.
                                if (prop === 'margin' || prop === 'padding') {
                                    expandQuads(currentRules, prop, propValue);
                                }
                                else {
                                    currentRules[prop] = propValue;
                                }
                            }
                        }
                    }
                }
            }
        }
        return rules;
    }
    function expandQuads(currentRules, name, value) {
        var parts = typeof value === 'string' ? tokenizeWithParentheses_1.tokenizeWithParentheses(value) : [value];
        if (parts.length === 0) {
            parts.push(value);
        }
        if (parts[parts.length - 1] === '!important') {
            // Remove !important from parts, and append it to each part individually
            parts = parts.slice(0, -1).map(function (p) { return p + ' !important'; });
        }
        currentRules[name + 'Top'] = parts[0];
        currentRules[name + 'Right'] = parts[1] || parts[0];
        currentRules[name + 'Bottom'] = parts[2] || parts[0];
        currentRules[name + 'Left'] = parts[3] || parts[1] || parts[0];
    }
    function getKeyForRules(options, rules) {
        var serialized = [options.rtl ? 'rtl' : 'ltr'];
        var hasProps = false;
        for (var _i = 0, _a = rules.__order; _i < _a.length; _i++) {
            var selector = _a[_i];
            serialized.push(selector);
            var rulesForSelector = rules[selector];
            for (var propName in rulesForSelector) {
                if (rulesForSelector.hasOwnProperty(propName) && rulesForSelector[propName] !== undefined) {
                    hasProps = true;
                    serialized.push(propName, rulesForSelector[propName]);
                }
            }
        }
        return hasProps ? serialized.join('') : undefined;
    }
    function repeatString(target, count) {
        if (count <= 0) {
            return '';
        }
        if (count === 1) {
            return target;
        }
        return target + repeatString(target, count - 1);
    }
    function serializeRuleEntries(options, ruleEntries) {
        if (!ruleEntries) {
            return '';
        }
        var allEntries = [];
        for (var entry in ruleEntries) {
            if (ruleEntries.hasOwnProperty(entry) && entry !== DISPLAY_NAME && ruleEntries[entry] !== undefined) {
                allEntries.push(entry, ruleEntries[entry]);
            }
        }
        // Apply transforms.
        for (var i = 0; i < allEntries.length; i += 2) {
            kebabRules_1.kebabRules(allEntries, i);
            provideUnits_1.provideUnits(allEntries, i);
            rtlifyRules_1.rtlifyRules(options, allEntries, i);
            prefixRules_1.prefixRules(allEntries, i);
        }
        // Apply punctuation.
        for (var i = 1; i < allEntries.length; i += 4) {
            allEntries.splice(i, 1, ':', allEntries[i], ';');
        }
        return allEntries.join('');
    }
    exports.serializeRuleEntries = serializeRuleEntries;
    function styleToRegistration(options) {
        var args = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            args[_i - 1] = arguments[_i];
        }
        var rules = extractRules(args);
        var key = getKeyForRules(options, rules);
        if (key) {
            var stylesheet = Stylesheet_1.Stylesheet.getInstance();
            var registration = {
                className: stylesheet.classNameFromKey(key),
                key: key,
                args: args,
            };
            if (!registration.className) {
                registration.className = stylesheet.getClassName(getDisplayName(rules));
                var rulesToInsert = [];
                for (var _a = 0, _b = rules.__order; _a < _b.length; _a++) {
                    var selector = _b[_a];
                    rulesToInsert.push(selector, serializeRuleEntries(options, rules[selector]));
                }
                registration.rulesToInsert = rulesToInsert;
            }
            return registration;
        }
        return undefined;
    }
    exports.styleToRegistration = styleToRegistration;
    /**
     * Insert style to stylesheet.
     * @param registration Style registration.
     * @param specificityMultiplier Number of times classname selector is repeated in the css rule.
     * This is to increase css specificity in case it's needed. Default to 1.
     */
    function applyRegistration(registration, specificityMultiplier) {
        if (specificityMultiplier === void 0) { specificityMultiplier = 1; }
        var stylesheet = Stylesheet_1.Stylesheet.getInstance();
        var className = registration.className, key = registration.key, args = registration.args, rulesToInsert = registration.rulesToInsert;
        if (rulesToInsert) {
            // rulesToInsert is an ordered array of selector/rule pairs.
            for (var i = 0; i < rulesToInsert.length; i += 2) {
                var rules = rulesToInsert[i + 1];
                if (rules) {
                    var selector = rulesToInsert[i];
                    selector = selector.replace(/&/g, repeatString("." + registration.className, specificityMultiplier));
                    // Insert. Note if a media query, we must close the query with a final bracket.
                    var processedRule = selector + "{" + rules + "}" + (selector.indexOf('@') === 0 ? '}' : '');
                    stylesheet.insertRule(processedRule);
                }
            }
            stylesheet.cacheClassName(className, key, args, rulesToInsert);
        }
    }
    exports.applyRegistration = applyRegistration;
    function styleToClassName(options) {
        var args = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            args[_i - 1] = arguments[_i];
        }
        var registration = styleToRegistration.apply(void 0, tslib_1.__spreadArray([options], args));
        if (registration) {
            applyRegistration(registration, options.specificityMultiplier);
            return registration.className;
        }
        return '';
    }
    exports.styleToClassName = styleToClassName;
});
//# sourceMappingURL=styleToClassName.js.map