(function() {
  var BracketMatcherView, CompositeDisposable, Range, TagFinder, endPair, endPairMatches, pairRegexes, startPair, startPairMatches, _;

  CompositeDisposable = require('atom').CompositeDisposable;

  _ = require('underscore-plus');

  Range = require('atom').Range;

  TagFinder = require('./tag-finder');

  startPairMatches = {
    '(': ')',
    '[': ']',
    '{': '}'
  };

  endPairMatches = {
    ')': '(',
    ']': '[',
    '}': '{'
  };

  pairRegexes = {};

  for (startPair in startPairMatches) {
    endPair = startPairMatches[startPair];
    pairRegexes[startPair] = new RegExp("[" + (_.escapeRegExp(startPair + endPair)) + "]", 'g');
  }

  module.exports = BracketMatcherView = (function() {
    function BracketMatcherView(editor, editorElement) {
      this.editor = editor;
      this.subscriptions = new CompositeDisposable;
      this.tagFinder = new TagFinder(this.editor);
      this.pairHighlighted = false;
      this.tagHighlighted = false;
      this.subscriptions.add(this.editor.onDidChange((function(_this) {
        return function() {
          return _this.updateMatch();
        };
      })(this)));
      this.subscriptions.add(this.editor.onDidChangeGrammar((function(_this) {
        return function() {
          return _this.updateMatch();
        };
      })(this)));
      this.subscribeToCursor();
      this.subscriptions.add(atom.commands.add(editorElement, 'bracket-matcher:go-to-matching-bracket', (function(_this) {
        return function() {
          return _this.goToMatchingPair();
        };
      })(this)));
      this.subscriptions.add(atom.commands.add(editorElement, 'bracket-matcher:go-to-enclosing-bracket', (function(_this) {
        return function() {
          return _this.goToEnclosingPair();
        };
      })(this)));
      this.subscriptions.add(atom.commands.add(editorElement, 'bracket-matcher:select-inside-brackets', (function(_this) {
        return function() {
          return _this.selectInsidePair();
        };
      })(this)));
      this.subscriptions.add(atom.commands.add(editorElement, 'bracket-matcher:close-tag', (function(_this) {
        return function() {
          return _this.closeTag();
        };
      })(this)));
      this.subscriptions.add(atom.commands.add(editorElement, 'bracket-matcher:remove-matching-brackets', (function(_this) {
        return function() {
          return _this.removeMatchingBrackets();
        };
      })(this)));
      this.updateMatch();
    }

    BracketMatcherView.prototype.subscribeToCursor = function() {
      var cursor, cursorSubscriptions;
      cursor = this.editor.getLastCursor();
      if (cursor == null) {
        return;
      }
      cursorSubscriptions = new CompositeDisposable;
      cursorSubscriptions.add(cursor.onDidChangePosition((function(_this) {
        return function() {
          return _this.updateMatch();
        };
      })(this)));
      return cursorSubscriptions.add(cursor.onDidDestroy((function(_this) {
        return function() {
          cursorSubscriptions.dispose();
          _this.subscribeToCursor();
          if (_this.editor.isAlive()) {
            return _this.updateMatch();
          }
        };
      })(this)));
    };

    BracketMatcherView.prototype.updateMatch = function() {
      var currentPair, matchPosition, matchingPair, pair, position, _ref, _ref1;
      if (this.pairHighlighted) {
        this.editor.destroyMarker(this.startMarker.id);
        this.editor.destroyMarker(this.endMarker.id);
      }
      this.pairHighlighted = false;
      this.tagHighlighted = false;
      if (!this.editor.getLastSelection().isEmpty()) {
        return;
      }
      if (this.editor.isFoldedAtCursorRow()) {
        return;
      }
      _ref = this.findCurrentPair(startPairMatches), position = _ref.position, currentPair = _ref.currentPair, matchingPair = _ref.matchingPair;
      if (position) {
        matchPosition = this.findMatchingEndPair(position, currentPair, matchingPair);
      } else {
        _ref1 = this.findCurrentPair(endPairMatches), position = _ref1.position, currentPair = _ref1.currentPair, matchingPair = _ref1.matchingPair;
        if (position) {
          matchPosition = this.findMatchingStartPair(position, matchingPair, currentPair);
        }
      }
      if ((position != null) && (matchPosition != null)) {
        this.startMarker = this.createMarker([position, position.traverse([0, 1])]);
        this.endMarker = this.createMarker([matchPosition, matchPosition.traverse([0, 1])]);
        return this.pairHighlighted = true;
      } else {
        if (pair = this.tagFinder.findMatchingTags()) {
          this.startMarker = this.createMarker(pair.startRange);
          this.endMarker = this.createMarker(pair.endRange);
          this.pairHighlighted = true;
          return this.tagHighlighted = true;
        }
      }
    };

    BracketMatcherView.prototype.removeMatchingBrackets = function() {
      if (this.editor.hasMultipleCursors()) {
        return this.editor.backspace();
      }
      return this.editor.transact((function(_this) {
        return function() {
          var currentPair, matchPosition, matchingPair, position, text, _ref, _ref1;
          if (_this.editor.getLastSelection().isEmpty()) {
            _this.editor.selectLeft();
          }
          text = _this.editor.getSelectedText();
          if (startPairMatches.hasOwnProperty(text) || endPairMatches.hasOwnProperty(text)) {
            _ref = _this.findCurrentPair(startPairMatches), position = _ref.position, currentPair = _ref.currentPair, matchingPair = _ref.matchingPair;
            if (position) {
              matchPosition = _this.findMatchingEndPair(position, currentPair, matchingPair);
            } else {
              _ref1 = _this.findCurrentPair(endPairMatches), position = _ref1.position, currentPair = _ref1.currentPair, matchingPair = _ref1.matchingPair;
              if (position) {
                matchPosition = _this.findMatchingStartPair(position, matchingPair, currentPair);
              }
            }
            if ((position != null) && (matchPosition != null)) {
              _this.editor.setCursorBufferPosition(matchPosition);
              _this.editor["delete"]();
              if (position.row === matchPosition.row && endPairMatches.hasOwnProperty(currentPair)) {
                position = position.traverse([0, -1]);
              }
              _this.editor.setCursorBufferPosition(position);
              return _this.editor["delete"]();
            } else {
              return _this.editor.backspace();
            }
          } else {
            return _this.editor.backspace();
          }
        };
      })(this));
    };

    BracketMatcherView.prototype.findMatchingEndPair = function(startPairPosition, startPair, endPair) {
      var endPairPosition, scanRange, unpairedCount;
      scanRange = new Range(startPairPosition.traverse([0, 1]), this.editor.buffer.getEndPosition());
      endPairPosition = null;
      unpairedCount = 0;
      this.editor.scanInBufferRange(pairRegexes[startPair], scanRange, function(_arg) {
        var match, range, stop;
        match = _arg.match, range = _arg.range, stop = _arg.stop;
        switch (match[0]) {
          case startPair:
            return unpairedCount++;
          case endPair:
            unpairedCount--;
            if (unpairedCount < 0) {
              endPairPosition = range.start;
              return stop();
            }
        }
      });
      return endPairPosition;
    };

    BracketMatcherView.prototype.findMatchingStartPair = function(endPairPosition, startPair, endPair) {
      var scanRange, startPairPosition, unpairedCount;
      scanRange = new Range([0, 0], endPairPosition);
      startPairPosition = null;
      unpairedCount = 0;
      this.editor.backwardsScanInBufferRange(pairRegexes[startPair], scanRange, function(_arg) {
        var match, range, stop;
        match = _arg.match, range = _arg.range, stop = _arg.stop;
        switch (match[0]) {
          case startPair:
            unpairedCount--;
            if (unpairedCount < 0) {
              startPairPosition = range.start;
              return stop();
            }
            break;
          case endPair:
            return unpairedCount++;
        }
      });
      return startPairPosition;
    };

    BracketMatcherView.prototype.findAnyStartPair = function(cursorPosition) {
      var combinedRegExp, endPairRegExp, scanRange, startPairRegExp, startPosition, unpairedCount;
      scanRange = new Range([0, 0], cursorPosition);
      startPair = _.escapeRegExp(_.keys(startPairMatches).join(''));
      endPair = _.escapeRegExp(_.keys(endPairMatches).join(''));
      combinedRegExp = new RegExp("[" + startPair + endPair + "]", 'g');
      startPairRegExp = new RegExp("[" + startPair + "]", 'g');
      endPairRegExp = new RegExp("[" + endPair + "]", 'g');
      startPosition = null;
      unpairedCount = 0;
      this.editor.backwardsScanInBufferRange(combinedRegExp, scanRange, (function(_this) {
        return function(_arg) {
          var match, range, stop;
          match = _arg.match, range = _arg.range, stop = _arg.stop;
          if (match[0].match(endPairRegExp)) {
            return unpairedCount++;
          } else if (match[0].match(startPairRegExp)) {
            unpairedCount--;
            startPosition = range.start;
            if (unpairedCount < 0) {
              return stop();
            }
          }
        };
      })(this));
      return startPosition;
    };

    BracketMatcherView.prototype.createMarker = function(bufferRange) {
      var marker;
      marker = this.editor.markBufferRange(bufferRange);
      this.editor.decorateMarker(marker, {
        type: 'highlight',
        "class": 'bracket-matcher',
        deprecatedRegionClass: 'bracket-matcher'
      });
      return marker;
    };

    BracketMatcherView.prototype.findCurrentPair = function(matches) {
      var currentPair, matchingPair, position;
      position = this.editor.getCursorBufferPosition();
      currentPair = this.editor.getTextInRange(Range.fromPointWithDelta(position, 0, 1));
      if (!matches[currentPair]) {
        position = position.traverse([0, -1]);
        currentPair = this.editor.getTextInRange(Range.fromPointWithDelta(position, 0, 1));
      }
      if (matchingPair = matches[currentPair]) {
        return {
          position: position,
          currentPair: currentPair,
          matchingPair: matchingPair
        };
      } else {
        return {};
      }
    };

    BracketMatcherView.prototype.goToMatchingPair = function() {
      var endPosition, endRange, position, previousPosition, startPosition, startRange, tagCharacterOffset, tagLength, _ref;
      if (!this.pairHighlighted) {
        return this.goToEnclosingPair();
      }
      position = this.editor.getCursorBufferPosition();
      if (this.tagHighlighted) {
        startRange = this.startMarker.getBufferRange();
        tagLength = startRange.end.column - startRange.start.column;
        endRange = this.endMarker.getBufferRange();
        if (startRange.compare(endRange) > 0) {
          _ref = [endRange, startRange], startRange = _ref[0], endRange = _ref[1];
        }
        startRange = new Range(startRange.start.traverse([0, -1]), endRange.end.traverse([0, -1]));
        endRange = new Range(endRange.start.traverse([0, -2]), endRange.end.traverse([0, -2]));
        if (position.isLessThan(endRange.start)) {
          tagCharacterOffset = position.column - startRange.start.column;
          if (tagCharacterOffset > 0) {
            tagCharacterOffset++;
          }
          tagCharacterOffset = Math.min(tagCharacterOffset, tagLength + 2);
          return this.editor.setCursorBufferPosition(endRange.start.traverse([0, tagCharacterOffset]));
        } else {
          tagCharacterOffset = position.column - endRange.start.column;
          if (tagCharacterOffset > 1) {
            tagCharacterOffset--;
          }
          tagCharacterOffset = Math.min(tagCharacterOffset, tagLength + 1);
          return this.editor.setCursorBufferPosition(startRange.start.traverse([0, tagCharacterOffset]));
        }
      } else {
        previousPosition = position.traverse([0, -1]);
        startPosition = this.startMarker.getStartBufferPosition();
        endPosition = this.endMarker.getStartBufferPosition();
        if (position.isEqual(startPosition)) {
          return this.editor.setCursorBufferPosition(endPosition.traverse([0, 1]));
        } else if (previousPosition.isEqual(startPosition)) {
          return this.editor.setCursorBufferPosition(endPosition);
        } else if (position.isEqual(endPosition)) {
          return this.editor.setCursorBufferPosition(startPosition.traverse([0, 1]));
        } else if (previousPosition.isEqual(endPosition)) {
          return this.editor.setCursorBufferPosition(startPosition);
        }
      }
    };

    BracketMatcherView.prototype.goToEnclosingPair = function() {
      var endRange, matchPosition, pair, startRange, _ref;
      if (this.pairHighlighted) {
        return;
      }
      if (matchPosition = this.findAnyStartPair(this.editor.getCursorBufferPosition())) {
        return this.editor.setCursorBufferPosition(matchPosition);
      } else if (pair = this.tagFinder.findEnclosingTags()) {
        startRange = pair.startRange, endRange = pair.endRange;
        if (startRange.compare(endRange) > 0) {
          _ref = [endRange, startRange], startRange = _ref[0], endRange = _ref[1];
        }
        return this.editor.setCursorBufferPosition(pair.startRange.start);
      }
    };

    BracketMatcherView.prototype.selectInsidePair = function() {
      var endPosition, endRange, pair, rangeToSelect, startPosition, startRange, _ref, _ref1;
      if (this.pairHighlighted) {
        startRange = this.startMarker.getBufferRange();
        endRange = this.endMarker.getBufferRange();
        if (startRange.compare(endRange) > 0) {
          _ref = [endRange, startRange], startRange = _ref[0], endRange = _ref[1];
        }
        if (this.tagHighlighted) {
          startPosition = startRange.end;
          endPosition = endRange.start.traverse([0, -2]);
        } else {
          startPosition = startRange.start;
          endPosition = endRange.start;
        }
      } else {
        if (startPosition = this.findAnyStartPair(this.editor.getCursorBufferPosition())) {
          startPair = this.editor.getTextInRange(Range.fromPointWithDelta(startPosition, 0, 1));
          endPosition = this.findMatchingEndPair(startPosition, startPair, startPairMatches[startPair]);
        } else if (pair = this.tagFinder.findEnclosingTags()) {
          startRange = pair.startRange, endRange = pair.endRange;
          if (startRange.compare(endRange) > 0) {
            _ref1 = [endRange, startRange], startRange = _ref1[0], endRange = _ref1[1];
          }
          startPosition = startRange.end;
          endPosition = endRange.start.traverse([0, -2]);
        }
      }
      if ((startPosition != null) && (endPosition != null)) {
        rangeToSelect = new Range(startPosition.traverse([0, 1]), endPosition);
        return this.editor.setSelectedBufferRange(rangeToSelect);
      }
    };

    BracketMatcherView.prototype.closeTag = function() {
      var cursorPosition, postFragment, preFragment, tag, textLimits;
      cursorPosition = this.editor.getCursorBufferPosition();
      textLimits = this.editor.getBuffer().getRange();
      preFragment = this.editor.getTextInBufferRange([[0, 0], cursorPosition]);
      postFragment = this.editor.getTextInBufferRange([cursorPosition, [Infinity, Infinity]]);
      if (tag = this.tagFinder.closingTagForFragments(preFragment, postFragment)) {
        return this.editor.insertText("</" + tag + ">");
      }
    };

    return BracketMatcherView;

  })();

}).call(this);
