'use strict';

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

var _regenerator;

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

var _bluebird;

function _load_bluebird() {
  return _bluebird = require('bluebird');
}

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 _fsExtra;

function _load_fsExtra() {
  return _fsExtra = _interopRequireDefault(require('fs-extra'));
}

var _path;

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

var _componentMap;

function _load_componentMap() {
  return _componentMap = _interopRequireDefault(require('../bit-map/component-map'));
}

var _bitId;

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

var _logger;

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

var _generalError;

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

var _path2;

function _load_path2() {
  return _path2 = require('../../utils/path');
}

var _constants;

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

var _componentNodeModulesPath;

function _load_componentNodeModulesPath() {
  return _componentNodeModulesPath = _interopRequireDefault(require('../../utils/bit/component-node-modules-path'));
}

var _packageJson;

function _load_packageJson() {
  return _packageJson = require('../component/package-json');
}

var _jsonFile;

function _load_jsonFile() {
  return _jsonFile = _interopRequireDefault(require('../component/sources/json-file'));
}

var _dataToPersist;

function _load_dataToPersist() {
  return _dataToPersist = _interopRequireDefault(require('../component/sources/data-to-persist'));
}

var _removePath;

function _load_removePath() {
  return _removePath = _interopRequireDefault(require('../component/sources/remove-path'));
}

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

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

var ComponentWriter = function () {
  function ComponentWriter(_ref) {
    var component = _ref.component,
        writeToPath = _ref.writeToPath,
        _ref$writeConfig = _ref.writeConfig,
        writeConfig = _ref$writeConfig === undefined ? false : _ref$writeConfig,
        configDir = _ref.configDir,
        _ref$writePackageJson = _ref.writePackageJson,
        writePackageJson = _ref$writePackageJson === undefined ? true : _ref$writePackageJson,
        _ref$override = _ref.override,
        override = _ref$override === undefined ? true : _ref$override,
        origin = _ref.origin,
        parent = _ref.parent,
        consumer = _ref.consumer,
        _ref$writeBitDependen = _ref.writeBitDependencies,
        writeBitDependencies = _ref$writeBitDependen === undefined ? false : _ref$writeBitDependen,
        deleteBitDirContent = _ref.deleteBitDirContent,
        existingComponentMap = _ref.existingComponentMap,
        _ref$excludeRegistryP = _ref.excludeRegistryPrefix,
        excludeRegistryPrefix = _ref$excludeRegistryP === undefined ? false : _ref$excludeRegistryP;

    _classCallCheck(this, ComponentWriter);

    this.component = component;
    this.writeToPath = writeToPath;
    this.writeConfig = writeConfig;
    this.configDir = configDir;
    this.writePackageJson = writePackageJson;
    this.override = override;
    this.origin = origin;
    this.parent = parent;
    this.consumer = consumer;
    this.writeBitDependencies = writeBitDependencies;
    this.deleteBitDirContent = deleteBitDirContent;
    this.existingComponentMap = existingComponentMap;
    this.excludeRegistryPrefix = excludeRegistryPrefix;
  }

  _createClass(ComponentWriter, [{
    key: 'write',


    /**
     * write the component to the filesystem and update .bitmap with the details.
     *
     * bitMap gets updated before writing the files to the filesystem, because as part of writing the
     * package-json file, the componentMap is needed to be stored with the updated version.
     *
     * when a component is not new, write the files according to the paths in .bitmap.
     */
    value: function () {
      var _ref2 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return this.populateComponentsFilesToWrite();

              case 2:
                this.component.dataToPersist.addBasePath(this.consumer.getPath());
                _context.next = 5;
                return this.component.dataToPersist.persistAllToFS();

              case 5:
                return _context.abrupt('return', this.component);

              case 6:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function write() {
        return _ref2.apply(this, arguments);
      }

      return write;
    }()
  }, {
    key: 'populateComponentsFilesToWrite',
    value: function () {
      var _ref3 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                if (!(!this.component.files || !this.component.files.length)) {
                  _context2.next = 2;
                  break;
                }

                throw new (_generalError || _load_generalError()).default(`Component ${this.component.id.toString()} is invalid as it has no files`);

              case 2:
                this.component.dataToPersist = new (_dataToPersist || _load_dataToPersist()).default();
                this._updateFilesBasePaths();
                this.componentMap = this.existingComponentMap || this.addComponentToBitMap(this.writeToPath);
                this.component.componentMap = this.componentMap;
                this._copyFilesIntoDistsWhenDistsOutsideComponentDir();
                this._determineWhetherToDeleteComponentDirContent();
                _context2.next = 10;
                return this._handlePreviouslyNestedCurrentlyImportedCase();

              case 10:
                this._determineWhetherToWriteConfig();
                this._updateComponentRootPathAccordingToBitMap();
                this._updateBitMapIfNeeded();
                this._determineWhetherToWritePackageJson();
                _context2.next = 16;
                return this.populateFilesToWriteToComponentDir();

              case 16:
                return _context2.abrupt('return', this.component);

              case 17:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function populateComponentsFilesToWrite() {
        return _ref3.apply(this, arguments);
      }

      return populateComponentsFilesToWrite;
    }()
  }, {
    key: 'populateFilesToWriteToComponentDir',
    value: function () {
      var _ref4 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3() {
        var _this = this;

        var dists, resolvedConfigDir, configToWrite, packageJson, packageJsonPath, jsonFile;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                if (this.deleteBitDirContent) {
                  this.component.dataToPersist.removePath(new (_removePath || _load_removePath()).default(this.writeToPath));
                }
                this.component.files.forEach(function (file) {
                  return file.override = _this.override;
                });
                this.component.files.map(function (file) {
                  return _this.component.dataToPersist.addFile(file);
                });
                _context3.next = 5;
                return this.component.dists.getDistsToWrite(this.component, this.consumer, false);

              case 5:
                dists = _context3.sent;

                if (dists) this.component.dataToPersist.merge(dists);

                if (!(this.writeConfig && this.consumer)) {
                  _context3.next = 13;
                  break;
                }

                resolvedConfigDir = this.configDir || this.consumer.dirStructure.ejectedEnvsDirStructure;
                _context3.next = 11;
                return this.component.getConfigToWrite(this.consumer, resolvedConfigDir, this.override);

              case 11:
                configToWrite = _context3.sent;

                this.component.dataToPersist.merge(configToWrite.dataToPersist);

              case 13:
                if (!(this.writePackageJson && (this.consumer.isolated || this.writeToPath !== this.consumer.getPath()))) {
                  _context3.next = 21;
                  break;
                }

                _context3.next = 16;
                return (0, (_packageJson || _load_packageJson()).preparePackageJsonToWrite)(this.consumer, this.component, this.writeToPath, this.override, this.writeBitDependencies, this.excludeRegistryPrefix);

              case 16:
                packageJson = _context3.sent;
                packageJsonPath = (_path || _load_path()).default.join(this.writeToPath, (_constants || _load_constants()).PACKAGE_JSON);
                jsonFile = (_jsonFile || _load_jsonFile()).default.load({ base: this.writeToPath, path: packageJsonPath, content: packageJson });

                this.component.dataToPersist.addFile(jsonFile);
                this.component.packageJsonInstance = packageJson;

              case 21:
                if (this.component.license && this.component.license.contents) {
                  this.component.license.updatePaths({ newBase: this.writeToPath });
                  // $FlowFixMe this.component.license is set
                  this.component.license.override = this.override;
                  // $FlowFixMe this.component.license is set
                  this.component.dataToPersist.addFile(this.component.license);
                }

              case 22:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this);
      }));

      function populateFilesToWriteToComponentDir() {
        return _ref4.apply(this, arguments);
      }

      return populateFilesToWriteToComponentDir;
    }()
  }, {
    key: 'addComponentToBitMap',
    value: function addComponentToBitMap(rootDir) {
      var _this2 = this;

      var filesForBitMap = this.component.files.map(function (file) {
        return { name: file.basename, relativePath: (0, (_path2 || _load_path2()).pathNormalizeToLinux)(file.relative), test: file.test };
      });
      var getConfigDir = function getConfigDir() {
        if (_this2.configDir) return _this2.configDir;
        if (_this2.componentMap) return _this2.componentMap.configDir;
        return undefined;
      };

      return this.consumer.bitMap.addComponent({
        componentId: this.component.id,
        files: filesForBitMap,
        mainFile: this.component.mainFile, // $FlowFixMe
        rootDir, // $FlowFixMe
        configDir: getConfigDir(),
        detachedCompiler: this.component.detachedCompiler,
        detachedTester: this.component.detachedTester,
        origin: this.origin,
        parent: this.parent,
        originallySharedDir: this.component.originallySharedDir,
        wrapDir: this.component.wrapDir
      });
    }
  }, {
    key: '_copyFilesIntoDistsWhenDistsOutsideComponentDir',
    value: function _copyFilesIntoDistsWhenDistsOutsideComponentDir() {
      if (!this.consumer.shouldDistsBeInsideTheComponent() && this.component.dists.isEmpty()) {
        // since the dists are set to be outside the components dir, the source files must be saved there
        // otherwise, other components in dists won't be able to link to this component
        this.component.copyFilesIntoDists();
      }
    }
  }, {
    key: '_updateComponentRootPathAccordingToBitMap',
    value: function _updateComponentRootPathAccordingToBitMap() {
      this.writeToPath = this.componentMap.getRootDir();
      this.component.writtenPath = this.writeToPath;
      this._updateFilesBasePaths();
    }

    /**
     * when there is componentMap, this component (with this version or other version) is already part of the project.
     * There are several options as to what was the origin before and what is the origin now and according to this,
     * we update/remove/don't-touch the record in bit.map.
     * 1) current origin is AUTHORED - If the version is the same as before, don't update bit.map. Otherwise, update.
     * 2) current origin is IMPORTED - If the version is the same as before, don't update bit.map. Otherwise, update.
     * one exception is where the origin was NESTED before, in this case, remove the current record and add a new one.
     * this case has been already handled before by this._handlePreviouslyNestedCurrentlyImportedCase();
     * 3) current origin is NESTED - the version can't be the same as before (otherwise it would be ignored before and
     * never reach this function, see @write-components.writeToComponentsDir). Therefore, always add to bit.map.
     */

  }, {
    key: '_updateBitMapIfNeeded',
    value: function _updateBitMapIfNeeded() {
      var componentMapExistWithSameVersion = this.consumer.bitMap.isExistWithSameVersion(this.component.id);
      var updateBitMap = !componentMapExistWithSameVersion || this.componentMap.originallySharedDir !== this.component.originallySharedDir;
      if (updateBitMap) {
        if (componentMapExistWithSameVersion) {
          // originallySharedDir has been changed. it affects also the relativePath of the files
          // so it's better to just remove the old record and add a new one
          this.consumer.bitMap.removeComponent(this.component.id);
        }
        this.addComponentToBitMap(this.componentMap.rootDir);
      }
    }
  }, {
    key: '_determineWhetherToWriteConfig',
    value: function _determineWhetherToWriteConfig() {
      if (this.componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
        this.writeConfig = false;
      }
    }

    /**
     * don't write the package.json for an authored component, because its dependencies are managed
     * by the root package.json
     */

  }, {
    key: '_determineWhetherToWritePackageJson',
    value: function _determineWhetherToWritePackageJson() {
      this.writePackageJson = this.writePackageJson && this.origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED;
    }

    /**
     * when a user imports a component that was a dependency before, write the component directly
     * into the components directory for an easy access/change. Then, remove the current record from
     * bit.map and add an updated one.
     */

  }, {
    key: '_handlePreviouslyNestedCurrentlyImportedCase',
    value: function () {
      var _ref5 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                if (!(this.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED && this.componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED)) {
                  _context4.next = 4;
                  break;
                }

                _context4.next = 3;
                return this._cleanOldNestedComponent();

              case 3:
                this.componentMap = this.addComponentToBitMap(this.writeToPath);

              case 4:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this);
      }));

      function _handlePreviouslyNestedCurrentlyImportedCase() {
        return _ref5.apply(this, arguments);
      }

      return _handlePreviouslyNestedCurrentlyImportedCase;
    }()

    /**
     * For IMPORTED component we have to delete the content of the directory before importing.
     * Otherwise, when the author adds new files outside of the previous originallySharedDir and this user imports them
     * the environment will contain both copies, the old one with the old originallySharedDir and the new one.
     * If a user made changes to the imported component, it will show a warning and stop the process.
     */

  }, {
    key: '_determineWhetherToDeleteComponentDirContent',
    value: function _determineWhetherToDeleteComponentDirContent() {
      if (typeof this.deleteBitDirContent === 'undefined') {
        this.deleteBitDirContent = this.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED;
      }
    }
  }, {
    key: '_updateFilesBasePaths',
    value: function _updateFilesBasePaths() {
      var newBase = this.writeToPath || '.';
      this.component.files.forEach(function (file) {
        return file.updatePaths({ newBase });
      });
      if (!this.component.dists.isEmpty()) {
        this.component.dists.get().forEach(function (dist) {
          return dist.updatePaths({ newBase });
        });
      }
    }
  }, {
    key: '_cleanOldNestedComponent',
    value: function () {
      var _ref6 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5() {
        var oldLocation;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                // $FlowFixMe this function gets called when it was previously NESTED, so the rootDir is set
                oldLocation = (_path || _load_path()).default.join(this.consumer.getPath(), this.componentMap.rootDir);

                (_logger || _load_logger()).default.debugAndAddBreadCrumb('component-writer._cleanOldNestedComponent', 'deleting the old directory of a component at {oldLocation}', { oldLocation });
                _context5.next = 4;
                return (_fsExtra || _load_fsExtra()).default.remove(oldLocation);

              case 4:
                _context5.next = 6;
                return this._removeNodeModulesLinksFromDependents();

              case 6:
                this.consumer.bitMap.removeComponent(this.component.id);

              case 7:
              case 'end':
                return _context5.stop();
            }
          }
        }, _callee5, this);
      }));

      function _cleanOldNestedComponent() {
        return _ref6.apply(this, arguments);
      }

      return _cleanOldNestedComponent;
    }()
  }, {
    key: '_removeNodeModulesLinksFromDependents',
    value: function () {
      var _ref7 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee6() {
        var _this3 = this;

        var directDependentComponents;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                _context6.next = 2;
                return this.consumer.getAuthoredAndImportedDependentsOfComponents([this.component]);

              case 2:
                directDependentComponents = _context6.sent;
                _context6.next = 5;
                return Promise.all(directDependentComponents.map(function (dependent) {
                  var dependentComponentMap = _this3.consumer.bitMap.getComponent(dependent.id);
                  var relativeLinkPath = (0, (_componentNodeModulesPath || _load_componentNodeModulesPath()).default)(_this3.consumer.bitJson.bindingPrefix, _this3.component.id);
                  var nodeModulesLinkAbs = _this3.consumer.toAbsolutePath((_path || _load_path()).default.join(dependentComponentMap.getRootDir(), relativeLinkPath));
                  (_logger || _load_logger()).default.debug(`deleting an obsolete link to node_modules at ${nodeModulesLinkAbs}`);
                  return (_fsExtra || _load_fsExtra()).default.remove(nodeModulesLinkAbs);
                }));

              case 5:
              case 'end':
                return _context6.stop();
            }
          }
        }, _callee6, this);
      }));

      function _removeNodeModulesLinksFromDependents() {
        return _ref7.apply(this, arguments);
      }

      return _removeNodeModulesLinksFromDependents;
    }()
  }], [{
    key: 'getInstance',
    value: function getInstance(componentWriterProps) {
      return new ComponentWriter(componentWriterProps);
    }
  }]);

  return ComponentWriter;
}();

exports.default = ComponentWriter;