"use strict";
const path = require("path");
const temp = require("temp");
const android_device_hash_service_1 = require("./android-device-hash-service");
const Future = require("fibers/future");
class AndroidDeviceFileSystem {
    constructor(adb, identifier, $fs, $logger, $mobileHelper, $injector, $options) {
        this.adb = adb;
        this.identifier = identifier;
        this.$fs = $fs;
        this.$logger = $logger;
        this.$mobileHelper = $mobileHelper;
        this.$injector = $injector;
        this.$options = $options;
        this._deviceHashServices = Object.create(null);
    }
    listFiles(devicePath, appIdentifier) {
        let listCommandArgs = ["ls", "-a", devicePath];
        if (appIdentifier) {
            listCommandArgs = ["run-as", appIdentifier].concat(listCommandArgs);
        }
        return this.adb.executeShellCommand(listCommandArgs);
    }
    getFile(deviceFilePath) {
        return Future.fromResult();
    }
    putFile(localFilePath, deviceFilePath) {
        return Future.fromResult();
    }
    transferFiles(deviceAppData, localToDevicePaths) {
        return (() => {
            _(localToDevicePaths)
                .filter(localToDevicePathData => this.$fs.getFsStats(localToDevicePathData.getLocalPath()).wait().isFile())
                .each(localToDevicePathData => this.adb.executeCommand(["push", localToDevicePathData.getLocalPath(), localToDevicePathData.getDevicePath()]).wait());
            _(localToDevicePaths)
                .filter(localToDevicePathData => this.$fs.getFsStats(localToDevicePathData.getLocalPath()).wait().isDirectory())
                .each(localToDevicePathData => this.adb.executeShellCommand(["chmod", "0777", localToDevicePathData.getDevicePath()]).wait());
            let deviceHashService = this.getDeviceHashService(deviceAppData.appIdentifier);
            if (!deviceHashService.updateHashes(localToDevicePaths).wait()) {
                this.$logger.trace("Unable to find hash file on device. The next livesync command will create it.");
            }
        }).future()();
    }
    transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath) {
        return (() => {
            let devicePaths = [], currentShasums = {};
            localToDevicePaths.forEach(localToDevicePathData => {
                let localPath = localToDevicePathData.getLocalPath();
                let stats = this.$fs.getFsStats(localPath).wait();
                if (stats.isFile()) {
                    let fileShasum = this.$fs.getFileShasum(localPath).wait();
                    currentShasums[localPath] = fileShasum;
                }
                devicePaths.push(`"${localToDevicePathData.getDevicePath()}"`);
            });
            let commandsDeviceFilePath = this.$mobileHelper.buildDevicePath(deviceAppData.deviceProjectRootPath, "nativescript.commands.sh");
            let deviceHashService = this.getDeviceHashService(deviceAppData.appIdentifier);
            let filesToChmodOnDevice = devicePaths;
            if (this.$options.force) {
                this.adb.executeShellCommand(["rm", "-rf", deviceHashService.hashFileDevicePath]).wait();
                this.adb.executeCommand(["push", projectFilesPath, deviceAppData.deviceProjectRootPath]).wait();
            }
            else {
                let oldShasums = deviceHashService.getShasumsFromDevice().wait();
                if (oldShasums) {
                    let changedShasums = _.omitBy(currentShasums, (hash, pathToFile) => !!_.find(oldShasums, (oldHash, oldPath) => pathToFile === oldPath && hash === oldHash));
                    this.$logger.trace("Changed file hashes are:", changedShasums);
                    filesToChmodOnDevice = [];
                    let futures = _(changedShasums)
                        .map((hash, filePath) => _.find(localToDevicePaths, ldp => ldp.getLocalPath() === filePath))
                        .map(localToDevicePathData => {
                        filesToChmodOnDevice.push(`"${localToDevicePathData.getDevicePath()}"`);
                        return this.transferFile(localToDevicePathData.getLocalPath(), localToDevicePathData.getDevicePath());
                    })
                        .value();
                    Future.wait(futures);
                }
                else {
                    this.adb.executeCommand(["push", projectFilesPath, deviceAppData.deviceProjectRootPath]).wait();
                }
            }
            if (filesToChmodOnDevice.length) {
                this.createFileOnDevice(commandsDeviceFilePath, "chmod 0777 " + filesToChmodOnDevice.join(" ")).wait();
                this.adb.executeShellCommand([commandsDeviceFilePath]).wait();
            }
            deviceHashService.uploadHashFileToDevice(currentShasums).wait();
        }).future()();
    }
    transferFile(localPath, devicePath) {
        return (() => {
            this.$logger.trace(`Transfering ${localPath} to ${devicePath}`);
            let stats = this.$fs.getFsStats(localPath).wait();
            if (stats.isDirectory()) {
                this.adb.executeShellCommand(["mkdir", path.dirname(devicePath)]).wait();
            }
            else {
                this.adb.executeCommand(["push", localPath, devicePath]).wait();
            }
        }).future()();
    }
    createFileOnDevice(deviceFilePath, fileContent) {
        return (() => {
            let hostTmpDir = this.getTempDir();
            let commandsFileHostPath = path.join(hostTmpDir, "temp.commands.file");
            this.$fs.writeFile(commandsFileHostPath, fileContent).wait();
            this.transferFile(commandsFileHostPath, deviceFilePath).wait();
            this.adb.executeShellCommand(["chmod", "0777", deviceFilePath]).wait();
        }).future()();
    }
    getTempDir() {
        temp.track();
        return temp.mkdirSync("application-");
    }
    getDeviceHashService(appIdentifier) {
        if (!this._deviceHashServices[appIdentifier]) {
            this._deviceHashServices[appIdentifier] = this.$injector.resolve(android_device_hash_service_1.AndroidDeviceHashService, { adb: this.adb, appIdentifier: appIdentifier });
        }
        return this._deviceHashServices[appIdentifier];
    }
}
exports.AndroidDeviceFileSystem = AndroidDeviceFileSystem;
