(function() {
  var BracketMatcher, CompositeDisposable, SelectorCache, _,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  _ = require('underscore-plus');

  CompositeDisposable = require('atom').CompositeDisposable;

  SelectorCache = require('./selector-cache');

  module.exports = BracketMatcher = (function() {
    BracketMatcher.prototype.pairsToIndent = {
      '(': ')',
      '[': ']',
      '{': '}'
    };

    BracketMatcher.prototype.defaultPairs = {
      '(': ')',
      '[': ']',
      '{': '}',
      '"': '"',
      "'": "'",
      '`': '`'
    };

    BracketMatcher.prototype.smartQuotePairs = {
      "“": "”",
      '‘': '’',
      "«": "»",
      "‹": "›"
    };

    BracketMatcher.prototype.toggleQuotes = function(includeSmartQuotes) {
      if (includeSmartQuotes) {
        return this.pairedCharacters = _.extend({}, this.defaultPairs, this.smartQuotePairs);
      } else {
        return this.pairedCharacters = this.defaultPairs;
      }
    };

    function BracketMatcher(editor, editorElement) {
      this.editor = editor;
      this.backspace = __bind(this.backspace, this);
      this.insertNewline = __bind(this.insertNewline, this);
      this.insertText = __bind(this.insertText, this);
      this.subscriptions = new CompositeDisposable;
      this.bracketMarkers = [];
      _.adviseBefore(this.editor, 'insertText', this.insertText);
      _.adviseBefore(this.editor, 'insertNewline', this.insertNewline);
      _.adviseBefore(this.editor, 'backspace', this.backspace);
      this.subscriptions.add(atom.commands.add(editorElement, 'bracket-matcher:remove-brackets-from-selection', (function(_this) {
        return function(event) {
          if (!_this.removeBrackets()) {
            return event.abortKeyBinding();
          }
        };
      })(this)));
      this.subscriptions.add(atom.config.observe('bracket-matcher.autocompleteSmartQuotes', (function(_this) {
        return function(newValue) {
          return _this.toggleQuotes(newValue);
        };
      })(this)));
      this.subscriptions.add(this.editor.onDidDestroy((function(_this) {
        return function() {
          return _this.unsubscribe();
        };
      })(this)));
    }

    BracketMatcher.prototype.insertText = function(text, options) {
      var autoCompleteOpeningBracket, bracketMarker, cursorBufferPosition, hasEscapeSequenceBeforeCursor, hasQuoteBeforeCursor, hasWordAfterCursor, hasWordBeforeCursor, nextCharacter, pair, previousCharacter, previousCharacters, range, skipOverExistingClosingBracket, _ref;
      if (!text) {
        return true;
      }
      if ((options != null ? options.select : void 0) || (options != null ? options.undo : void 0) === 'skip') {
        return true;
      }
      if (this.wrapSelectionInBrackets(text)) {
        return false;
      }
      if (this.editor.hasMultipleCursors()) {
        return true;
      }
      cursorBufferPosition = this.editor.getCursorBufferPosition();
      previousCharacters = this.editor.getTextInBufferRange([cursorBufferPosition.traverse([0, -2]), cursorBufferPosition]);
      nextCharacter = this.editor.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.traverse([0, 1])]);
      previousCharacter = previousCharacters.slice(-1);
      hasWordAfterCursor = /\w/.test(nextCharacter);
      hasWordBeforeCursor = /\w/.test(previousCharacter);
      hasQuoteBeforeCursor = previousCharacter === text[0];
      hasEscapeSequenceBeforeCursor = ((_ref = previousCharacters.match(/\\/g)) != null ? _ref.length : void 0) >= 1;
      if (text === '#' && this.isCursorOnInterpolatedString()) {
        autoCompleteOpeningBracket = atom.config.get('bracket-matcher.autocompleteBrackets') && !hasEscapeSequenceBeforeCursor;
        text += '{';
        pair = '}';
      } else {
        autoCompleteOpeningBracket = atom.config.get('bracket-matcher.autocompleteBrackets') && this.isOpeningBracket(text) && !hasWordAfterCursor && !(this.isQuote(text) && (hasWordBeforeCursor || hasQuoteBeforeCursor)) && !hasEscapeSequenceBeforeCursor;
        pair = this.pairedCharacters[text];
      }
      skipOverExistingClosingBracket = false;
      if (this.isClosingBracket(text) && nextCharacter === text && !hasEscapeSequenceBeforeCursor) {
        if (bracketMarker = _.find(this.bracketMarkers, (function(_this) {
          return function(marker) {
            return marker.isValid() && marker.getBufferRange().end.isEqual(cursorBufferPosition);
          };
        })(this))) {
          skipOverExistingClosingBracket = true;
        }
      }
      if (skipOverExistingClosingBracket) {
        bracketMarker.destroy();
        _.remove(this.bracketMarkers, bracketMarker);
        this.editor.moveRight();
        return false;
      } else if (autoCompleteOpeningBracket) {
        this.editor.insertText(text + pair);
        this.editor.moveLeft();
        range = [cursorBufferPosition, cursorBufferPosition.traverse([0, text.length])];
        this.bracketMarkers.push(this.editor.markBufferRange(range));
        return false;
      }
    };

    BracketMatcher.prototype.insertNewline = function() {
      var cursorBufferPosition, hasEscapeSequenceBeforeCursor, nextCharacter, previousCharacter, previousCharacters, _ref;
      if (this.editor.hasMultipleCursors()) {
        return;
      }
      if (!this.editor.getLastSelection().isEmpty()) {
        return;
      }
      cursorBufferPosition = this.editor.getCursorBufferPosition();
      previousCharacters = this.editor.getTextInBufferRange([cursorBufferPosition.traverse([0, -2]), cursorBufferPosition]);
      nextCharacter = this.editor.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.traverse([0, 1])]);
      previousCharacter = previousCharacters.slice(-1);
      hasEscapeSequenceBeforeCursor = ((_ref = previousCharacters.match(/\\/g)) != null ? _ref.length : void 0) >= 1;
      if (this.pairsToIndent[previousCharacter] === nextCharacter && !hasEscapeSequenceBeforeCursor) {
        this.editor.transact((function(_this) {
          return function() {
            var cursorRow;
            _this.editor.insertText("\n\n");
            _this.editor.moveUp();
            if (atom.config.get('editor.autoIndent')) {
              cursorRow = _this.editor.getCursorBufferPosition().row;
              return _this.editor.autoIndentBufferRows(cursorRow, cursorRow + 1);
            }
          };
        })(this));
        return false;
      }
    };

    BracketMatcher.prototype.backspace = function() {
      var cursorBufferPosition, hasEscapeSequenceBeforeCursor, nextCharacter, previousCharacter, previousCharacters, _ref;
      if (this.editor.hasMultipleCursors()) {
        return;
      }
      if (!this.editor.getLastSelection().isEmpty()) {
        return;
      }
      cursorBufferPosition = this.editor.getCursorBufferPosition();
      previousCharacters = this.editor.getTextInBufferRange([cursorBufferPosition.traverse([0, -2]), cursorBufferPosition]);
      nextCharacter = this.editor.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.traverse([0, 1])]);
      previousCharacter = previousCharacters.slice(-1);
      hasEscapeSequenceBeforeCursor = ((_ref = previousCharacters.match(/\\/g)) != null ? _ref.length : void 0) >= 1;
      if ((this.pairedCharacters[previousCharacter] === nextCharacter) && !hasEscapeSequenceBeforeCursor && atom.config.get('bracket-matcher.autocompleteBrackets')) {
        this.editor.transact((function(_this) {
          return function() {
            _this.editor.moveLeft();
            _this.editor["delete"]();
            return _this.editor["delete"]();
          };
        })(this));
        return false;
      }
    };

    BracketMatcher.prototype.removeBrackets = function() {
      var bracketsRemoved;
      bracketsRemoved = false;
      this.editor.mutateSelectedText((function(_this) {
        return function(selection) {
          var options, range, selectionEnd, selectionStart, text;
          if (!_this.selectionIsWrappedByMatchingBrackets(selection)) {
            return;
          }
          range = selection.getBufferRange();
          options = {
            reversed: selection.isReversed()
          };
          selectionStart = range.start;
          if (range.start.row === range.end.row) {
            selectionEnd = range.end.traverse([0, -2]);
          } else {
            selectionEnd = range.end.traverse([0, -1]);
          }
          text = selection.getText();
          selection.insertText(text.substring(1, text.length - 1));
          selection.setBufferRange([selectionStart, selectionEnd], options);
          return bracketsRemoved = true;
        };
      })(this));
      return bracketsRemoved;
    };

    BracketMatcher.prototype.wrapSelectionInBrackets = function(bracket) {
      var pair, selectionWrapped;
      if (!atom.config.get('bracket-matcher.wrapSelectionsInBrackets')) {
        return false;
      }
      if (bracket === '#') {
        if (!this.isCursorOnInterpolatedString()) {
          return false;
        }
        bracket = '#{';
        pair = '}';
      } else {
        if (!this.isOpeningBracket(bracket)) {
          return false;
        }
        pair = this.pairedCharacters[bracket];
      }
      selectionWrapped = false;
      this.editor.mutateSelectedText(function(selection) {
        var options, range, selectionEnd, selectionStart;
        if (selection.isEmpty()) {
          return;
        }
        if (bracket === '#{' && !selection.getBufferRange().isSingleLine()) {
          return;
        }
        selectionWrapped = true;
        range = selection.getBufferRange();
        options = {
          reversed: selection.isReversed()
        };
        selection.insertText("" + bracket + (selection.getText()) + pair);
        selectionStart = range.start.traverse([0, bracket.length]);
        if (range.start.row === range.end.row) {
          selectionEnd = range.end.traverse([0, bracket.length]);
        } else {
          selectionEnd = range.end;
        }
        return selection.setBufferRange([selectionStart, selectionEnd], options);
      });
      return selectionWrapped;
    };

    BracketMatcher.prototype.isQuote = function(string) {
      return /['"`]/.test(string);
    };

    BracketMatcher.prototype.isCursorOnInterpolatedString = function() {
      var segments;
      if (this.interpolatedStringSelector == null) {
        segments = ['*.*.*.interpolated.ruby', 'string.interpolated.ruby', 'string.regexp.interpolated.ruby', 'string.quoted.double.coffee', 'string.unquoted.heredoc.ruby', 'string.quoted.double.livescript', 'string.quoted.double.heredoc.livescript'];
        this.interpolatedStringSelector = SelectorCache.get(segments.join(' | '));
      }
      return this.interpolatedStringSelector.matches(this.editor.getLastCursor().getScopeDescriptor().getScopesArray());
    };

    BracketMatcher.prototype.getInvertedPairedCharacters = function() {
      var close, open, _ref;
      if (this.invertedPairedCharacters) {
        return this.invertedPairedCharacters;
      }
      this.invertedPairedCharacters = {};
      _ref = this.pairedCharacters;
      for (open in _ref) {
        close = _ref[open];
        this.invertedPairedCharacters[close] = open;
      }
      return this.invertedPairedCharacters;
    };

    BracketMatcher.prototype.isOpeningBracket = function(string) {
      return this.pairedCharacters.hasOwnProperty(string);
    };

    BracketMatcher.prototype.isClosingBracket = function(string) {
      return this.getInvertedPairedCharacters().hasOwnProperty(string);
    };

    BracketMatcher.prototype.selectionIsWrappedByMatchingBrackets = function(selection) {
      var firstCharacter, lastCharacter, selectedText;
      if (selection.isEmpty()) {
        return false;
      }
      selectedText = selection.getText();
      firstCharacter = selectedText[0];
      lastCharacter = selectedText[selectedText.length - 1];
      return this.pairedCharacters[firstCharacter] === lastCharacter;
    };

    BracketMatcher.prototype.unsubscribe = function() {
      return this.subscriptions.dispose();
    };

    return BracketMatcher;

  })();

}).call(this);
