/**
 * Service which provides helper methods for downloading a data file from a URL, and extracting
 * its contents as .tar, .tar.gz, or .zip file. Note that this service depends on external
 * library code in the lib/ directory:
 *  - jszip.min.js
 *  - Blob.js
 *  - zlib.js
 */
angular.module('quay').factory('DataFileService', [function() {
  var dataFileService = {};

  dataFileService.getName_ = function(filePath) {
    var parts = filePath.split('/');
    return parts[parts.length - 1];
  };

  dataFileService.tryAsZip_ = function(buf, success, failure) {
    var zip = null;
    var zipFiles = null;
    try {
      var zip = new JSZip(buf);
      zipFiles = zip.files;
    } catch (e) {
      failure();
      return;
    }

    var files = [];
    for (var filePath in zipFiles) {
      if (zipFiles.hasOwnProperty(filePath)) {
        files.push({
          'name': dataFileService.getName_(filePath),
          'path': filePath,
          'canRead': true,
          'toBlob': (function(fp) {
            return function() {
              return new Blob([zip.file(fp).asArrayBuffer()]);
            };
          }(filePath))
        });
      }
    }

    success(files);
  };

  dataFileService.tryAsTarGz_ = function(buf, success, failure) {
    var gunzip = new Zlib.Gunzip(new Uint8Array(buf));
    var plain = null;

    try {
      plain = gunzip.decompress();
    } catch (e) {
      failure();
      return;
    }

    if (plain.byteLength == 0) {
      plain = buf;
    }

    dataFileService.tryAsTar_(plain, success, failure);
  };

  dataFileService.tryAsTar_ = function(buf, success, failure) {
    var collapsePath = function(originalPath) {
      // Tar files can contain entries of the form './', so we need to collapse
      // those paths down.
      var parts = originalPath.split('/');
      for (var i = parts.length - 1; i >= 0; i--) {
        var part = parts[i];
        if (part == '.') {
          parts.splice(i, 1);
        }
      }
      return parts.join('/');
    };

    var handler = new Untar(new Uint8Array(buf));
    handler.process(function(status, read, files, err) {
      switch (status) {
        case 'error':
          failure(err);
          break;

        case 'done':
            var processed = [];
            for (var i = 0; i < files.length; ++i) {
              var currentFile = files[i];
              var path = collapsePath(currentFile.meta.filename);

              if (path == '' || path == 'pax_global_header') { continue; }

              processed.push({
                'name': dataFileService.getName_(path),
                'path': path,
                'canRead': true,
                'toBlob': (function(currentFile) {
                  return function() {
                    return new Blob([currentFile.buffer], {type: 'application/octet-binary'});
                  };
                }(currentFile))
              });
            }
            success(processed);
            break;
      }
    });
  };

  dataFileService.blobToString = function(blob, callback) {
    var reader = new FileReader();
    reader.onload = function(event){
      callback(reader.result);
    };
    reader.readAsText(blob);
  };

  dataFileService.arrayToString = function(buf, callback) {
    var bb = new Blob([buf], {type: 'application/octet-binary'});
    var f = new FileReader();
    f.onload = function(e) {
      callback(e.target.result);
    };
    f.onerror = function(e) {
      callback(null);
    };
    f.onabort = function(e) {
      callback(null);
    };
    f.readAsText(bb);
  };

  dataFileService.readDataArrayAsPossibleArchive = function(buf, success, failure) {
    dataFileService.tryAsZip_(buf, success, function() {
      dataFileService.tryAsTarGz_(buf, success, function() {
        dataFileService.tryAsTar_(buf, success, failure);
      });
    });
  };

  dataFileService.downloadDataFileAsArrayBuffer = function($scope, url, progress, error, loaded) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    request.onprogress = function(e) {
      $scope.$apply(function() {
        var percentLoaded;
        if (e.lengthComputable) {
          progress(e.loaded / e.total);
        }
      });
    };

    request.onerror = function() {
      $scope.$apply(function() {
        error();
      });
    };

    request.onload = function() {
      if (this.status == 200) {
        $scope.$apply(function() {
          var uint8array = new Uint8Array(request.response);
          loaded(uint8array);
        });
        return;
      }
    };

    request.send();
  };

  return dataFileService;
}]);