text: "use strict";
  var __extends = (this && this.__extends) || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
      function __() { this.constructor = d; }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  };
  var Lint = require("../lint");
  var ts = require("typescript");
  var OPTION_VARIABLES_BEFORE_FUNCTIONS = "variables-before-functions";
  var OPTION_STATIC_BEFORE_INSTANCE = "static-before-instance";
  var OPTION_PUBLIC_BEFORE_PRIVATE = "public-before-private";
  var OPTION_ORDER = "order";
  var PRESET_ORDERS = {
      "fields-first": [
          "public-static-field",
          "protected-static-field",
          "private-static-field",
          "public-instance-field",
          "protected-instance-field",
          "private-instance-field",
          "constructor",
          "public-static-method",
          "protected-static-method",
          "private-static-method",
          "public-instance-method",
          "protected-instance-method",
          "private-instance-method",
      ],
      "statics-first": [
          "public-static-field",
          "public-static-method",
          "protected-static-field",
          "protected-static-method",
          "private-static-field",
          "private-static-method",
          "public-instance-field",
          "protected-instance-field",
          "private-instance-field",
          "constructor",
          "public-instance-method",
          "protected-instance-method",
          "private-instance-method",
      ],
      "instance-sandwich": [
          "public-static-field",
          "protected-static-field",
          "private-static-field",
          "public-instance-field",
          "protected-instance-field",
          "private-instance-field",
          "constructor",
          "public-instance-method",
          "protected-instance-method",
          "private-instance-method",
          "public-static-method",
          "protected-static-method",
          "private-static-method",
      ],
  };
  var Rule = (function (_super) {
      __extends(Rule, _super);
      function Rule() {
          _super.apply(this, arguments);
      }
      Rule.prototype.apply = function (sourceFile) {
          return this.applyWithWalker(new MemberOrderingWalker(sourceFile, this.getOptions()));
      };
      return Rule;
  }(Lint.Rules.AbstractRule));
  exports.Rule = Rule;
  function getModifiers(isMethod, modifiers) {
      return {
          isInstance: !Lint.hasModifier(modifiers, ts.SyntaxKind.StaticKeyword),
          isMethod: isMethod,
          isPrivate: Lint.hasModifier(modifiers, ts.SyntaxKind.PrivateKeyword),
      };
  }
  function toString(modifiers) {
      return [
          modifiers.isPrivate ? "private" : "public",
          modifiers.isInstance ? "instance" : "static",
          "member",
          modifiers.isMethod ? "function" : "variable",
      ].join(" ");
  }
  var AccessLevel;
  (function (AccessLevel) {
      AccessLevel[AccessLevel["PRIVATE"] = 0] = "PRIVATE";
      AccessLevel[AccessLevel["PROTECTED"] = 1] = "PROTECTED";
      AccessLevel[AccessLevel["PUBLIC"] = 2] = "PUBLIC";
  })(AccessLevel || (AccessLevel = {}));
  var Membership;
  (function (Membership) {
      Membership[Membership["INSTANCE"] = 0] = "INSTANCE";
      Membership[Membership["STATIC"] = 1] = "STATIC";
  })(Membership || (Membership = {}));
  var Kind;
  (function (Kind) {
      Kind[Kind["FIELD"] = 0] = "FIELD";
      Kind[Kind["METHOD"] = 1] = "METHOD";
  })(Kind || (Kind = {}));
  function getNodeAndModifiers(node, isMethod, isConstructor) {
      if (isConstructor === void 0) { isConstructor = false; }
      var modifiers = node.modifiers;
      var accessLevel = Lint.hasModifier(modifiers, ts.SyntaxKind.PrivateKeyword) ? AccessLevel.PRIVATE
          : Lint.hasModifier(modifiers, ts.SyntaxKind.ProtectedKeyword) ? AccessLevel.PROTECTED
              : AccessLevel.PUBLIC;
      var kind = isMethod ? Kind.METHOD : Kind.FIELD;
      var membership = Lint.hasModifier(modifiers, ts.SyntaxKind.StaticKeyword) ? Membership.STATIC : Membership.INSTANCE;
      return {
          accessLevel: accessLevel,
          isConstructor: isConstructor,
          kind: kind,
          membership: membership,
          node: node,
      };
  }
  function getNodeOption(_a) {
      var accessLevel = _a.accessLevel, isConstructor = _a.isConstructor, kind = _a.kind, membership = _a.membership;
      if (isConstructor) {
          return "constructor";
      }
      return [
          AccessLevel[accessLevel].toLowerCase(),
          Membership[membership].toLowerCase(),
          Kind[kind].toLowerCase(),
      ].join("-");
  }
  var MemberOrderingWalker = (function (_super) {
      __extends(MemberOrderingWalker, _super);
      function MemberOrderingWalker() {
          _super.apply(this, arguments);
          this.memberStack = [];
          this.hasOrderOption = this.getHasOrderOption();
      }
      MemberOrderingWalker.prototype.visitClassDeclaration = function (node) {
          this.resetPreviousModifiers();
          this.newMemberList();
          _super.prototype.visitClassDeclaration.call(this, node);
          this.checkMemberOrder();
      };
      MemberOrderingWalker.prototype.visitClassExpression = function (node) {
          this.resetPreviousModifiers();
          this.newMemberList();
          _super.prototype.visitClassExpression.call(this, node);
          this.checkMemberOrder();
      };
      MemberOrderingWalker.prototype.visitInterfaceDeclaration = function (node) {
          this.resetPreviousModifiers();
          this.newMemberList();
          _super.prototype.visitInterfaceDeclaration.call(this, node);
          this.checkMemberOrder();
      };
      MemberOrderingWalker.prototype.visitMethodDeclaration = function (node) {
          this.checkModifiersAndSetPrevious(node, getModifiers(true, node.modifiers));
          this.pushMember(getNodeAndModifiers(node, true));
          _super.prototype.visitMethodDeclaration.call(this, node);
      };
      MemberOrderingWalker.prototype.visitMethodSignature = function (node) {
          this.checkModifiersAndSetPrevious(node, getModifiers(true, node.modifiers));
          this.pushMember(getNodeAndModifiers(node, true));
          _super.prototype.visitMethodSignature.call(this, node);
      };
      MemberOrderingWalker.prototype.visitConstructorDeclaration = function (node) {
          this.checkModifiersAndSetPrevious(node, getModifiers(true, node.modifiers));
          this.pushMember(getNodeAndModifiers(node, true, true));
          _super.prototype.visitConstructorDeclaration.call(this, node);
      };
      MemberOrderingWalker.prototype.visitPropertyDeclaration = function (node) {
          var initializer = node.initializer;
          var isFunction = initializer != null
              && (initializer.kind === ts.SyntaxKind.ArrowFunction || initializer.kind === ts.SyntaxKind.FunctionExpression);
          this.checkModifiersAndSetPrevious(node, getModifiers(isFunction, node.modifiers));
          this.pushMember(getNodeAndModifiers(node, isFunction));
          _super.prototype.visitPropertyDeclaration.call(this, node);
      };
      MemberOrderingWalker.prototype.visitPropertySignature = function (node) {
          this.checkModifiersAndSetPrevious(node, getModifiers(false, node.modifiers));
          this.pushMember(getNodeAndModifiers(node, false));
          _super.prototype.visitPropertySignature.call(this, node);
      };
      MemberOrderingWalker.prototype.visitTypeLiteral = function (node) {
      };
      MemberOrderingWalker.prototype.visitObjectLiteralExpression = function (node) {
      };
      MemberOrderingWalker.prototype.resetPreviousModifiers = function () {
          this.previousMember = {
              isInstance: false,
              isMethod: false,
              isPrivate: false,
          };
      };
      MemberOrderingWalker.prototype.checkModifiersAndSetPrevious = function (node, currentMember) {
          if (!this.canAppearAfter(this.previousMember, currentMember)) {
              var failure = this.createFailure(node.getStart(), node.getWidth(), "Declaration of " + toString(currentMember) + " not allowed to appear after declaration of " + toString(this.previousMember));
              this.addFailure(failure);
          }
          this.previousMember = currentMember;
      };
      MemberOrderingWalker.prototype.canAppearAfter = function (previousMember, currentMember) {
          if (previousMember == null || currentMember == null) {
              return true;
          }
          if (this.hasOption(OPTION_VARIABLES_BEFORE_FUNCTIONS) && previousMember.isMethod !== currentMember.isMethod) {
              return Number(previousMember.isMethod) < Number(currentMember.isMethod);
          }
          if (this.hasOption(OPTION_STATIC_BEFORE_INSTANCE) && previousMember.isInstance !== currentMember.isInstance) {
              return Number(previousMember.isInstance) < Number(currentMember.isInstance);
          }
          if (this.hasOption(OPTION_PUBLIC_BEFORE_PRIVATE) && previousMember.isPrivate !== currentMember.isPrivate) {
              return Number(previousMember.isPrivate) < Number(currentMember.isPrivate);
          }
          return true;
      };
      MemberOrderingWalker.prototype.newMemberList = function () {
          if (this.hasOrderOption) {
              this.memberStack.push([]);
          }
      };
      MemberOrderingWalker.prototype.pushMember = function (node) {
          if (this.hasOrderOption) {
              this.memberStack[this.memberStack.length - 1].push(node);
          }
      };
      MemberOrderingWalker.prototype.checkMemberOrder = function () {
          var _this = this;
          if (this.hasOrderOption) {
              var memberList_1 = this.memberStack.pop();
              var order_1 = this.getOrder();
              var memberRank_1 = memberList_1.map(function (n) { return order_1.indexOf(getNodeOption(n)); });
              var prevRank_1 = -1;
              memberRank_1.forEach(function (rank, i) {
                  if (rank === -1) {
                      return;
                  }
                  if (rank < prevRank_1) {
                      var node = memberList_1[i].node;
                      var nodeType = order_1[rank].split("-").join(" ");
                      var prevNodeType = order_1[prevRank_1].split("-").join(" ");
                      var lowerRanks = memberRank_1.filter(function (r) { return r < rank && r !== -1; }).sort();
                      var locationHint = lowerRanks.length > 0
                          ? "after " + order_1[lowerRanks[lowerRanks.length - 1]].split("-").join(" ") + "s"
                          : "at the beginning of the class/interface";
                      var errorLine1 = ("Declaration of " + nodeType + " not allowed after declaration of " + prevNodeType + ". ") +
                          ("Instead, this should come " + locationHint + ".");
                      _this.addFailure(_this.createFailure(node.getStart(), node.getWidth(), errorLine1));
                  }
                  else {
                      prevRank_1 = rank;
                  }
              });
          }
      };
      MemberOrderingWalker.prototype.getHasOrderOption = function () {
          var allOptions = this.getOptions();
          if (allOptions == null || allOptions.length === 0) {
              return false;
          }
          var firstOption = allOptions[0];
          return firstOption != null && typeof firstOption === "object" && firstOption[OPTION_ORDER] != null;
      };
      MemberOrderingWalker.prototype.getOrder = function () {
          var orderOption = this.getOptions()[0][OPTION_ORDER];
          if (Array.isArray(orderOption)) {
              return orderOption;
          }
          else if (typeof orderOption === "string") {
              return PRESET_ORDERS[orderOption] || PRESET_ORDERS["default"];
          }
      };
      return MemberOrderingWalker;
  }(Lint.RuleWalker));
  exports.MemberOrderingWalker = MemberOrderingWalker;
  