'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _regenerator;

function _load_regenerator() {
  return _regenerator = _interopRequireDefault(require('babel-runtime/regenerator'));
}

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _ramda;

function _load_ramda() {
  return _ramda = _interopRequireDefault(require('ramda'));
}

var _path;

function _load_path() {
  return _path = _interopRequireDefault(require('path'));
}

var _logger;

function _load_logger() {
  return _logger = _interopRequireDefault(require('../../logger/logger'));
}

var _constants;

function _load_constants() {
  return _constants = require('../../constants');
}

var _utils;

function _load_utils() {
  return _utils = require('../../utils');
}

var _;

function _load_() {
  return _ = require('..');
}

var _bitId;

function _load_bitId() {
  return _bitId = require('../../bit-id');
}

var _addComponents;

function _load_addComponents() {
  return _addComponents = _interopRequireDefault(require('../component-ops/add-components'));
}

var _exceptions;

function _load_exceptions() {
  return _exceptions = require('../component-ops/add-components/exceptions');
}

var _generalError;

function _load_generalError() {
  return _generalError = _interopRequireDefault(require('../../error/general-error'));
}

var _validationError;

function _load_validationError() {
  return _validationError = _interopRequireDefault(require('../../error/validation-error'));
}

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ComponentMap = function () {
  // directory shared among a component and its dependencies by the original author. Relevant for IMPORTED only
  // needed for the link process
  // relevant for AUTHORED only when a component was added as a directory, used for tracking changes in that dir
  function ComponentMap(_ref) {
    var files = _ref.files,
        mainFile = _ref.mainFile,
        rootDir = _ref.rootDir,
        trackDir = _ref.trackDir,
        origin = _ref.origin,
        dependencies = _ref.dependencies,
        mainDistFile = _ref.mainDistFile,
        originallySharedDir = _ref.originallySharedDir;

    _classCallCheck(this, ComponentMap);

    this.files = files;
    this.mainFile = mainFile;
    this.rootDir = rootDir;
    this.trackDir = trackDir;
    this.origin = origin;
    this.dependencies = dependencies;
    this.mainDistFile = mainDistFile;
    this.originallySharedDir = originallySharedDir;
  } // needed when there is a build process involved
  // always set for IMPORTED and NESTED.
  // reason why trackDir and not re-use rootDir is because using rootDir requires all paths to be
  // relative to rootDir for consistency, then, when saving into the model changing them back to
  // be relative to consumer-root. (we can't save in the model relative to rootDir, otherwise the
  // dependencies paths won't work).


  _createClass(ComponentMap, [{
    key: '_findFile',
    value: function _findFile(fileName) {
      var _this = this;

      return this.files.find(function (file) {
        var filePath = _this.rootDir ? (0, (_utils || _load_utils()).pathJoinLinux)(_this.rootDir, file.relativePath) : file.relativePath;
        return filePath === fileName;
      });
    }
  }, {
    key: 'updateFileLocation',
    value: function updateFileLocation(fileFrom, fileTo) {
      fileFrom = (0, (_utils || _load_utils()).pathNormalizeToLinux)(fileFrom);
      fileTo = (0, (_utils || _load_utils()).pathNormalizeToLinux)(fileTo);
      var currentFile = this._findFile(fileFrom);
      var changes = [];
      if (currentFile) {
        var _rootDir = this.rootDir;
        var newLocation = _rootDir ? ComponentMap.getPathWithoutRootDir(_rootDir, fileTo) : fileTo;
        (_logger || _load_logger()).default.debug(`updating file location from ${currentFile.relativePath} to ${newLocation}`);
        if (this.mainFile === currentFile.relativePath) this.mainFile = newLocation;
        changes.push({ from: currentFile.relativePath, to: newLocation });
        currentFile.relativePath = newLocation;
        currentFile.name = (_path || _load_path()).default.basename(newLocation);
      }
      this.validate();
      return changes;
    }
  }, {
    key: 'updateDirLocation',
    value: function updateDirLocation(dirFrom, dirTo) {
      var _this2 = this;

      dirFrom = (0, (_utils || _load_utils()).pathNormalizeToLinux)(dirFrom);
      dirTo = (0, (_utils || _load_utils()).pathNormalizeToLinux)(dirTo);
      var changes = [];
      if (this.rootDir && this.rootDir.startsWith(dirFrom)) {
        var _rootDir2 = this.rootDir;
        var newRootDir = _rootDir2.replace(dirFrom, dirTo);
        var newRootDirNormalized = (0, (_utils || _load_utils()).pathNormalizeToLinux)(newRootDir);
        changes.push({ from: _rootDir2, to: newRootDirNormalized });
        (_logger || _load_logger()).default.debug(`updating rootDir location from ${_rootDir2} to ${newRootDirNormalized}`);
        this.rootDir = newRootDirNormalized;
        return changes;
      }
      this.files.forEach(function (file) {
        var filePath = _this2.rootDir ? (_path || _load_path()).default.join(_this2.rootDir, file.relativePath) : file.relativePath;
        if (filePath.startsWith(dirFrom)) {
          var fileTo = filePath.replace(dirFrom, dirTo);
          var newLocation = _this2.rootDir ? ComponentMap.getPathWithoutRootDir(_this2.rootDir, fileTo) : fileTo;
          (_logger || _load_logger()).default.debug(`updating file location from ${file.relativePath} to ${newLocation}`);
          if (_this2.mainFile === file.relativePath) _this2.mainFile = newLocation;
          changes.push({ from: file.relativePath, to: newLocation });
          file.relativePath = newLocation;
        }
      });
      if (this.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED && this.trackDir && this.trackDir === dirFrom) {
        this.trackDir = dirTo;
      }
      this.validate();
      return changes;
    }
  }, {
    key: 'getFilesRelativeToConsumer',
    value: function getFilesRelativeToConsumer() {
      var _this3 = this;

      return this.files.map(function (file) {
        return _this3.rootDir ? (0, (_utils || _load_utils()).pathJoinLinux)(_this3.rootDir, file.relativePath) : file.relativePath;
      });
    }
  }, {
    key: 'getAllFilesPaths',
    value: function getAllFilesPaths() {
      return this.files.map(function (file) {
        return file.relativePath;
      });
    }
  }, {
    key: 'getFilesGroupedByBeingTests',
    value: function getFilesGroupedByBeingTests() {
      var allFiles = [];
      var nonTestsFiles = [];
      var testsFiles = [];
      this.files.forEach(function (file) {
        allFiles.push(file.relativePath);
        if (file.test) testsFiles.push(file.relativePath);else nonTestsFiles.push(file.relativePath);
      });
      return { allFiles, nonTestsFiles, testsFiles };
    }

    /**
     * if one of the added files is outside of the trackDir, remove the trackDir attribute
     */

  }, {
    key: 'removeTrackDirIfNeeded',
    value: function removeTrackDirIfNeeded() {
      if (this.trackDir) {
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = this.files[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var file = _step.value;

            if (!file.relativePath.startsWith(this.trackDir)) {
              this.trackDir = undefined;
              return;
            }
          }
        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator.return) {
              _iterator.return();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }
      }
    }

    /**
     * directory to track for changes (such as files added/renamed)
     */

  }, {
    key: 'getTrackDir',
    value: function getTrackDir() {
      if (this.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) return this.trackDir;
      if (this.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED) return this.rootDir;
      // DO NOT track nested components!
      return null;
    }

    /**
     * in case new files were created in the track-dir directory, add them to the component-map
     * so then they'll be tracked by bitmap
     */

  }, {
    key: 'trackDirectoryChanges',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(consumer, id) {
        var trackDir, trackDirAbsolute, trackDirRelative, addParams, numOfFilesBefore, addComponents;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                trackDir = this.getTrackDir();

                if (!trackDir) {
                  _context.next = 19;
                  break;
                }

                trackDirAbsolute = (_path || _load_path()).default.join(consumer.getPath(), trackDir);
                trackDirRelative = (_path || _load_path()).default.relative(process.cwd(), trackDirAbsolute);
                addParams = {
                  componentPaths: [trackDirRelative || '.'],
                  id: id.toString(),
                  override: false, // this makes sure to not override existing files of componentMap
                  trackDirFeature: true,
                  origin: this.origin
                };
                numOfFilesBefore = this.files.length;
                addComponents = new (_addComponents || _load_addComponents()).default(consumer, addParams);
                _context.prev = 7;
                _context.next = 10;
                return addComponents.add();

              case 10:
                _context.next = 18;
                break;

              case 12:
                _context.prev = 12;
                _context.t0 = _context['catch'](7);

                if (!(_context.t0 instanceof (_exceptions || _load_exceptions()).NoFiles || _context.t0 instanceof (_exceptions || _load_exceptions()).EmptyDirectory)) {
                  _context.next = 17;
                  break;
                }

                _context.next = 18;
                break;

              case 17:
                throw _context.t0;

              case 18:
                if (this.files.length > numOfFilesBefore) {
                  (_logger || _load_logger()).default.info(`new file(s) have been added to .bitmap for ${id.toString()}`);
                  consumer.bitMap.hasChanged = true;
                }

              case 19:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[7, 12]]);
      }));

      function trackDirectoryChanges(_x, _x2) {
        return _ref2.apply(this, arguments);
      }

      return trackDirectoryChanges;
    }()
  }, {
    key: 'removeFiles',
    value: function removeFiles(files) {
      var relativePaths = files.map(function (file) {
        return file.relativePath;
      });
      this.files = this.files.reduce(function (accumulator, file) {
        return relativePaths.includes(file.relativePath) ? accumulator : accumulator.concat(file);
      }, []);
      this.validate();
    }
  }, {
    key: 'sort',
    value: function sort() {
      this.files = (_ramda || _load_ramda()).default.sortBy((_ramda || _load_ramda()).default.prop('relativePath'), this.files);
    }
  }, {
    key: 'validate',
    value: function validate() {
      var _this4 = this;

      var errorMessage = `failed adding or updating a component-map record (of ${(_constants || _load_constants()).BIT_MAP} file).`;
      if (!this.mainFile) throw new (_validationError || _load_validationError()).default(`${errorMessage} mainFile attribute is missing`);
      if (!(0, (_utils || _load_utils()).isValidPath)(this.mainFile)) {
        throw new (_validationError || _load_validationError()).default(`${errorMessage} mainFile attribute ${this.mainFile} is invalid`);
      }
      // if it's an environment component (such as compiler) the rootDir is empty
      if (!this.rootDir && this.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED) {
        throw new (_validationError || _load_validationError()).default(`${errorMessage} rootDir attribute is missing`);
      }
      // $FlowFixMe
      if (this.rootDir && !(0, (_utils || _load_utils()).isValidPath)(this.rootDir)) {
        throw new (_validationError || _load_validationError()).default(`${errorMessage} rootDir attribute ${this.rootDir} is invalid`);
      }
      if (this.rootDir && this.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
        throw new (_validationError || _load_validationError()).default(`${errorMessage} rootDir attribute should not be set for AUTHORED component`);
      }
      if (this.trackDir && this.origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
        throw new (_validationError || _load_validationError()).default(`${errorMessage} trackDir attribute should be set for AUTHORED component only`);
      }
      if (!this.files || !this.files.length) throw new (_validationError || _load_validationError()).default(`${errorMessage} files list is missing`);
      this.files.forEach(function (file) {
        if (!(0, (_utils || _load_utils()).isValidPath)(file.relativePath)) {
          throw new (_validationError || _load_validationError()).default(`${errorMessage} file path ${file.relativePath} is invalid`);
        }
      });
      var foundMainFile = this.files.find(function (file) {
        return file.relativePath === _this4.mainFile;
      });
      if (!foundMainFile || (_ramda || _load_ramda()).default.isEmpty(foundMainFile)) {
        throw new (_validationError || _load_validationError()).default(`${errorMessage} mainFile ${this.mainFile} is not in the files list`);
      }
      var filesPaths = this.files.map(function (file) {
        return file.relativePath;
      });
      var duplicateFiles = filesPaths.filter(function (file) {
        return filesPaths.filter(function (f) {
          return file.toLowerCase() === f.toLowerCase();
        }).length > 1;
      });
      if (duplicateFiles.length) {
        throw new (_validationError || _load_validationError()).default(`${errorMessage} the following files are duplicated ${duplicateFiles.join(', ')}`);
      }
      if (this.trackDir) {
        var _trackDir = this.trackDir;
        this.files.forEach(function (file) {
          if (!file.relativePath.startsWith(_trackDir)) {
            throw new (_validationError || _load_validationError()).default(`${errorMessage} a file path ${file.relativePath} is not in the trackDir ${_trackDir}`);
          }
        });
      }
    }
  }], [{
    key: 'fromJson',
    value: function fromJson(componentMapObj) {
      return new ComponentMap(componentMapObj);
    }
  }, {
    key: 'getPathWithoutRootDir',
    value: function getPathWithoutRootDir(rootDir, filePath) {
      var newPath = (0, (_utils || _load_utils()).pathRelativeLinux)(rootDir, filePath);
      if (newPath.startsWith('..')) {
        // this is forbidden for security reasons. Allowing files to be written outside the components directory may
        // result in overriding OS files.
        throw new (_generalError || _load_generalError()).default(`unable to add file ${filePath} because it's located outside the component root dir ${rootDir}`);
      }
      return newPath;
    }
  }, {
    key: 'changeFilesPathAccordingToItsRootDir',
    value: function changeFilesPathAccordingToItsRootDir(existingRootDir, files) {
      var _this5 = this;

      var changes = [];
      files.forEach(function (file) {
        var newPath = _this5.getPathWithoutRootDir(existingRootDir, file.relativePath);
        changes.push({ from: file.relativePath, to: newPath });
        file.relativePath = newPath;
      });
      return changes;
    }
  }]);

  return ComponentMap;
}();

exports.default = ComponentMap;