var trace = require("trace");
var cssSelector = require("ui/styling/css-selector");
var cssParser = require("css");
var application = require("application");
var types;
function ensureTypes() {
    if (!types) {
        types = require("utils/types");
    }
}
var utils;
function ensureUtils() {
    if (!utils) {
        utils = require("utils/utils");
    }
}
var fs;
function ensureFS() {
    if (!fs) {
        fs = require("file-system");
    }
}
var vs;
function ensureVisualState() {
    if (!vs) {
        vs = require("./visual-state");
    }
}
var pattern = /('|")(.*?)\1/;
var StyleScope = (function () {
    function StyleScope() {
        this._statesByKey = {};
        this._viewIdToKey = {};
    }
    Object.defineProperty(StyleScope.prototype, "css", {
        get: function () {
            return this._css;
        },
        set: function (value) {
            this._css = value;
            this._cssFileName = undefined;
            this._cssSelectors = undefined;
            this._reset();
        },
        enumerable: true,
        configurable: true
    });
    StyleScope.prototype.addCss = function (cssString, cssFileName) {
        this._css = this._css ? this._css + cssString : cssString;
        this._cssFileName = cssFileName;
        this._reset();
        if (!this._cssSelectors) {
            if (application.cssSelectorsCache) {
                this._cssSelectors = StyleScope._joinCssSelectorsArrays([application.cssSelectorsCache]);
            }
            else {
                this._cssSelectors = new Array();
            }
        }
        var selectorsFromFile = StyleScope.createSelectorsFromCss(cssString, cssFileName);
        this._cssSelectors = StyleScope._joinCssSelectorsArrays([this._cssSelectors, selectorsFromFile]);
    };
    StyleScope.createSelectorsFromCss = function (css, cssFileName) {
        try {
            var pageCssSyntaxTree = css ? cssParser.parse(css, { source: cssFileName }) : null;
            var pageCssSelectors = new Array();
            if (pageCssSyntaxTree) {
                pageCssSelectors = StyleScope._joinCssSelectorsArrays([pageCssSelectors, StyleScope.createSelectorsFromImports(pageCssSyntaxTree)]);
                pageCssSelectors = StyleScope._joinCssSelectorsArrays([pageCssSelectors, StyleScope.createSelectorsFromSyntaxTree(pageCssSyntaxTree)]);
            }
            return pageCssSelectors;
        }
        catch (e) {
            trace.write("Css styling failed: " + e, trace.categories.Error, trace.messageType.error);
        }
    };
    StyleScope.createSelectorsFromImports = function (tree) {
        var selectors = new Array();
        ensureTypes();
        if (!types.isNullOrUndefined(tree)) {
            var imports = tree["stylesheet"]["rules"].filter(function (r) { return r.type === "import"; });
            for (var i = 0; i < imports.length; i++) {
                var importItem = imports[i]["import"];
                var match = importItem && importItem.match(pattern);
                var url = match && match[2];
                if (!types.isNullOrUndefined(url)) {
                    ensureUtils();
                    if (utils.isFileOrResourcePath(url)) {
                        ensureFS();
                        var fileName = types.isString(url) ? url.trim() : "";
                        if (fileName.indexOf("~/") === 0) {
                            fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
                        }
                        if (fs.File.exists(fileName)) {
                            var file = fs.File.fromPath(fileName);
                            var text = file.readTextSync();
                            if (text) {
                                selectors = StyleScope._joinCssSelectorsArrays([selectors, StyleScope.createSelectorsFromCss(text, fileName)]);
                            }
                        }
                    }
                }
            }
        }
        return selectors;
    };
    StyleScope.prototype.ensureSelectors = function () {
        if (!this._cssSelectors && (this._css || application.cssSelectorsCache)) {
            var applicationCssSelectors = application.cssSelectorsCache ? application.cssSelectorsCache : null;
            var pageCssSelectors = StyleScope.createSelectorsFromCss(this._css, this._cssFileName);
            this._cssSelectors = StyleScope._joinCssSelectorsArrays([applicationCssSelectors, pageCssSelectors]);
        }
    };
    StyleScope._joinCssSelectorsArrays = function (arrays) {
        var mergedResult = [];
        var i;
        for (i = 0; i < arrays.length; i++) {
            if (arrays[i]) {
                mergedResult.push.apply(mergedResult, arrays[i]);
            }
        }
        mergedResult.sort(function (a, b) { return a.specificity - b.specificity; });
        return mergedResult;
    };
    StyleScope.prototype.applySelectors = function (view) {
        if (!this._cssSelectors) {
            return;
        }
        view.style._beginUpdate();
        var i, selector, matchedStateSelectors = new Array();
        for (i = 0; i < this._cssSelectors.length; i++) {
            selector = this._cssSelectors[i];
            if (selector.matches(view)) {
                if (selector instanceof cssSelector.CssVisualStateSelector) {
                    matchedStateSelectors.push(selector);
                }
                else {
                    selector.apply(view);
                }
            }
        }
        if (matchedStateSelectors.length > 0) {
            var key = "";
            matchedStateSelectors.forEach(function (s) { return key += s.key + "|"; });
            this._viewIdToKey[view._domId] = key;
            if (!this._statesByKey[key]) {
                this._createVisualsStatesForSelectors(key, matchedStateSelectors);
            }
        }
        view.style._endUpdate();
    };
    StyleScope.prototype.getVisualStates = function (view) {
        var key = this._viewIdToKey[view._domId];
        if (key === undefined) {
            return undefined;
        }
        return this._statesByKey[key];
    };
    StyleScope.prototype._createVisualsStatesForSelectors = function (key, matchedStateSelectors) {
        var i, allStates = {}, stateSelector;
        this._statesByKey[key] = allStates;
        ensureVisualState();
        for (i = 0; i < matchedStateSelectors.length; i++) {
            stateSelector = matchedStateSelectors[i];
            var visualState = allStates[stateSelector.state];
            if (!visualState) {
                visualState = new vs.VisualState();
                allStates[stateSelector.state] = visualState;
            }
            stateSelector.eachSetter(function (property, value) {
                visualState.setters[property.name] = value;
            });
        }
    };
    StyleScope.createSelectorsFromSyntaxTree = function (ast) {
        var result = [];
        var rules = ast.stylesheet.rules;
        var rule;
        var i;
        var j;
        for (i = 0; i < rules.length; i++) {
            rule = rules[i];
            if (rule.type === "rule") {
                var filteredDeclarations = [];
                if (rule.declarations) {
                    for (j = 0; j < rule.declarations.length; j++) {
                        var declaration = rule.declarations[j];
                        if (declaration.type === "declaration") {
                            filteredDeclarations.push({
                                property: declaration.property.toLowerCase(),
                                value: declaration.value
                            });
                        }
                    }
                }
                for (j = 0; j < rule.selectors.length; j++) {
                    result.push(cssSelector.createSelector(rule.selectors[j], filteredDeclarations));
                }
            }
        }
        return result;
    };
    StyleScope.prototype._reset = function () {
        this._statesByKey = {};
        this._viewIdToKey = {};
    };
    return StyleScope;
}());
exports.StyleScope = StyleScope;
function applyInlineSyle(view, style) {
    try {
        var syntaxTree = cssParser.parse("local { " + style + " }", undefined);
        var filteredDeclarations = syntaxTree.stylesheet.rules[0].declarations.filter(function (val, i, arr) { return val.type === "declaration"; });
        cssSelector.applyInlineSyle(view, filteredDeclarations);
    }
    catch (ex) {
        trace.write("Applying local style failed: " + ex, trace.categories.Error, trace.messageType.error);
    }
}
exports.applyInlineSyle = applyInlineSyle;
