"use strict";
const constants = require("../../../lib/constants");
const fs = require("fs");
const path = require("path");
const shelljs = require("shelljs");
const Future = require("fibers/future");
const node_modules_dest_copy_1 = require("./node-modules-dest-copy");
const node_modules_dependencies_builder_1 = require("./node-modules-dependencies-builder");
const fiberBootstrap = require("../../common/fiber-bootstrap");
const helpers_1 = require("../../../lib/common/helpers");
let glob = require("glob");
class NodeModulesBuilder {
    constructor($fs, $projectData, $projectDataService, $injector, $logger, $lockfile, $options) {
        this.$fs = $fs;
        this.$projectData = $projectData;
        this.$projectDataService = $projectDataService;
        this.$injector = $injector;
        this.$logger = $logger;
        this.$lockfile = $lockfile;
        this.$options = $options;
    }
    getChangedNodeModules(absoluteOutputPath, platform, lastModifiedTime) {
        return (() => {
            let projectDir = this.$projectData.projectDir;
            let isNodeModulesModified = false;
            let nodeModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME);
            let nodeModules = {};
            if (lastModifiedTime) {
                let future = new Future();
                let match = new glob.Glob("node_modules/**", {
                    cwd: projectDir,
                    follow: true,
                    stat: true
                }, (er, files) => {
                    fiberBootstrap.run(() => {
                        while (this.$lockfile.check().wait()) {
                            helpers_1.sleep(10);
                        }
                        this.$lockfile.lock().wait();
                        if (er) {
                            if (!future.isResolved()) {
                                future.throw(er);
                            }
                            this.$lockfile.unlock().wait();
                            match.abort();
                            return;
                        }
                        for (let i = 0, l = files.length; i < l; i++) {
                            let file = files[i], resolvedPath = path.join(projectDir, file), relativePath = path.relative(projectDir, resolvedPath);
                            let stat = match.statCache[resolvedPath] || match.statCache[relativePath];
                            if (!stat) {
                                match.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.abort();
                                if (!future.isResolved()) {
                                    future.return();
                                }
                                return;
                            }
                            let rootModuleName = path.normalize(file).split(path.sep)[1];
                            let rootModuleFullPath = path.join(nodeModulesPath, rootModuleName);
                            nodeModules[rootModuleFullPath] = rootModuleFullPath;
                        }
                        this.$lockfile.unlock().wait();
                    });
                });
                match.on("end", () => {
                    if (!future.isResolved()) {
                        let intervalId = setInterval(() => {
                            fiberBootstrap.run(() => {
                                if (!this.$lockfile.check().wait() || future.isResolved()) {
                                    if (!future.isResolved()) {
                                        future.return();
                                    }
                                    clearInterval(intervalId);
                                }
                            });
                        }, 100);
                    }
                });
                future.wait();
            }
            if (isNodeModulesModified && this.$fs.exists(absoluteOutputPath).wait()) {
                let currentPreparedTnsModules = this.$fs.readDirectory(absoluteOutputPath).wait();
                let tnsModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, constants.TNS_CORE_MODULES_NAME);
                let tnsModulesInApp = this.$fs.readDirectory(tnsModulesPath).wait();
                let modulesToDelete = _.difference(currentPreparedTnsModules, tnsModulesInApp);
                _.each(modulesToDelete, moduleName => this.$fs.deleteDirectory(path.join(absoluteOutputPath, moduleName)).wait());
            }
            if (!lastModifiedTime || isNodeModulesModified) {
                this.expandScopedModules(nodeModulesPath, nodeModules);
            }
            return nodeModules;
        }).future()();
    }
    expandScopedModules(nodeModulesPath, nodeModules) {
        let nodeModulesDirectories = this.$fs.exists(nodeModulesPath).wait() ? this.$fs.readDirectory(nodeModulesPath).wait() : [];
        _.each(nodeModulesDirectories, nodeModuleDirectoryName => {
            let isNpmScope = /^@/.test(nodeModuleDirectoryName);
            let nodeModuleFullPath = path.join(nodeModulesPath, nodeModuleDirectoryName);
            if (isNpmScope) {
                this.expandScopedModules(nodeModuleFullPath, nodeModules);
            }
            else {
                nodeModules[nodeModuleFullPath] = nodeModuleFullPath;
            }
        });
    }
    prepareNodeModules(absoluteOutputPath, platform, lastModifiedTime) {
        return (() => {
            if (!fs.existsSync(absoluteOutputPath)) {
                lastModifiedTime = null;
            }
            let dependenciesBuilder = this.$injector.resolve(node_modules_dependencies_builder_1.NodeModulesDependenciesBuilder, {});
            let productionDependencies = dependenciesBuilder.getProductionDependencies(this.$projectData.projectDir);
            if (!this.$options.bundle) {
                const tnsModulesCopy = this.$injector.resolve(node_modules_dest_copy_1.TnsModulesCopy, {
                    outputRoot: absoluteOutputPath
                });
                tnsModulesCopy.copyModules(productionDependencies, platform);
            }
            else {
                this.cleanNodeModules(absoluteOutputPath, platform);
            }
            const npmPluginPrepare = this.$injector.resolve(node_modules_dest_copy_1.NpmPluginPrepare, {});
            npmPluginPrepare.preparePlugins(productionDependencies, platform);
        }).future()();
    }
    cleanNodeModules(absoluteOutputPath, platform) {
        shelljs.rm("-rf", absoluteOutputPath);
    }
}
exports.NodeModulesBuilder = NodeModulesBuilder;
$injector.register("nodeModulesBuilder", NodeModulesBuilder);
