text: "use strict";
  var fs_extra_1 = require('fs-extra');
  var path_1 = require('path');
  var logger_1 = require('./logger/logger');
  var config_1 = require('./util/config');
  var Constants = require('./util/constants');
  var events_1 = require('./util/events');
  var glob_util_1 = require('./util/glob-util');
  var helpers_1 = require('./util/helpers');
  var watch_1 = require('./watch');
  var copyFilePathCache = new Map();
  var FILTER_OUT_DIRS_FOR_CLEAN = ['{{WWW}}', '{{BUILD}}'];
  function copy(context, configFile) {
      configFile = config_1.getUserConfigFile(context, exports.taskInfo, configFile);
      var logger = new logger_1.Logger('copy');
      return copyWorker(context, configFile)
          .then(function () {
          logger.finish();
      })
          .catch(function (err) {
          throw logger.fail(err);
      });
  }
  exports.copy = copy;
  function copyWorker(context, configFile) {
      var copyConfig = config_1.fillConfigDefaults(configFile, exports.taskInfo.defaultConfigFile);
      var keys = Object.keys(copyConfig);
      var directoriesToCreate = new Set();
      var toCopyList = [];
      return Promise.resolve().then(function () {
          // for each entry, make sure each glob in the list of globs has had string replacement performed on it
          cleanConfigContent(keys, copyConfig, context);
          return getFilesPathsForConfig(keys, copyConfig);
      }).then(function (resultMap) {
          // sweet, we have the absolute path of the files in the glob, and the ability to get the relative path
          // basically, we've got a stew goin'
          return populateFileAndDirectoryInfo(resultMap, copyConfig, toCopyList, directoriesToCreate);
      }).then(function () {
          if (helpers_1.getBooleanPropertyValue(Constants.ENV_CLEAN_BEFORE_COPY)) {
              cleanDirectories(context, directoriesToCreate);
          }
      }).then(function () {
          // create the directories synchronously to avoid any disk locking issues
          var directoryPathList = Array.from(directoriesToCreate);
          for (var _i = 0, directoryPathList_1 = directoryPathList; _i < directoryPathList_1.length; _i++) {
              var directoryPath = directoryPathList_1[_i];
              fs_extra_1.mkdirpSync(directoryPath);
          }
      }).then(function () {
          // sweet, the directories are created, so now let's stream the files
          var promises = [];
          var _loop_1 = function(file) {
              cacheCopyData(file);
              var promise = helpers_1.copyFileAsync(file.absoluteSourcePath, file.absoluteDestPath);
              promise.then(function () {
                  logger_1.Logger.debug("Successfully copied " + file.absoluteSourcePath + " to " + file.absoluteDestPath);
              }).catch(function (err) {
                  logger_1.Logger.warn("Failed to copy " + file.absoluteSourcePath + " to " + file.absoluteDestPath);
              });
              promises.push(promise);
          };
          for (var _i = 0, toCopyList_1 = toCopyList; _i < toCopyList_1.length; _i++) {
              var file = toCopyList_1[_i];
              _loop_1(file);
          }
          return Promise.all(promises);
      });
  }
  exports.copyWorker = copyWorker;
  function copyUpdate(changedFiles, context) {
      var logger = new logger_1.Logger('copy update');
      var configFile = config_1.getUserConfigFile(context, exports.taskInfo, null);
      var copyConfig = config_1.fillConfigDefaults(configFile, exports.taskInfo.defaultConfigFile);
      var keys = Object.keys(copyConfig);
      var directoriesToCreate = new Set();
      var toCopyList = [];
      return Promise.resolve().then(function () {
          changedFiles.forEach(function (changedFile) { return logger_1.Logger.debug("copyUpdate, event: " + changedFile.event + ", path: " + changedFile.filePath); });
          // for each entry, make sure each glob in the list of globs has had string replacement performed on it
          cleanConfigContent(keys, copyConfig, context);
          return getFilesPathsForConfig(keys, copyConfig);
      }).then(function (resultMap) {
          // sweet, we have the absolute path of the files in the glob, and the ability to get the relative path
          // basically, we've got a stew goin'
          return populateFileAndDirectoryInfo(resultMap, copyConfig, toCopyList, directoriesToCreate);
      }).then(function () {
          // first, process any deleted directories
          var promises = [];
          var directoryDeletions = changedFiles.filter(function (changedFile) { return changedFile.event === 'unlinkDir'; });
          directoryDeletions.forEach(function (changedFile) { return promises.push(processRemoveDir(changedFile)); });
          return Promise.all(promises);
      }).then(function () {
          // process any deleted files
          var promises = [];
          var fileDeletions = changedFiles.filter(function (changedFile) { return changedFile.event === 'unlink'; });
          fileDeletions.forEach(function (changedFile) { return promises.push(processRemoveFile(changedFile)); });
          return Promise.all(promises);
      }).then(function () {
          var promises = [];
          var additions = changedFiles.filter(function (changedFile) { return changedFile.event === 'change' || changedFile.event === 'add' || changedFile.event === 'addDir'; });
          additions.forEach(function (changedFile) {
              var matchingItems = toCopyList.filter(function (toCopyEntry) { return toCopyEntry.absoluteSourcePath === changedFile.filePath; });
              matchingItems.forEach(function (matchingItem) {
                  // create the directories first (if needed)
                  fs_extra_1.mkdirpSync(path_1.dirname(matchingItem.absoluteDestPath));
                  // cache the data and copy the files
                  cacheCopyData(matchingItem);
                  promises.push(helpers_1.copyFileAsync(matchingItem.absoluteSourcePath, matchingItem.absoluteDestPath));
                  events_1.emit(events_1.EventType.FileChange, additions);
              });
          });
          return Promise.all(promises);
      }).then(function () {
          logger.finish();
      }).catch(function (err) {
          throw logger.fail(err);
      });
  }
  exports.copyUpdate = copyUpdate;
  function cleanDirectories(context, directoriesToCreate) {
      var filterOut = config_1.replacePathVars(context, FILTER_OUT_DIRS_FOR_CLEAN);
      var directoryPathList = Array.from(directoriesToCreate);
      // filter out any directories that we don't want to allow a clean on
      var cleanableDirectories = directoryPathList.filter(function (directoryPath) {
          for (var _i = 0, filterOut_1 = filterOut; _i < filterOut_1.length; _i++) {
              var uncleanableDir = filterOut_1[_i];
              if (uncleanableDir === directoryPath) {
                  return false;
              }
          }
          return true;
      });
      return deleteDirectories(cleanableDirectories);
  }
  function deleteDirectories(directoryPaths) {
      var promises = [];
      for (var _i = 0, directoryPaths_1 = directoryPaths; _i < directoryPaths_1.length; _i++) {
          var directoryPath = directoryPaths_1[_i];
          promises.push(helpers_1.rimRafAsync(directoryPath));
      }
      return Promise.all(promises);
  }
  function processRemoveFile(changedFile) {
      // delete any destination files that match the source file
      var list = copyFilePathCache.get(changedFile.filePath) || [];
      copyFilePathCache.delete(changedFile.filePath);
      var promises = [];
      var deletedFilePaths = [];
      list.forEach(function (copiedFile) {
          var promise = helpers_1.unlinkAsync(copiedFile.absoluteDestPath);
          promises.push(promise);
          promise.catch(function (err) {
              if (err && err.message && err.message.indexOf('ENOENT') >= 0) {
                  logger_1.Logger.warn("Failed to delete " + copiedFile.absoluteDestPath + " because it doesn't exist");
                  return;
              }
              throw err;
          });
          deletedFilePaths.push(copiedFile.absoluteDestPath);
      });
      events_1.emit(events_1.EventType.FileDelete, deletedFilePaths);
      return Promise.all(promises).catch(function (err) {
      });
  }
  function processRemoveDir(changedFile) {
      // remove any files from the cache where the dirname equals the provided path
      var keysToRemove = [];
      var directoriesToRemove = new Set();
      copyFilePathCache.forEach(function (copiedFiles, filePath) {
          if (path_1.dirname(filePath) === changedFile.filePath) {
              keysToRemove.push(filePath);
              copiedFiles.forEach(function (copiedFile) { return directoriesToRemove.add(path_1.dirname(copiedFile.absoluteDestPath)); });
          }
      });
      keysToRemove.forEach(function (keyToRemove) { return copyFilePathCache.delete(keyToRemove); });
      // the entries are removed from the cache, now just rim raf the directoryPath
      var promises = [];
      directoriesToRemove.forEach(function (directoryToRemove) {
          promises.push(helpers_1.rimRafAsync(directoryToRemove));
      });
      events_1.emit(events_1.EventType.DirectoryDelete, Array.from(directoriesToRemove));
      return Promise.all(promises);
  }
  function cacheCopyData(copyObject) {
      var list = copyFilePathCache.get(copyObject.absoluteSourcePath);
      if (!list) {
          list = [];
      }
      list.push(copyObject);
      copyFilePathCache.set(copyObject.absoluteSourcePath, list);
  }
  function getFilesPathsForConfig(copyConfigKeys, copyConfig) {
      // execute the glob functions to determine what files match each glob
      var srcToResultsMap = new Map();
      var promises = [];
      copyConfigKeys.forEach(function (key) {
          var copyOptions = copyConfig[key];
          if (copyOptions && copyOptions.src) {
              var promise = glob_util_1.globAll(copyOptions.src);
              promises.push(promise);
              promise.then(function (globResultList) {
                  srcToResultsMap.set(key, globResultList);
              });
          }
      });
      return Promise.all(promises).then(function () {
          return srcToResultsMap;
      });
  }
  function populateFileAndDirectoryInfo(resultMap, copyConfig, toCopyList, directoriesToCreate) {
      resultMap.forEach(function (globResults, dictionaryKey) {
          globResults.forEach(function (globResult) {
              // get the relative path from the of each file from the glob
              var relativePath = path_1.relative(globResult.base, globResult.absolutePath);
              // append the relative path to the dest
              var destFileName = path_1.resolve(path_1.join(copyConfig[dictionaryKey].dest, relativePath));
              // store the file information
              toCopyList.push({
                  absoluteSourcePath: globResult.absolutePath,
                  absoluteDestPath: destFileName
              });
              var directoryToCreate = path_1.dirname(destFileName);
              directoriesToCreate.add(directoryToCreate);
          });
      });
  }
  function cleanConfigContent(dictionaryKeys, copyConfig, context) {
      dictionaryKeys.forEach(function (key) {
          var copyOption = copyConfig[key];
          if (copyOption && copyOption.src && copyOption.src.length) {
              var cleanedUpSrc = config_1.replacePathVars(context, copyOption.src);
              copyOption.src = cleanedUpSrc;
              var cleanedUpDest = config_1.replacePathVars(context, copyOption.dest);
              copyOption.dest = cleanedUpDest;
          }
      });
  }
  function copyConfigToWatchConfig(context) {
      if (!context) {
          context = config_1.generateContext(context);
      }
      var configFile = config_1.getUserConfigFile(context, exports.taskInfo, '');
      var copyConfig = config_1.fillConfigDefaults(configFile, exports.taskInfo.defaultConfigFile);
      var results = [];
      for (var _i = 0, _a = Object.keys(copyConfig); _i < _a.length; _i++) {
          var key = _a[_i];
          if (copyConfig[key] && copyConfig[key].src) {
              var list = glob_util_1.generateGlobTasks(copyConfig[key].src, {});
              results = results.concat(list);
          }
      }
      var paths = [];
      var ignored = [];
      for (var _b = 0, results_1 = results; _b < results_1.length; _b++) {
          var result = results_1[_b];
          paths.push(result.pattern);
          if (result.opts && result.opts.ignore) {
              ignored = ignored.concat(result.opts.ignore);
          }
      }
      return {
          paths: paths,
          options: {
              ignored: ignored
          },
          callback: watch_1.copyUpdate
      };
  }
  exports.copyConfigToWatchConfig = copyConfigToWatchConfig;
  exports.taskInfo = {
      fullArg: '--copy',
      shortArg: '-y',
      envVar: 'IONIC_COPY',
      packageConfig: 'ionic_copy',
      defaultConfigFile: 'copy.config'
  };
  ;
  