(function() {
  var Selector, indexCounter, slick;

  slick = require('atom-slick');

  indexCounter = 0;

  module.exports = Selector = (function() {
    Selector.create = function(source, options) {
      var selectorAst, selectorComponent, _i, _j, _len, _len1, _ref, _results;
      _ref = slick.parse(source);
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        selectorAst = _ref[_i];
        for (_j = 0, _len1 = selectorAst.length; _j < _len1; _j++) {
          selectorComponent = selectorAst[_j];
          this.parsePseudoSelectors(selectorComponent);
        }
        _results.push(new this(selectorAst, options));
      }
      return _results;
    };

    Selector.parsePseudoSelectors = function(selectorComponent) {
      var pseudoClass, _i, _len, _ref, _ref1;
      if (selectorComponent.pseudos == null) {
        return;
      }
      _ref = selectorComponent.pseudos;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        pseudoClass = _ref[_i];
        if (pseudoClass.name === 'not') {
          if (selectorComponent.notSelectors == null) {
            selectorComponent.notSelectors = [];
          }
          (_ref1 = selectorComponent.notSelectors).push.apply(_ref1, this.create(pseudoClass.value));
        } else {
          console.warn("Unsupported pseudo-selector: " + pseudoClass.name);
        }
      }
    };

    function Selector(selector, options) {
      var priority, _ref;
      this.selector = selector;
      priority = (_ref = options != null ? options.priority : void 0) != null ? _ref : 0;
      this.specificity = this.calculateSpecificity();
      this.index = priority + indexCounter++;
    }

    Selector.prototype.matches = function(scopeChain) {
      var requireMatch, scopeIndex, selectorIndex;
      if (typeof scopeChain === 'string') {
        scopeChain = slick.parse(scopeChain)[0];
        if (scopeChain == null) {
          return false;
        }
      }
      selectorIndex = this.selector.length - 1;
      scopeIndex = scopeChain.length - 1;
      requireMatch = true;
      while (selectorIndex >= 0 && scopeIndex >= 0) {
        if (this.selectorComponentMatchesScope(this.selector[selectorIndex], scopeChain[scopeIndex])) {
          requireMatch = this.selector[selectorIndex].combinator === '>';
          selectorIndex--;
        } else if (requireMatch) {
          return false;
        }
        scopeIndex--;
      }
      return selectorIndex < 0;
    };

    Selector.prototype.selectorComponentMatchesScope = function(selectorComponent, scope) {
      var attribute, className, scopeAttributes, selector, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
      if (selectorComponent.classList != null) {
        _ref = selectorComponent.classList;
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          className = _ref[_i];
          if (((_ref1 = scope.classes) != null ? _ref1[className] : void 0) == null) {
            return false;
          }
        }
      }
      if (selectorComponent.tag != null) {
        if (!(selectorComponent.tag === scope.tag || selectorComponent.tag === '*')) {
          return false;
        }
      }
      if (selectorComponent.attributes != null) {
        scopeAttributes = {};
        _ref3 = (_ref2 = scope.attributes) != null ? _ref2 : [];
        for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
          attribute = _ref3[_j];
          scopeAttributes[attribute.name] = attribute;
        }
        _ref4 = selectorComponent.attributes;
        for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) {
          attribute = _ref4[_k];
          if (((_ref5 = scopeAttributes[attribute.name]) != null ? _ref5.value : void 0) !== attribute.value) {
            return false;
          }
        }
      }
      if (selectorComponent.notSelectors != null) {
        _ref6 = selectorComponent.notSelectors;
        for (_l = 0, _len3 = _ref6.length; _l < _len3; _l++) {
          selector = _ref6[_l];
          if (selector.matches([scope])) {
            return false;
          }
        }
      }
      return true;
    };

    Selector.prototype.compare = function(other) {
      if (other.specificity === this.specificity) {
        return other.index - this.index;
      } else {
        return other.specificity - this.specificity;
      }
    };

    Selector.prototype.isEqual = function(other) {
      return this.toString() === other.toString();
    };

    Selector.prototype.calculateSpecificity = function() {
      var a, b, c, selectorComponent, _i, _len, _ref;
      a = 0;
      b = 0;
      c = 0;
      _ref = this.selector;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        selectorComponent = _ref[_i];
        if (selectorComponent.classList != null) {
          b += selectorComponent.classList.length;
        }
        if (selectorComponent.attributes != null) {
          b += selectorComponent.attributes.length;
        }
        if (selectorComponent.tag != null) {
          c += 1;
        }
      }
      return (a * 100) + (b * 10) + (c * 1);
    };

    Selector.prototype.toString = function() {
      return this.selector.toString().replace(/\*\./g, '.');
    };

    return Selector;

  })();

}).call(this);
