(function() {
  var CompositeDisposable, Emitter, FindModel, escapeHelper, _, _ref,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  _ = require('underscore-plus');

  _ref = require('atom'), Emitter = _ref.Emitter, CompositeDisposable = _ref.CompositeDisposable;

  escapeHelper = require('./escape-helper');

  module.exports = FindModel = (function() {
    FindModel.markerClass = 'find-result';

    function FindModel(state) {
      var _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8;
      if (state == null) {
        state = {};
      }
      this.setCurrentMarkerFromSelection = __bind(this.setCurrentMarkerFromSelection, this);
      this.activePaneItemChanged = __bind(this.activePaneItemChanged, this);
      this.emitter = new Emitter();
      this.pattern = '';
      this.useRegex = (_ref1 = (_ref2 = state.useRegex) != null ? _ref2 : atom.config.get('find-and-replace.useRegex')) != null ? _ref1 : false;
      this.inCurrentSelection = (_ref3 = (_ref4 = state.inCurrentSelection) != null ? _ref4 : atom.config.get('find-and-replace.inCurrentSelection')) != null ? _ref3 : false;
      this.caseSensitive = (_ref5 = (_ref6 = state.caseSensitive) != null ? _ref6 : atom.config.get('find-and-replace.caseSensitive')) != null ? _ref5 : false;
      this.wholeWord = (_ref7 = (_ref8 = state.wholeWord) != null ? _ref8 : atom.config.get('find-and-replace.wholeWord')) != null ? _ref7 : false;
      this.valid = false;
      atom.workspace.observeActivePaneItem(this.activePaneItemChanged);
    }

    FindModel.prototype.onDidUpdate = function(callback) {
      return this.emitter.on('did-update', callback);
    };

    FindModel.prototype.onDidError = function(callback) {
      return this.emitter.on('did-error', callback);
    };

    FindModel.prototype.onDidChangeCurrentResult = function(callback) {
      return this.emitter.on('did-change-current-result', callback);
    };

    FindModel.prototype.activePaneItemChanged = function(paneItem) {
      var _ref1;
      this.editor = null;
      if ((_ref1 = this.subscriptions) != null) {
        _ref1.dispose();
      }
      this.subscriptions = new CompositeDisposable;
      this.destroyAllMarkers();
      if ((paneItem != null ? typeof paneItem.getBuffer === "function" ? paneItem.getBuffer() : void 0 : void 0) != null) {
        this.editor = paneItem;
        this.subscriptions.add(this.editor.getBuffer().onDidStopChanging((function(_this) {
          return function() {
            if (!_this.replacing) {
              return _this.updateMarkers();
            }
          };
        })(this)));
        this.subscriptions.add(this.editor.onDidAddSelection(this.setCurrentMarkerFromSelection));
        this.subscriptions.add(this.editor.onDidChangeSelectionRange(this.setCurrentMarkerFromSelection));
        return this.updateMarkers();
      }
    };

    FindModel.prototype.serialize = function() {
      return {
        useRegex: this.useRegex,
        inCurrentSelection: this.inCurrentSelection,
        caseSensitive: this.caseSensitive,
        wholeWord: this.wholeWord
      };
    };

    FindModel.prototype.update = function(newParams) {
      var currentParams;
      if (newParams == null) {
        newParams = {};
      }
      currentParams = {
        pattern: this.pattern,
        useRegex: this.useRegex,
        inCurrentSelection: this.inCurrentSelection,
        caseSensitive: this.caseSensitive,
        wholeWord: this.wholeWord
      };
      _.defaults(newParams, currentParams);
      if (!(this.valid && _.isEqual(newParams, currentParams))) {
        _.extend(this, newParams);
        return this.updateMarkers();
      }
    };

    FindModel.prototype.replace = function(markers, replacementPattern) {
      if (!((markers != null ? markers.length : void 0) > 0)) {
        return;
      }
      this.replacing = true;
      this.editor.transact((function(_this) {
        return function() {
          var bufferRange, marker, replacementText, textToReplace, _i, _len, _results;
          _results = [];
          for (_i = 0, _len = markers.length; _i < _len; _i++) {
            marker = markers[_i];
            bufferRange = marker.getBufferRange();
            replacementText = null;
            if (_this.useRegex) {
              replacementPattern = escapeHelper.unescapeEscapeSequence(replacementPattern);
              textToReplace = _this.editor.getTextInBufferRange(bufferRange);
              replacementText = textToReplace.replace(_this.getRegex(), replacementPattern);
            }
            _this.editor.setTextInBufferRange(bufferRange, replacementText != null ? replacementText : replacementPattern);
            marker.destroy();
            _results.push(_this.markers.splice(_this.markers.indexOf(marker), 1));
          }
          return _results;
        };
      })(this));
      this.replacing = false;
      return this.emitter.emit('did-update', _.clone(this.markers));
    };

    FindModel.prototype.updateMarkers = function() {
      var bufferRange, e, id, marker, markersToRemoveById, updatedMarkers, _i, _len, _ref1;
      if ((this.editor == null) || !this.pattern) {
        this.destroyAllMarkers();
        return;
      }
      this.valid = true;
      if (this.inCurrentSelection) {
        bufferRange = this.editor.getSelectedBufferRange();
      } else {
        bufferRange = [[0, 0], [Infinity, Infinity]];
      }
      updatedMarkers = [];
      markersToRemoveById = {};
      _ref1 = this.markers;
      for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
        marker = _ref1[_i];
        markersToRemoveById[marker.id] = marker;
      }
      try {
        this.editor.scanInBufferRange(this.getRegex(), bufferRange, (function(_this) {
          return function(_arg) {
            var range;
            range = _arg.range;
            if (marker = _this.findMarker(range)) {
              delete markersToRemoveById[marker.id];
            } else {
              marker = _this.createMarker(range);
            }
            return updatedMarkers.push(marker);
          };
        })(this));
        for (id in markersToRemoveById) {
          marker = markersToRemoveById[id];
          marker.destroy();
        }
        this.markers = updatedMarkers;
        this.emitter.emit('did-update', _.clone(this.markers));
        return this.setCurrentMarkerFromSelection();
      } catch (_error) {
        e = _error;
        this.destroyAllMarkers();
        return this.emitter.emit('did-error', e);
      }
    };

    FindModel.prototype.setCurrentMarkerFromSelection = function() {
      var marker, _ref1, _ref2;
      marker = null;
      if (this.editor != null) {
        marker = this.findMarker(this.editor.getSelectedBufferRange());
      }
      if (marker === this.currentResultMarker) {
        return;
      }
      if (this.currentResultMarker != null) {
        if ((_ref1 = this.decorationsByMarkerId[this.currentResultMarker.id]) != null) {
          _ref1.setProperties({
            type: 'highlight',
            "class": this.constructor.markerClass
          });
        }
      }
      this.currentResultMarker = null;
      if (marker && !marker.isDestroyed()) {
        if ((_ref2 = this.decorationsByMarkerId[marker.id]) != null) {
          _ref2.setProperties({
            type: 'highlight',
            "class": 'current-result'
          });
        }
        this.currentResultMarker = marker;
      }
      return this.emitter.emit('did-change-current-result', this.currentResultMarker);
    };

    FindModel.prototype.findMarker = function(range) {
      var attributes;
      if ((this.markers != null) && this.markers.length) {
        attributes = {
          "class": this.constructor.markerClass,
          startPosition: range.start,
          endPosition: range.end
        };
        return _.find(this.editor.findMarkers(attributes), function(marker) {
          return marker.isValid();
        });
      }
    };

    FindModel.prototype.createMarker = function(range) {
      var decoration, marker, markerAttributes;
      markerAttributes = {
        "class": this.constructor.markerClass,
        invalidate: 'inside',
        replicate: false,
        persistent: false,
        isCurrent: false
      };
      marker = this.editor.markBufferRange(range, markerAttributes);
      if (this.editor.decorateMarker != null) {
        decoration = this.editor.decorateMarker(marker, {
          type: 'highlight',
          "class": this.constructor.markerClass
        });
        this.decorationsByMarkerId[marker.id] = decoration;
      }
      return marker;
    };

    FindModel.prototype.destroyAllMarkers = function() {
      var marker, _i, _len, _ref1, _ref2;
      this.valid = false;
      _ref2 = (_ref1 = this.markers) != null ? _ref1 : [];
      for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
        marker = _ref2[_i];
        marker.destroy();
      }
      this.markers = [];
      this.decorationsByMarkerId = {};
      this.currentResultMarker = null;
      this.emitter.emit('did-update', _.clone(this.markers));
      return this.setCurrentMarkerFromSelection();
    };

    FindModel.prototype.getEditor = function() {
      return this.editor;
    };

    FindModel.prototype.getRegex = function() {
      var expression, flags;
      flags = 'g';
      if (!this.caseSensitive) {
        flags += 'i';
      }
      if (this.useRegex) {
        expression = this.pattern;
      } else {
        expression = _.escapeRegExp(this.pattern);
      }
      if (this.wholeWord) {
        expression = "\\b" + expression + "\\b";
      }
      return new RegExp(expression, flags);
    };

    return FindModel;

  })();

}).call(this);
