"use strict";

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BOOTSTRAP_SCSS_PATH = "bootstrap/scss/";
var THEMEBUILDER_LESS_PATH = "devextreme-themebuilder/data/less/";

var SWATCH_SELECTOR_PREFIX = ".dx-swatch-";

var createModifyVars = function createModifyVars(modifyVars) {
    var result = "";
    for (var key in modifyVars) {
        if (modifyVars.hasOwnProperty(key)) {
            result += "@" + key + ": " + modifyVars[key] + ";";
        }
    }
    return result;
};

var addSwatchClass = function addSwatchClass(less, swatchSelector, modifyVars) {
    if (!swatchSelector) return less;
    return swatchSelector + "{" + less + createModifyVars(modifyVars) + "}";
};

var LessFontPlugin = function () {
    function LessFontPlugin() {
        _classCallCheck(this, LessFontPlugin);
    }

    _createClass(LessFontPlugin, [{
        key: "process",
        value: function process(css) {
            return css.replace(/(\f)(\d+)/g, "\\f$2");
        }
    }]);

    return LessFontPlugin;
}();

var LessMetadataPreCompilerPlugin = function () {
    function LessMetadataPreCompilerPlugin(metadata, swatchSelector, modifyVars) {
        _classCallCheck(this, LessMetadataPreCompilerPlugin);

        this._metadata = metadata;
        this.swatchSelector = swatchSelector;
        this.modifyVars = modifyVars;
    }

    _createClass(LessMetadataPreCompilerPlugin, [{
        key: "process",
        value: function process(less) {
            less += "#devexpress-metadata-compiler{";
            for (var key in this._metadata) {
                if (this._metadata.hasOwnProperty(key)) {
                    var value = this._metadata[key];
                    less += key + ": " + value + ";";
                }
            }
            less += "}";
            return addSwatchClass(less, this.swatchSelector, this.modifyVars);
        }
    }]);

    return LessMetadataPreCompilerPlugin;
}();

var LessMetadataPostCompilerPlugin = function () {
    function LessMetadataPostCompilerPlugin(compiledMetadata, swatchSelector, colorScheme) {
        _classCallCheck(this, LessMetadataPostCompilerPlugin);

        this._metadata = compiledMetadata;
        this.swatchSelector = swatchSelector;
        this.colorScheme = colorScheme;
    }

    _createClass(LessMetadataPostCompilerPlugin, [{
        key: "process",
        value: function process(css) {
            var _this = this;

            var metadataRegex = new RegExp("(?:" + this.swatchSelector + "\\s*)?\\s*#devexpress-metadata-compiler\\s*\\{((.|\\n|\\r)*?)\\}");
            metadataRegex.exec(css)[1].split(";").forEach(function (item) {
                var rule = getCompiledRule(item);
                for (var key in rule) {
                    if (rule.hasOwnProperty(key)) {
                        _this._metadata[key] = rule[key];
                    }
                }
            });

            if (this.swatchSelector) {
                var escapedSelector = this.swatchSelector.replace(".", "\\.");

                var customStylesDuplicateRegex = new RegExp("\\s+" + escapedSelector + "\\s+\\.dx-theme-.*?-typography\\s+\\.dx-theme-.*?{[\\s\\S]*?}[\\r\\n]*?", "g");
                var swatchOrderRegex = new RegExp("([ \\t]*)([\\w\\.#:\\*][\\w\\.#:\\*\\->()\\s]*)(" + escapedSelector + "\\s)([^,{+~]*)", "gm");
                var themeMarkerRegex = /(\.dx-theme-marker\s*{\s*font-family:\s*['"]dx\..*?\.)(.*)(['"])/g;

                css = css.replace(swatchOrderRegex, "$1$3$2$4").replace(customStylesDuplicateRegex, "").replace(/\s\.dx-theme-.*?-typography/g, "").replace(themeMarkerRegex, "$1" + this.colorScheme + "$3");
            }

            return css.replace(metadataRegex, "");
        }
    }]);

    return LessMetadataPostCompilerPlugin;
}();

var getCompiledRule = function getCompiledRule(cssString) {
    var result = {};
    var ruleRegex = /([-\w\d]*)\s*:\s*(.*)\s*/;
    var matches = ruleRegex.exec(cssString);
    if (matches) {
        result["@" + matches[1]] = matches[2];
    } else {
        result = null;
    }
    return result;
};

var LessTemplateLoader = function () {
    function LessTemplateLoader(config, version) {
        _classCallCheck(this, LessTemplateLoader);

        this.readFile = config.reader;
        this.lessCompiler = config.lessCompiler;
        this.sassCompiler = config.sassCompiler;
        this.themeBuilderLessPath = config.lessPath ? config.lessPath : THEMEBUILDER_LESS_PATH;
        this.bootstrapScssPath = config.scssPath ? config.scssPath : BOOTSTRAP_SCSS_PATH;
        this.swatchSelector = config.makeSwatch ? SWATCH_SELECTOR_PREFIX + config.outColorScheme : "";
        this.outColorScheme = config.outColorScheme;
        this.version = version;
    }

    _createClass(LessTemplateLoader, [{
        key: "load",
        value: function load(theme, colorScheme, metadata, modifiedItems) {
            var _this2 = this;

            return this._loadLess(theme, colorScheme).then(function (less) {
                var modifyVars = {};
                var metadataVariables = {};

                if (Array.isArray(modifiedItems)) {
                    modifiedItems.forEach(function (item) {
                        modifyVars[item.key.replace("@", "")] = item.value;
                    });
                }

                metadata.forEach(function (metaItem) {
                    metadataVariables[metaItem.Key.replace("@", "")] = metaItem.Key;
                });

                return _this2.compileLess(less, modifyVars, metadataVariables);
            });
        }
    }, {
        key: "compileLess",
        value: function compileLess(less, modifyVars, metadata) {
            var _this3 = this;

            return new Promise(function (resolve, reject) {
                var compiledMetadata = {};
                var options = {};

                // while using `less/lib/less-browser`, the global options are not passed to the `render` method, lets do it by ourselves
                if (_this3.lessCompiler.options && _typeof(_this3.lessCompiler.options) === "object") {
                    Object.assign(options, _this3.lessCompiler.options);
                }

                var customOptions = {
                    modifyVars: modifyVars,
                    plugins: [{
                        install: function install(_, pluginManager) {
                            pluginManager.addPostProcessor(new LessFontPlugin(_this3.options));
                        }
                    }, {
                        install: function install(_, pluginManager) {
                            pluginManager.addPreProcessor(new LessMetadataPreCompilerPlugin(metadata, _this3.swatchSelector, modifyVars));
                        }
                    }, {
                        install: function install(_, pluginManager) {
                            pluginManager.addPostProcessor(new LessMetadataPostCompilerPlugin(compiledMetadata, _this3.swatchSelector, _this3.outColorScheme));
                        }
                    }]
                };

                Object.assign(options, customOptions);

                _this3.lessCompiler.render(less, options).then(function (output) {
                    resolve({
                        compiledMetadata: compiledMetadata,
                        css: _this3._makeInfoHeader() + output.css,
                        swatchSelector: _this3.swatchSelector
                    });
                }, function (error) {
                    reject(error);
                });
            });
        }
    }, {
        key: "compileScss",
        value: function compileScss(less, metadata) {
            var _this4 = this;

            return new Promise(function (resolve, reject) {
                var compiledMetadata = {};

                var preCompiler = new LessMetadataPreCompilerPlugin(metadata, _this4.swatchSelector);
                var sassContent = preCompiler.process(less);

                _this4.sassCompiler.render(sassContent).then(function (css) {
                    var postCompiler = new LessMetadataPostCompilerPlugin(compiledMetadata, _this4.swatchSelector);
                    postCompiler.process(css);
                    resolve({
                        compiledMetadata: compiledMetadata,
                        css: css
                    });
                }, function (error) {
                    reject(error);
                });
            });
        }
    }, {
        key: "analyzeBootstrapTheme",
        value: function analyzeBootstrapTheme(theme, colorScheme, metadata, bootstrapMetadata, customLessContent, version) {
            var _this5 = this;

            var metadataVariables = "";
            for (var key in bootstrapMetadata) {
                if (bootstrapMetadata.hasOwnProperty(key)) {
                    metadataVariables += bootstrapMetadata[key] + ": dx-empty" + (version === 4 ? " !default" : "") + ";";
                }
            }

            return new Promise(function (resolve) {
                var processDxTheme = function processDxTheme(data) {
                    var compiledMetadata = data.compiledMetadata;
                    var modifyVars = {};
                    for (var _key in compiledMetadata) {
                        if (compiledMetadata.hasOwnProperty(_key)) {
                            var value = compiledMetadata[_key];
                            if (value !== "dx-empty") {
                                modifyVars[_key] = value;
                            }
                        }
                    }

                    _this5._loadLess(theme, colorScheme).then(function (less) {
                        var metadataVariables = {};

                        metadata.forEach(function (metaItem) {
                            metadataVariables[metaItem.Key.replace("@", "")] = metaItem.Key;
                        });

                        _this5.compileLess(less, modifyVars, metadataVariables).then(function (data) {
                            resolve({
                                compiledMetadata: data.compiledMetadata,
                                modifyVars: modifyVars,
                                css: data.css
                            });
                        });
                    });
                };

                if (version === 3) {
                    _this5.compileLess(metadataVariables + customLessContent, {}, bootstrapMetadata).then(processDxTheme);
                } else if (version === 4) {
                    var defaultBootstrapVariablesUrl = _this5.bootstrapScssPath + "_variables.scss",
                        defaultBootstrapFunctionsUrl = _this5.bootstrapScssPath + "_functions.scss";

                    Promise.all([_this5.readFile(defaultBootstrapFunctionsUrl), _this5.readFile(defaultBootstrapVariablesUrl)]).then(function (files) {
                        _this5.compileScss(files[0] + customLessContent + files[1] + metadataVariables, bootstrapMetadata).then(processDxTheme);
                    }, function () {
                        _this5.compileScss(customLessContent + metadataVariables, bootstrapMetadata).then(processDxTheme);
                    });
                }
            });
        }
    }, {
        key: "_loadLess",
        value: function _loadLess(theme, colorScheme) {
            var themeName = theme ? theme + "-" : "";
            return this._loadLessByFileName("theme-builder-" + themeName + colorScheme + ".less");
        }
    }, {
        key: "_loadLessByFileName",
        value: function _loadLessByFileName(fileName) {
            return this.readFile(this.themeBuilderLessPath + fileName);
        }
    }, {
        key: "_makeInfoHeader",
        value: function _makeInfoHeader() {
            var generatedBy = "* Generated by the DevExpress ThemeBuilder";
            var versionString = "* Version: " + this.version;
            var link = "* http://js.devexpress.com/ThemeBuilder/";

            return ["/*", generatedBy, versionString, link, "*/"].join("\n") + "\n\n";
        }
    }]);

    return LessTemplateLoader;
}();

module.exports = LessTemplateLoader;