(function() {
  var CompositeDisposable, Directory, Emitter, File, NaturalSort, PathWatcher, fs, path, realpathCache, repoForPath, _, _ref;

  path = require('path');

  _ = require('underscore-plus');

  _ref = require('event-kit'), CompositeDisposable = _ref.CompositeDisposable, Emitter = _ref.Emitter;

  fs = require('fs-plus');

  PathWatcher = require('pathwatcher');

  NaturalSort = require('javascript-natural-sort');

  File = require('./file');

  repoForPath = require('./helpers').repoForPath;

  realpathCache = {};

  module.exports = Directory = (function() {
    function Directory(_arg) {
      var fullPath, _base, _base1, _ref1;
      this.name = _arg.name, fullPath = _arg.fullPath, this.symlink = _arg.symlink, this.expansionState = _arg.expansionState, this.isRoot = _arg.isRoot, this.ignoredPatterns = _arg.ignoredPatterns;
      this.emitter = new Emitter();
      this.subscriptions = new CompositeDisposable();
      this.path = fullPath;
      this.realPath = this.path;
      if (fs.isCaseInsensitive()) {
        this.lowerCasePath = this.path.toLowerCase();
        this.lowerCaseRealPath = this.lowerCasePath;
      }
      if (this.isRoot == null) {
        this.isRoot = false;
      }
      if (this.expansionState == null) {
        this.expansionState = {};
      }
      if ((_base = this.expansionState).isExpanded == null) {
        _base.isExpanded = false;
      }
      if ((_base1 = this.expansionState).entries == null) {
        _base1.entries = {};
      }
      this.status = null;
      this.entries = {};
      this.submodule = (_ref1 = repoForPath(this.path)) != null ? _ref1.isSubmodule(this.path) : void 0;
      this.subscribeToRepo();
      this.updateStatus();
      this.loadRealPath();
    }

    Directory.prototype.destroy = function() {
      this.unwatch();
      this.subscriptions.dispose();
      return this.emitter.emit('did-destroy');
    };

    Directory.prototype.onDidDestroy = function(callback) {
      return this.emitter.on('did-destroy', callback);
    };

    Directory.prototype.onDidStatusChange = function(callback) {
      return this.emitter.on('did-status-change', callback);
    };

    Directory.prototype.onDidAddEntries = function(callback) {
      return this.emitter.on('did-add-entries', callback);
    };

    Directory.prototype.onDidRemoveEntries = function(callback) {
      return this.emitter.on('did-remove-entries', callback);
    };

    Directory.prototype.loadRealPath = function() {
      return fs.realpath(this.path, realpathCache, (function(_this) {
        return function(error, realPath) {
          if (realPath && realPath !== _this.path) {
            _this.realPath = realPath;
            if (fs.isCaseInsensitive()) {
              _this.lowerCaseRealPath = _this.realPath.toLowerCase();
            }
            return _this.updateStatus();
          }
        };
      })(this));
    };

    Directory.prototype.subscribeToRepo = function() {
      var repo;
      repo = repoForPath(this.path);
      if (repo == null) {
        return;
      }
      this.subscriptions.add(repo.onDidChangeStatus((function(_this) {
        return function(event) {
          if (event.path.indexOf("" + _this.path + path.sep) === 0) {
            return _this.updateStatus(repo);
          }
        };
      })(this)));
      return this.subscriptions.add(repo.onDidChangeStatuses((function(_this) {
        return function() {
          return _this.updateStatus(repo);
        };
      })(this)));
    };

    Directory.prototype.updateStatus = function() {
      var newStatus, repo, status;
      repo = repoForPath(this.path);
      if (repo == null) {
        return;
      }
      newStatus = null;
      if (repo.isPathIgnored(this.path)) {
        newStatus = 'ignored';
      } else {
        status = repo.getDirectoryStatus(this.path);
        if (repo.isStatusModified(status)) {
          newStatus = 'modified';
        } else if (repo.isStatusNew(status)) {
          newStatus = 'added';
        }
      }
      if (newStatus !== this.status) {
        this.status = newStatus;
        return this.emitter.emit('did-status-change', newStatus);
      }
    };

    Directory.prototype.isPathIgnored = function(filePath) {
      var ignoredPattern, repo, _i, _len, _ref1;
      if (atom.config.get('tree-view.hideVcsIgnoredFiles')) {
        repo = repoForPath(this.path);
        if ((repo != null) && repo.isProjectAtRoot() && repo.isPathIgnored(filePath)) {
          return true;
        }
      }
      if (atom.config.get('tree-view.hideIgnoredNames')) {
        _ref1 = this.ignoredPatterns;
        for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
          ignoredPattern = _ref1[_i];
          if (ignoredPattern.match(filePath)) {
            return true;
          }
        }
      }
      return false;
    };

    Directory.prototype.isPathPrefixOf = function(prefix, fullPath) {
      return fullPath.indexOf(prefix) === 0 && fullPath[prefix.length] === path.sep;
    };

    Directory.prototype.isPathEqual = function(pathToCompare) {
      return this.path === pathToCompare || this.realPath === pathToCompare;
    };

    Directory.prototype.contains = function(pathToCheck) {
      var directoryPath;
      if (!pathToCheck) {
        return false;
      }
      if (process.platform === 'win32') {
        pathToCheck = pathToCheck.replace(/\//g, '\\');
      }
      if (fs.isCaseInsensitive()) {
        directoryPath = this.lowerCasePath;
        pathToCheck = pathToCheck.toLowerCase();
      } else {
        directoryPath = this.path;
      }
      if (this.isPathPrefixOf(directoryPath, pathToCheck)) {
        return true;
      }
      if (this.realPath !== this.path) {
        if (fs.isCaseInsensitive()) {
          directoryPath = this.lowerCaseRealPath;
        } else {
          directoryPath = this.realPath;
        }
        return this.isPathPrefixOf(directoryPath, pathToCheck);
      }
      return false;
    };

    Directory.prototype.unwatch = function() {
      var entry, key, _ref1, _results;
      if (this.watchSubscription != null) {
        this.watchSubscription.close();
        this.watchSubscription = null;
      }
      _ref1 = this.entries;
      _results = [];
      for (key in _ref1) {
        entry = _ref1[key];
        entry.destroy();
        _results.push(delete this.entries[key]);
      }
      return _results;
    };

    Directory.prototype.watch = function() {
      try {
        return this.watchSubscription != null ? this.watchSubscription : this.watchSubscription = PathWatcher.watch(this.path, (function(_this) {
          return function(eventType) {
            switch (eventType) {
              case 'change':
                return _this.reload();
              case 'delete':
                return _this.destroy();
            }
          };
        })(this));
      } catch (_error) {}
    };

    Directory.prototype.getEntries = function() {
      var directories, error, expansionState, files, fullPath, name, names, stat, symlink, _i, _len;
      try {
        names = fs.readdirSync(this.path);
      } catch (_error) {
        error = _error;
        names = [];
      }
      NaturalSort.insensitive = true;
      names.sort(NaturalSort);
      files = [];
      directories = [];
      for (_i = 0, _len = names.length; _i < _len; _i++) {
        name = names[_i];
        fullPath = path.join(this.path, name);
        if (this.isPathIgnored(fullPath)) {
          continue;
        }
        stat = fs.lstatSyncNoException(fullPath);
        symlink = typeof stat.isSymbolicLink === "function" ? stat.isSymbolicLink() : void 0;
        if (symlink) {
          stat = fs.statSyncNoException(fullPath);
        }
        if (typeof stat.isDirectory === "function" ? stat.isDirectory() : void 0) {
          if (this.entries.hasOwnProperty(name)) {
            directories.push(name);
          } else {
            expansionState = this.expansionState.entries[name];
            directories.push(new Directory({
              name: name,
              fullPath: fullPath,
              symlink: symlink,
              expansionState: expansionState,
              ignoredPatterns: this.ignoredPatterns
            }));
          }
        } else if (typeof stat.isFile === "function" ? stat.isFile() : void 0) {
          if (this.entries.hasOwnProperty(name)) {
            files.push(name);
          } else {
            files.push(new File({
              name: name,
              fullPath: fullPath,
              symlink: symlink,
              realpathCache: realpathCache
            }));
          }
        }
      }
      return this.sortEntries(directories.concat(files));
    };

    Directory.prototype.normalizeEntryName = function(value) {
      var normalizedValue;
      normalizedValue = value.name;
      if (normalizedValue == null) {
        normalizedValue = value;
      }
      if (normalizedValue != null) {
        normalizedValue = normalizedValue.toLowerCase();
      }
      return normalizedValue;
    };

    Directory.prototype.sortEntries = function(combinedEntries) {
      if (atom.config.get('tree-view.sortFoldersBeforeFiles')) {
        return combinedEntries;
      } else {
        return combinedEntries.sort((function(_this) {
          return function(first, second) {
            var firstName, secondName;
            firstName = _this.normalizeEntryName(first);
            secondName = _this.normalizeEntryName(second);
            return firstName.localeCompare(secondName);
          };
        })(this));
      }
    };

    Directory.prototype.reload = function() {
      var entriesRemoved, entry, index, name, newEntries, removedEntries, _i, _j, _len, _len1, _ref1;
      newEntries = [];
      removedEntries = _.clone(this.entries);
      index = 0;
      _ref1 = this.getEntries();
      for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
        entry = _ref1[_i];
        if (this.entries.hasOwnProperty(entry)) {
          delete removedEntries[entry];
          index++;
          continue;
        }
        entry.indexInParentDirectory = index;
        index++;
        newEntries.push(entry);
      }
      entriesRemoved = false;
      for (name in removedEntries) {
        entry = removedEntries[name];
        entriesRemoved = true;
        entry.destroy();
        delete this.entries[name];
        delete this.expansionState[name];
      }
      if (entriesRemoved) {
        this.emitter.emit('did-remove-entries', removedEntries);
      }
      if (newEntries.length > 0) {
        for (_j = 0, _len1 = newEntries.length; _j < _len1; _j++) {
          entry = newEntries[_j];
          this.entries[entry.name] = entry;
        }
        return this.emitter.emit('did-add-entries', newEntries);
      }
    };

    Directory.prototype.collapse = function() {
      this.expansionState.isExpanded = false;
      this.expansionState = this.serializeExpansionState();
      return this.unwatch();
    };

    Directory.prototype.expand = function() {
      this.expansionState.isExpanded = true;
      this.reload();
      return this.watch();
    };

    Directory.prototype.serializeExpansionState = function() {
      var entry, expansionState, name, _ref1;
      expansionState = {};
      expansionState.isExpanded = this.expansionState.isExpanded;
      expansionState.entries = {};
      _ref1 = this.entries;
      for (name in _ref1) {
        entry = _ref1[name];
        if (entry.expansionState != null) {
          expansionState.entries[name] = entry.serializeExpansionState();
        }
      }
      return expansionState;
    };

    return Directory;

  })();

}).call(this);
