"use strict";
var constants = require("../../../lib/constants");
var fs = require("fs");
var path = require("path");
var shelljs = require("shelljs");
var Future = require("fibers/future");
var destCopyLib = require("./node-modules-dest-copy");
var fiberBootstrap = require("../../common/fiber-bootstrap");
var helpers_1 = require("../../../lib/common/helpers");
var glob = require("glob");
var Builder = (function () {
    function Builder($fs, $nodeModulesTree, $projectData, $projectDataService, $injector, $logger, $lockfile) {
        this.$fs = $fs;
        this.$nodeModulesTree = $nodeModulesTree;
        this.$projectData = $projectData;
        this.$projectDataService = $projectDataService;
        this.$injector = $injector;
        this.$logger = $logger;
        this.$lockfile = $lockfile;
    }
    Builder.prototype.getChangedNodeModules = function (absoluteOutputPath, platform, lastModifiedTime) {
        var _this = this;
        return (function () {
            var projectDir = _this.$projectData.projectDir;
            var isNodeModulesModified = false;
            var nodeModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME);
            var nodeModules = {};
            if (lastModifiedTime) {
                var future_1 = new Future();
                var match_1 = new glob.Glob("node_modules/**", {
                    cwd: projectDir,
                    follow: true,
                    stat: true
                }, function (er, files) {
                    fiberBootstrap.run(function () {
                        while (_this.$lockfile.check().wait()) {
                            helpers_1.sleep(10);
                        }
                        _this.$lockfile.lock().wait();
                        if (er) {
                            if (!future_1.isResolved()) {
                                future_1.throw(er);
                            }
                            _this.$lockfile.unlock().wait();
                            match_1.abort();
                            return;
                        }
                        for (var i = 0, l = files.length; i < l; i++) {
                            var file = files[i], resolvedPath = path.join(projectDir, file), relativePath = path.relative(projectDir, resolvedPath);
                            var stat = match_1.statCache[resolvedPath] || match_1.statCache[relativePath];
                            if (!stat) {
                                match_1.statCache[resolvedPath] = stat = _this.$fs.getFsStats(resolvedPath).wait();
                            }
                            if (stat.mtime <= lastModifiedTime) {
                                continue;
                            }
                            if (file === constants.NODE_MODULES_FOLDER_NAME) {
                                isNodeModulesModified = true;
                                _this.$lockfile.unlock().wait();
                                match_1.abort();
                                if (!future_1.isResolved()) {
                                    future_1.return();
                                }
                                return;
                            }
                            var rootModuleName = path.normalize(file).split(path.sep)[1];
                            var rootModuleFullPath = path.join(nodeModulesPath, rootModuleName);
                            nodeModules[rootModuleFullPath] = rootModuleFullPath;
                        }
                        _this.$lockfile.unlock().wait();
                    });
                });
                match_1.on("end", function () {
                    if (!future_1.isResolved()) {
                        var intervalId_1 = setInterval(function () {
                            fiberBootstrap.run(function () {
                                if (!_this.$lockfile.check().wait() || future_1.isResolved()) {
                                    if (!future_1.isResolved()) {
                                        future_1.return();
                                    }
                                    clearInterval(intervalId_1);
                                }
                            });
                        }, 100);
                    }
                });
                future_1.wait();
            }
            if (isNodeModulesModified && _this.$fs.exists(absoluteOutputPath).wait()) {
                var currentPreparedTnsModules = _this.$fs.readDirectory(absoluteOutputPath).wait();
                var tnsModulesPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.TNS_MODULES_FOLDER_NAME);
                if (!_this.$fs.exists(tnsModulesPath).wait()) {
                    tnsModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, constants.TNS_CORE_MODULES_NAME);
                }
                var tnsModulesInApp = _this.$fs.readDirectory(tnsModulesPath).wait();
                var modulesToDelete = _.difference(currentPreparedTnsModules, tnsModulesInApp);
                _.each(modulesToDelete, function (moduleName) { return _this.$fs.deleteDirectory(path.join(absoluteOutputPath, moduleName)).wait(); });
            }
            if (!lastModifiedTime || isNodeModulesModified) {
                _this.listModules(nodeModulesPath, nodeModules);
            }
            return nodeModules;
        }).future()();
    };
    Builder.prototype.listModules = function (nodeModulesPath, nodeModules) {
        var _this = this;
        var nodeModulesDirectories = this.$fs.exists(nodeModulesPath).wait() ? this.$fs.readDirectory(nodeModulesPath).wait() : [];
        _.each(nodeModulesDirectories, function (nodeModuleDirectoryName) {
            var isNpmScope = /^@/.test(nodeModuleDirectoryName);
            var nodeModuleFullPath = path.join(nodeModulesPath, nodeModuleDirectoryName);
            if (isNpmScope) {
                _this.listModules(nodeModuleFullPath, nodeModules);
            }
            else {
                nodeModules[nodeModuleFullPath] = nodeModuleFullPath;
            }
        });
    };
    Builder.prototype.prepareNodeModules = function (absoluteOutputPath, platform, lastModifiedTime) {
        var _this = this;
        return (function () {
            if (!fs.existsSync(absoluteOutputPath)) {
                lastModifiedTime = null;
            }
            var nodeModules = _this.getChangedNodeModules(absoluteOutputPath, platform, lastModifiedTime).wait();
            var destCopy = _this.$injector.resolve(destCopyLib.DestCopy, {
                inputPath: _this.$projectData.projectDir,
                cachePath: "",
                outputRoot: absoluteOutputPath,
                projectDir: _this.$projectData.projectDir,
                platform: platform
            });
            destCopy.rebuildChangedDirectories(_.keys(nodeModules), platform);
        }).future()();
    };
    Builder.prototype.cleanNodeModules = function (absoluteOutputPath, platform) {
        shelljs.rm("-rf", absoluteOutputPath);
    };
    return Builder;
}());
exports.Builder = Builder;
$injector.register("broccoliBuilder", Builder);
