(function() {
  var ChunkedExecutor, ChunkedLineReader, EventEmitter, LINE_END_REGEX, MAX_LINE_LENGTH, PathSearcher, TRAILING_LINE_END_REGEX, WORD_BREAK_REGEX, fs, os, _,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  _ = require("underscore");

  fs = require("fs");

  os = require("os");

  EventEmitter = require("events").EventEmitter;

  ChunkedExecutor = require("./chunked-executor");

  ChunkedLineReader = require("./chunked-line-reader");

  MAX_LINE_LENGTH = 100;

  WORD_BREAK_REGEX = /[ \r\n\t;:?=&\/]/;

  LINE_END_REGEX = /\r\n|\n|\r/;

  TRAILING_LINE_END_REGEX = /\r?\n?$/;

  module.exports = PathSearcher = (function(_super) {
    __extends(PathSearcher, _super);

    function PathSearcher(_arg) {
      var _ref;
      _ref = _arg != null ? _arg : {}, this.maxLineLength = _ref.maxLineLength, this.wordBreakRegex = _ref.wordBreakRegex;
      if (this.maxLineLength == null) {
        this.maxLineLength = MAX_LINE_LENGTH;
      }
      if (this.wordBreakRegex == null) {
        this.wordBreakRegex = WORD_BREAK_REGEX;
      }
    }

    /*
    Section: Searching
    */


    PathSearcher.prototype.searchPaths = function(regex, paths, doneCallback) {
      var errors, results, searchPath,
        _this = this;
      errors = null;
      results = null;
      searchPath = function(filePath, pathCallback) {
        return _this.searchPath(regex, filePath, function(pathResult, error) {
          if (pathResult) {
            if (results == null) {
              results = [];
            }
            results.push(pathResult);
          }
          if (error) {
            if (errors == null) {
              errors = [];
            }
            errors.push(error);
          }
          return pathCallback();
        });
      };
      return new ChunkedExecutor(paths, searchPath).execute(function() {
        return doneCallback(results, errors);
      });
    };

    PathSearcher.prototype.searchPath = function(regex, filePath, doneCallback) {
      var e, error, lineNumber, matches, reader,
        _this = this;
      matches = null;
      lineNumber = 0;
      reader = new ChunkedLineReader(filePath);
      error = null;
      reader.on('end', function() {
        var output;
        if (matches != null ? matches.length : void 0) {
          output = {
            filePath: filePath,
            matches: matches
          };
          _this.emit('results-found', output);
        } else {
          _this.emit('results-not-found', filePath);
        }
        return doneCallback(output, error);
      });
      try {
        reader.on('data', function(chunk) {
          var line, lineMatches, lines, match, _i, _len, _results;
          lines = chunk.toString().replace(TRAILING_LINE_END_REGEX, '').split(LINE_END_REGEX);
          _results = [];
          for (_i = 0, _len = lines.length; _i < _len; _i++) {
            line = lines[_i];
            lineMatches = _this.searchLine(regex, line, lineNumber++);
            if (lineMatches != null) {
              if (matches == null) {
                matches = [];
              }
              _results.push((function() {
                var _j, _len1, _results1;
                _results1 = [];
                for (_j = 0, _len1 = lineMatches.length; _j < _len1; _j++) {
                  match = lineMatches[_j];
                  _results1.push(matches.push(match));
                }
                return _results1;
              })());
            } else {
              _results.push(void 0);
            }
          }
          return _results;
        });
      } catch (_error) {
        e = _error;
        error = e;
        this.emit('file-error', e);
      }
    };

    PathSearcher.prototype.searchLine = function(regex, line, lineNumber) {
      var lineText, lineTextEndOffset, lineTextLength, lineTextOffset, matchEndIndex, matchIndex, matchLength, matchText, matches, range;
      matches = null;
      lineTextOffset = 0;
      while (regex.test(line)) {
        lineTextOffset = 0;
        lineTextLength = line.length;
        matchText = RegExp.lastMatch;
        matchLength = matchText.length;
        matchIndex = regex.lastIndex - matchLength;
        matchEndIndex = regex.lastIndex;
        if (lineTextLength < this.maxLineLength) {
          lineText = line;
        } else {
          lineTextOffset = Math.round(matchIndex - (this.maxLineLength - matchLength) / 2);
          lineTextEndOffset = lineTextOffset + this.maxLineLength;
          if (lineTextOffset <= 0) {
            lineTextOffset = 0;
            lineTextEndOffset = this.maxLineLength;
          } else if (lineTextEndOffset > lineTextLength - 2) {
            lineTextEndOffset = lineTextLength - 1;
            lineTextOffset = lineTextEndOffset - this.maxLineLength;
          }
          lineTextOffset = this.findWordBreak(line, lineTextOffset, -1);
          lineTextEndOffset = this.findWordBreak(line, lineTextEndOffset, 1) + 1;
          lineTextLength = lineTextEndOffset - lineTextOffset;
          lineText = line.substr(lineTextOffset, lineTextLength);
        }
        range = [[lineNumber, matchIndex], [lineNumber, matchEndIndex]];
        if (matches == null) {
          matches = [];
        }
        matches.push({
          matchText: matchText,
          lineText: lineText,
          lineTextOffset: lineTextOffset,
          range: range
        });
      }
      regex.lastIndex = 0;
      return matches;
    };

    PathSearcher.prototype.findWordBreak = function(line, offset, increment) {
      var checkIndex, i, len, maxIndex;
      i = offset;
      len = line.length;
      maxIndex = len - 1;
      while (i < len && i >= 0) {
        checkIndex = i + increment;
        if (this.wordBreakRegex.test(line[checkIndex])) {
          return i;
        }
        i = checkIndex;
      }
      if (i < 0) {
        return 0;
      }
      if (i > maxIndex) {
        return maxIndex;
      }
      return i;
    };

    return PathSearcher;

  })(EventEmitter);

}).call(this);
