(function() {
  var ArchiveEntry, convertToTree, findEntryWithName, fs, isGzipPath, isTarPath, isZipPath, listGzip, listTar, listTarStream, listZip, path, readEntry, readFileFromGzip, readFileFromTar, readFileFromTarStream, readFileFromZip, util, wrapCallback;

  fs = require('fs');

  path = require('path');

  util = require('util');

  ArchiveEntry = (function() {
    function ArchiveEntry(path, type) {
      this.path = path;
      this.type = type;
      if (this.isDirectory()) {
        this.children = [];
      }
    }

    ArchiveEntry.prototype.add = function(entry) {
      var child, name, segments;
      if (!this.isParentOf(entry)) {
        return false;
      }
      segments = entry.getPath().substring(this.getPath().length + 1).split(path.sep);
      if (segments.length === 0) {
        return false;
      }
      if (segments.length === 1) {
        this.children.push(entry);
        return true;
      } else {
        name = segments[0];
        child = findEntryWithName(this.children, name);
        if (child == null) {
          child = new ArchiveEntry("" + (this.getPath()) + path.sep + name, 5);
          this.children.push(child);
        }
        if (child.isDirectory()) {
          return child.add(entry);
        } else {
          return false;
        }
      }
    };

    ArchiveEntry.prototype.isParentOf = function(entry) {
      return this.isDirectory() && entry.getPath().indexOf("" + (this.getPath()) + path.sep) === 0;
    };

    ArchiveEntry.prototype.getPath = function() {
      return this.path;
    };

    ArchiveEntry.prototype.getName = function() {
      return this.name != null ? this.name : this.name = path.basename(this.path);
    };

    ArchiveEntry.prototype.isFile = function() {
      return this.type === 0;
    };

    ArchiveEntry.prototype.isDirectory = function() {
      return this.type === 5;
    };

    ArchiveEntry.prototype.isSymbolicLink = function() {
      return this.type === 2;
    };

    ArchiveEntry.prototype.toString = function() {
      return this.getPath();
    };

    return ArchiveEntry;

  })();

  findEntryWithName = function(entries, name) {
    var entry, _i, _len;
    for (_i = 0, _len = entries.length; _i < _len; _i++) {
      entry = entries[_i];
      if (name === entry.getName()) {
        return entry;
      }
    }
  };

  convertToTree = function(entries) {
    var entry, name, parent, rootEntries, segments, _i, _len;
    rootEntries = [];
    for (_i = 0, _len = entries.length; _i < _len; _i++) {
      entry = entries[_i];
      segments = entry.getPath().split(path.sep);
      if (segments.length === 1) {
        rootEntries.push(entry);
      } else {
        name = segments[0];
        parent = findEntryWithName(rootEntries, name);
        if (parent == null) {
          parent = new ArchiveEntry(name, 5);
          rootEntries.push(parent);
        }
        parent.add(entry);
      }
    }
    return rootEntries;
  };

  wrapCallback = function(callback) {
    var called;
    called = false;
    return function(error, data) {
      if (!called) {
        if ((error != null) && !util.isError(error)) {
          error = new Error(error);
        }
        called = true;
        return callback(error, data);
      }
    };
  };

  listZip = function(archivePath, options, callback) {
    var DecompressZip, entries, zipStream;
    DecompressZip = require('decompress-zip');
    entries = [];
    zipStream = new DecompressZip(archivePath);
    zipStream.on('error', callback);
    zipStream.on('list', function(files) {
      var entryPath, entryType, file, _i, _len;
      for (_i = 0, _len = files.length; _i < _len; _i++) {
        file = files[_i];
        if (file.slice(-1) === path.sep) {
          entryPath = file.slice(0, -1);
          entryType = 5;
        } else {
          entryPath = file;
          entryType = 0;
        }
        entries.push(new ArchiveEntry(entryPath, entryType));
      }
      if (options.tree) {
        entries = convertToTree(entries);
      }
      return callback(null, entries);
    });
    return zipStream.list();
  };

  listGzip = function(archivePath, options, callback) {
    var fileStream, gzipStream, zlib;
    zlib = require('zlib');
    fileStream = fs.createReadStream(archivePath);
    fileStream.on('error', callback);
    gzipStream = fileStream.pipe(zlib.createGunzip());
    gzipStream.on('error', callback);
    return listTarStream(gzipStream, options, callback);
  };

  listTar = function(archivePath, options, callback) {
    var fileStream;
    fileStream = fs.createReadStream(archivePath);
    fileStream.on('error', callback);
    return listTarStream(fileStream, options, callback);
  };

  listTarStream = function(inputStream, options, callback) {
    var entries, tarStream;
    entries = [];
    tarStream = inputStream.pipe(require('tar').Parse());
    tarStream.on('error', callback);
    tarStream.on('entry', function(entry) {
      var entryPath, entryType;
      if (entry.props.path.slice(-1) === '/') {
        entryPath = entry.props.path.slice(0, -1);
      } else {
        entryPath = entry.props.path;
      }
      entryType = parseInt(entry.props.type);
      entryPath = entryPath.replace(/\//g, path.sep);
      return entries.push(new ArchiveEntry(entryPath, entryType));
    });
    return tarStream.on('end', function() {
      if (options.tree) {
        entries = convertToTree(entries);
      }
      return callback(null, entries);
    });
  };

  readFileFromZip = function(archivePath, filePath, callback) {
    var DecompressZip, rimraf, tmp;
    DecompressZip = require('decompress-zip');
    rimraf = require('rimraf');
    tmp = require('tmp');
    return tmp.dir(function(error, tempDir) {
      var extractedType, unzipper;
      if (error) {
        return callback(error);
      }
      unzipper = new DecompressZip(archivePath);
      unzipper.on('error', callback);
      extractedType = null;
      unzipper.on('extract', function() {
        switch (extractedType) {
          case 'Directory':
            return callback("" + filePath + " is a folder in the archive: " + archivePath);
          case 'File':
            return fs.readFile(path.join(tempDir, filePath), function(error, contents) {
              rimraf(tempDir, function() {});
              return callback(error, contents);
            });
          default:
            return callback("" + filePath + " does not exist in the archive: " + archivePath);
        }
      });
      return unzipper.extract({
        path: tempDir,
        filter: function(file) {
          if (file.path === filePath) {
            extractedType = file.type;
          }
          return file.type === 'File' && file.path === filePath;
        }
      });
    });
  };

  readFileFromGzip = function(archivePath, filePath, callback) {
    var fileStream, gzipStream;
    fileStream = fs.createReadStream(archivePath);
    fileStream.on('error', callback);
    gzipStream = fileStream.pipe(require('zlib').createGunzip());
    gzipStream.on('error', callback);
    gzipStream.on('end', function() {
      return callback("" + filePath + " does not exist in the archive: " + archivePath);
    });
    return readFileFromTarStream(gzipStream, archivePath, filePath, callback);
  };

  readFileFromTar = function(archivePath, filePath, callback) {
    var fileStream;
    fileStream = fs.createReadStream(archivePath);
    fileStream.on('error', callback);
    fileStream.on('end', function() {
      return callback("" + filePath + " does not exist in the archive: " + archivePath);
    });
    return readFileFromTarStream(fileStream, archivePath, filePath, callback);
  };

  readFileFromTarStream = function(inputStream, archivePath, filePath, callback) {
    var tar, tarStream;
    tar = require('tar');
    tarStream = inputStream.pipe(tar.Parse());
    tarStream.on('error', callback);
    return tarStream.on('entry', function(entry) {
      if (filePath !== entry.props.path.replace(/\//g, path.sep)) {
        return;
      }
      if (entry.props.type === '0') {
        return readEntry(entry, callback);
      } else {
        return callback("" + filePath + " is not a normal file in the archive: " + archivePath);
      }
    });
  };

  readEntry = function(entry, callback) {
    var contents;
    contents = [];
    entry.on('data', function(data) {
      return contents.push(data);
    });
    return entry.on('end', function() {
      return callback(null, Buffer.concat(contents));
    });
  };

  isTarPath = function(archivePath) {
    return path.extname(archivePath) === '.tar';
  };

  isZipPath = function(archivePath) {
    var extension;
    extension = path.extname(archivePath);
    return extension === '.epub' || extension === '.jar' || extension === '.love' || extension === '.war' || extension === '.zip' || extension === '.egg' || extension === '.whl' || extension === '.xpi' || extension === '.nupkg';
  };

  isGzipPath = function(archivePath) {
    return path.extname(archivePath) === '.tgz' || path.extname(path.basename(archivePath, '.gz')) === '.tar';
  };

  module.exports = {
    isPathSupported: function(archivePath) {
      if (!archivePath) {
        return false;
      }
      return isTarPath(archivePath) || isZipPath(archivePath) || isGzipPath(archivePath);
    },
    list: function(archivePath, options, callback) {
      if (options == null) {
        options = {};
      }
      if (typeof options === 'function') {
        callback = options;
        options = {};
      }
      if (isTarPath(archivePath)) {
        listTar(archivePath, options, wrapCallback(callback));
      } else if (isGzipPath(archivePath)) {
        listGzip(archivePath, options, wrapCallback(callback));
      } else if (isZipPath(archivePath)) {
        listZip(archivePath, options, wrapCallback(callback));
      } else {
        callback(new Error("'" + (path.extname(archivePath)) + "' files are not supported"));
      }
      return void 0;
    },
    readFile: function(archivePath, filePath, callback) {
      if (isTarPath(archivePath)) {
        readFileFromTar(archivePath, filePath, wrapCallback(callback));
      } else if (isGzipPath(archivePath)) {
        readFileFromGzip(archivePath, filePath, wrapCallback(callback));
      } else if (isZipPath(archivePath)) {
        readFileFromZip(archivePath, filePath, wrapCallback(callback));
      } else {
        callback(new Error("'" + (path.extname(archivePath)) + "' files are not supported"));
      }
      return void 0;
    },
    readGzip: function(gzipArchivePath, callback) {
      var chunks, fileStream, gzipStream, zlib;
      callback = wrapCallback(callback);
      zlib = require('zlib');
      fileStream = fs.createReadStream(gzipArchivePath);
      fileStream.on('error', callback);
      gzipStream = fileStream.pipe(zlib.createGunzip());
      gzipStream.on('error', callback);
      chunks = [];
      gzipStream.on('data', function(chunk) {
        return chunks.push(chunk);
      });
      return gzipStream.on('end', function() {
        return callback(null, Buffer.concat(chunks));
      });
    }
  };

}).call(this);
