(function() {
  var GitUtils, Minimatch, PathFilter, fs, path;

  Minimatch = require('minimatch').Minimatch;

  GitUtils = require('git-utils');

  path = require('path');

  fs = require('fs');

  module.exports = PathFilter = (function() {
    PathFilter.MINIMATCH_OPTIONS = {
      matchBase: true,
      dot: true
    };

    PathFilter.escapeRegExp = function(str) {
      return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1');
    };

    function PathFilter(rootPath, options) {
      var excludeVcsIgnores, exclusions, globalExclusions, includeHidden, inclusions, ref;
      this.rootPath = rootPath;
      if (options == null) {
        options = {};
      }
      includeHidden = options.includeHidden, excludeVcsIgnores = options.excludeVcsIgnores;
      ref = this.sanitizePaths(options), inclusions = ref.inclusions, exclusions = ref.exclusions, globalExclusions = ref.globalExclusions;
      this.inclusions = this.createMatchers(inclusions, {
        deepMatch: true
      });
      this.exclusions = this.createMatchers(exclusions, {
        deepMatch: false
      });
      this.globalExclusions = this.createMatchers(globalExclusions, {
        deepMatch: false,
        disallowDuplicatesFrom: this.inclusions
      });
      if (excludeVcsIgnores) {
        this.repo = GitUtils.open(this.rootPath);
      }
      if (includeHidden !== true) {
        this.excludeHidden();
      }
    }


    /*
    Section: Testing For Acceptance
     */

    PathFilter.prototype.isFileAccepted = function(filepath) {
      return this.isDirectoryAccepted(filepath) && !this.isPathExcluded('file', filepath) && this.isPathIncluded('file', filepath) && !this.isPathGloballyExcluded('file', filepath);
    };

    PathFilter.prototype.isDirectoryAccepted = function(filepath) {
      var matchingGlobalExclusions, matchingInclusions, ref;
      if (this.isPathExcluded('directory', filepath) === true) {
        return false;
      }
      matchingInclusions = this.getMatchingItems(this.inclusions['directory'], filepath);
      matchingGlobalExclusions = this.overrideGlobalExclusions(this.getMatchingItems(this.globalExclusions['directory'], filepath), matchingInclusions);
      if (matchingGlobalExclusions.length) {
        return false;
      }
      if (matchingInclusions.length) {
        return true;
      }
      if ((ref = this.inclusions['directory']) != null ? ref.length : void 0) {
        return false;
      }
      return !this.isPathExcludedByGit(filepath);
    };


    /*
    Section: Private Methods
     */

    PathFilter.prototype.isPathIncluded = function(fileOrDirectory, filepath) {
      var ref, ref1, stopAfterFirst;
      if (!((ref = this.inclusions[fileOrDirectory]) != null ? ref.length : void 0)) {
        return true;
      }
      return ((ref1 = this.getMatchingItems(this.inclusions[fileOrDirectory], filepath, stopAfterFirst = true)) != null ? ref1.length : void 0) > 0;
    };

    PathFilter.prototype.isPathExcluded = function(fileOrDirectory, filepath) {
      var ref, stopAfterFirst;
      return ((ref = this.getMatchingItems(this.exclusions[fileOrDirectory], filepath, stopAfterFirst = true)) != null ? ref.length : void 0) > 0;
    };

    PathFilter.prototype.isPathGloballyExcluded = function(fileOrDirectory, filepath) {
      var ref, stopAfterFirst;
      return ((ref = this.getMatchingItems(this.globalExclusions[fileOrDirectory], filepath, stopAfterFirst = true)) != null ? ref.length : void 0) > 0;
    };

    PathFilter.prototype.getMatchingItems = function(matchers, filepath, stopAfterFirst) {
      var index, result;
      if (stopAfterFirst == null) {
        stopAfterFirst = false;
      }
      index = matchers.length;
      result = [];
      while (index--) {
        if (matchers[index].match(filepath)) {
          result.push(matchers[index]);
          if (stopAfterFirst) {
            return result;
          }
        }
      }
      return result;
    };

    PathFilter.prototype.isPathExcludedByGit = function(filepath) {
      var ref;
      return (ref = this.repo) != null ? ref.isIgnored(this.repo.relativize(path.join(this.rootPath, filepath))) : void 0;
    };

    PathFilter.prototype.overrideGlobalExclusions = function(globalExclusions, inclusions) {
      var exclusionIndex, inclusionIndex, requiresOverride, result;
      result = [];
      exclusionIndex = globalExclusions.length;
      while (exclusionIndex--) {
        inclusionIndex = inclusions.length;
        requiresOverride = false;
        while (inclusionIndex--) {
          if (this.isSubpathMatcher(globalExclusions[exclusionIndex], inclusions[inclusionIndex])) {
            requiresOverride = true;
          }
        }
        if (!requiresOverride) {
          result.push(globalExclusions[exclusionIndex]);
        }
      }
      return result;
    };

    PathFilter.prototype.isSubpathMatcher = function(parent, child) {
      var directoryPattern, matchIndex, parentPattern;
      parentPattern = parent.pattern;
      directoryPattern = RegExp(('\\' + path.sep) + "\\*$|" + ('\\' + path.sep) + "\\*\\*$");
      matchIndex = parentPattern.search(directoryPattern);
      if (matchIndex > -1) {
        parentPattern = parentPattern.slice(0, matchIndex);
      }
      return child.pattern.substr(0, parentPattern.length) === parentPattern;
    };

    PathFilter.prototype.sanitizePaths = function(options) {
      var includedPath, inclusions, j, len, ref, ref1;
      if (!((ref = options.inclusions) != null ? ref.length : void 0)) {
        return options;
      }
      inclusions = [];
      ref1 = options.inclusions;
      for (j = 0, len = ref1.length; j < len; j++) {
        includedPath = ref1[j];
        if (includedPath && includedPath[0] === '!') {
          if (options.exclusions == null) {
            options.exclusions = [];
          }
          options.exclusions.push(includedPath.slice(1));
        } else if (includedPath) {
          inclusions.push(includedPath);
        }
      }
      options.inclusions = inclusions;
      return options;
    };

    PathFilter.prototype.excludeHidden = function() {
      var matcher;
      matcher = new Minimatch(".*", PathFilter.MINIMATCH_OPTIONS);
      this.exclusions.file.push(matcher);
      return this.exclusions.directory.push(matcher);
    };

    PathFilter.prototype.createMatchers = function(patterns, arg) {
      var addDirectoryMatcher, addFileMatcher, deepMatch, disallowDuplicatesFrom, e, endsWithSeparatorOrStar, matchers, pattern, r, ref, stat;
      if (patterns == null) {
        patterns = [];
      }
      ref = arg != null ? arg : {}, deepMatch = ref.deepMatch, disallowDuplicatesFrom = ref.disallowDuplicatesFrom;
      addFileMatcher = (function(_this) {
        return function(matchers, pattern) {
          if ((disallowDuplicatesFrom != null) && _this.containsPattern(disallowDuplicatesFrom, 'file', pattern)) {
            return;
          }
          return matchers.file.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS));
        };
      })(this);
      addDirectoryMatcher = (function(_this) {
        return function(matchers, pattern, deepMatch) {
          var deepPath, directoryPattern, i, j, lastIndex, matchIndex, paths, ref1, ref2;
          if (pattern[pattern.length - 1] === path.sep) {
            pattern += '**';
          }
          if (deepMatch) {
            paths = pattern.split(path.sep);
            lastIndex = paths.length - 2;
            if ((ref1 = paths[paths.length - 1]) === '*' || ref1 === '**') {
              lastIndex--;
            }
            if (lastIndex >= 0) {
              deepPath = '';
              for (i = j = 0, ref2 = lastIndex; 0 <= ref2 ? j <= ref2 : j >= ref2; i = 0 <= ref2 ? ++j : --j) {
                deepPath = path.join(deepPath, paths[i]);
                addDirectoryMatcher(matchers, deepPath);
              }
            }
          }
          directoryPattern = RegExp(('\\' + path.sep) + "\\*$|" + ('\\' + path.sep) + "\\*\\*$");
          matchIndex = pattern.search(directoryPattern);
          if (matchIndex > -1) {
            addDirectoryMatcher(matchers, pattern.slice(0, matchIndex));
          }
          if ((disallowDuplicatesFrom != null) && _this.containsPattern(disallowDuplicatesFrom, 'directory', pattern)) {
            return;
          }
          return matchers.directory.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS));
        };
      })(this);
      pattern = null;
      matchers = {
        file: [],
        directory: []
      };
      r = patterns.length;
      while (r--) {
        pattern = patterns[r].trim();
        if (pattern.length === 0 || pattern[0] === '#') {
          continue;
        }
        endsWithSeparatorOrStar = RegExp(('\\' + path.sep) + "$|" + ('\\' + path.sep) + "\\**$");
        if (endsWithSeparatorOrStar.test(pattern)) {
          addDirectoryMatcher(matchers, pattern, deepMatch);
        } else if (pattern.indexOf('*') < 0) {
          try {
            stat = fs.statSync(path.join(this.rootPath, pattern));
          } catch (_error) {
            e = _error;
            stat = null;
          }
          if (stat != null ? stat.isFile() : void 0) {
            addFileMatcher(matchers, pattern);
          } else {
            addDirectoryMatcher(matchers, pattern + path.sep + '**', deepMatch);
          }
        } else {
          addFileMatcher(matchers, pattern);
        }
      }
      return matchers;
    };

    PathFilter.prototype.containsPattern = function(matchers, fileOrDirectory, pattern) {
      var j, len, matcher, ref;
      ref = matchers[fileOrDirectory];
      for (j = 0, len = ref.length; j < len; j++) {
        matcher = ref[j];
        if (matcher.pattern === pattern) {
          return true;
        }
      }
      return false;
    };

    return PathFilter;

  })();

}).call(this);
