"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var Lint = require("tslint");
var utils_1 = require("./util/utils");
var classDeclarationUtils_1 = require("./util/classDeclarationUtils");
var ngWalker_1 = require("./angular/ngWalker");
var recursiveAngularExpressionVisitor_1 = require("./angular/templates/recursiveAngularExpressionVisitor");
var SyntaxKind = require("./util/syntaxKind");
var DeclarationType;
(function (DeclarationType) {
    DeclarationType[DeclarationType["Property"] = 0] = "Property";
    DeclarationType[DeclarationType["Method"] = 1] = "Method";
})(DeclarationType || (DeclarationType = {}));
var SymbolAccessValidator = (function (_super) {
    __extends(SymbolAccessValidator, _super);
    function SymbolAccessValidator() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    SymbolAccessValidator.prototype.visitPropertyRead = function (ast, context) {
        return this.doCheck(ast, DeclarationType.Property, context);
    };
    SymbolAccessValidator.prototype.visitMethodCall = function (ast, context) {
        this.doCheck(ast, DeclarationType.Method, context);
    };
    SymbolAccessValidator.prototype.visitPropertyWrite = function (ast, context) {
        this.doCheck(ast, DeclarationType.Property, context);
    };
    SymbolAccessValidator.prototype.doCheck = function (ast, type, context) {
        if (ast.receiver && (ast.receiver.name || ast.receiver.key)) {
            var receiver = ast.receiver;
            while (receiver.receiver.name) {
                receiver = receiver.receiver;
            }
            ast = receiver;
        }
        if (this.preDefinedVariables[ast.name]) {
            return;
        }
        var allMembers = classDeclarationUtils_1.getDeclaredMethods(this.context.controller).concat(classDeclarationUtils_1.getDeclaredProperties(this.context.controller));
        var member = allMembers.filter(function (m) { return m.name && m.name.text === ast.name; }).pop();
        if (member) {
            var isPublic = !member.modifiers || !member.modifiers
                .some(function (m) { return m.kind === SyntaxKind.current().PrivateKeyword || m.kind === SyntaxKind.current().ProtectedKeyword; });
            var width = ast.name.length;
            if (!isPublic) {
                var failureString = "You can bind only to public class members. \"" + member.name.getText() + "\" is not a public class member.";
                this.addFailure(this.createFailure(ast.span.start, width, failureString));
            }
        }
    };
    SymbolAccessValidator.prototype.getTopSuggestion = function (list, current) {
        var result = [];
        var tmp = list.map(function (e) {
            return {
                element: e,
                distance: utils_1.stringDistance(e, current)
            };
        }).sort(function (a, b) { return a.distance - b.distance; });
        var first = tmp.shift();
        if (!first) {
            return [];
        }
        else {
            result.push(first);
            var current_1;
            while (current_1 = tmp.shift()) {
                if (current_1.distance !== first.distance) {
                    return result;
                }
                else {
                    result.push(current_1);
                }
            }
            return result;
        }
    };
    return SymbolAccessValidator;
}(recursiveAngularExpressionVisitor_1.RecursiveAngularExpressionVisitor));
var Rule = (function (_super) {
    __extends(Rule, _super);
    function Rule() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Rule.prototype.apply = function (sourceFile) {
        return this.applyWithWalker(new ngWalker_1.NgWalker(sourceFile, this.getOptions(), {
            expressionVisitorCtrl: SymbolAccessValidator
        }));
    };
    Rule.metadata = {
        ruleName: 'templates-use-public',
        type: 'functionality',
        description: "Ensure that properties and methods accessed from the template are public.",
        rationale: "When Angular compiles the templates, it has to access these properties from outside the class.",
        options: null,
        optionsDescription: "Not configurable.",
        typescriptOnly: true,
    };
    return Rule;
}(Lint.Rules.AbstractRule));
exports.Rule = Rule;