"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AndroidDeviceFileSystem = void 0;
const path = require("path");
const _ = require("lodash");
const semver = require("semver");
const android_device_hash_service_1 = require("./android-device-hash-service");
const helpers_1 = require("../../helpers");
const constants_1 = require("../../constants");
class AndroidDeviceFileSystem {
    constructor(adb, $fs, $logger, $mobileHelper, $tempService, $injector) {
        this.adb = adb;
        this.$fs = $fs;
        this.$logger = $logger;
        this.$mobileHelper = $mobileHelper;
        this.$tempService = $tempService;
        this.$injector = $injector;
        this._deviceHashServices = Object.create(null);
    }
    listFiles(devicePath, appIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            let listCommandArgs = ["ls", "-a", devicePath];
            if (appIdentifier) {
                listCommandArgs = ["run-as", appIdentifier].concat(listCommandArgs);
            }
            return this.adb.executeShellCommand(listCommandArgs);
        });
    }
    getFile(deviceFilePath, appIdentifier, outputPath) {
        return __awaiter(this, void 0, void 0, function* () {
            const stdout = !outputPath;
            if (stdout) {
                outputPath = yield this.$tempService.path({
                    prefix: "sync",
                    suffix: ".tmp",
                });
            }
            yield this.adb.executeCommand(["pull", deviceFilePath, outputPath]);
            if (stdout) {
                yield new Promise((resolve, reject) => {
                    const readStream = this.$fs.createReadStream(outputPath);
                    readStream.pipe(process.stdout);
                    readStream.on("end", () => {
                        resolve();
                    });
                    readStream.on("error", (err) => {
                        reject(err);
                    });
                });
            }
        });
    }
    getFileContent(deviceFilePath, appIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            const result = yield this.adb.executeShellCommand(["cat", deviceFilePath]);
            return result;
        });
    }
    putFile(localFilePath, deviceFilePath, appIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.adb.pushFile(localFilePath, deviceFilePath);
        });
    }
    transferFiles(deviceAppData, localToDevicePaths) {
        return __awaiter(this, void 0, void 0, function* () {
            const directoriesToChmod = [];
            const transferredFiles = [];
            const action = (localToDevicePathData) => __awaiter(this, void 0, void 0, function* () {
                const fstat = this.$fs.getFsStats(localToDevicePathData.getLocalPath());
                if (fstat.isFile()) {
                    const devicePath = localToDevicePathData.getDevicePath();
                    yield this.adb.pushFile(localToDevicePathData.getLocalPath(), devicePath);
                    transferredFiles.push(localToDevicePathData);
                }
                else if (fstat.isDirectory()) {
                    const dirToChmod = localToDevicePathData.getDevicePath();
                    directoriesToChmod.push(dirToChmod);
                }
            });
            yield (0, helpers_1.executeActionByChunks)(localToDevicePaths, constants_1.DEFAULT_CHUNK_SIZE, action);
            const dirsChmodAction = (directoryToChmod) => this.adb.executeShellCommand(["chmod", "0777", directoryToChmod]);
            yield (0, helpers_1.executeActionByChunks)(_.uniq(directoriesToChmod), constants_1.DEFAULT_CHUNK_SIZE, dirsChmodAction);
            return transferredFiles;
        });
    }
    transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath) {
        return __awaiter(this, void 0, void 0, function* () {
            // starting from Android 9, adb push is throwing an exception when there are subfolders
            // the check could be removed when we start supporting only runtime versions with sockets
            const minAndroidWithoutAdbPushDir = "9.0.0";
            const isAdbPushDirSupported = semver.lt(semver.coerce(deviceAppData.device.deviceInfo.version), minAndroidWithoutAdbPushDir);
            const deviceProjectDir = yield deviceAppData.getDeviceProjectRootPath();
            let transferredLocalToDevicePaths = [];
            if (isAdbPushDirSupported) {
                yield this.adb.pushFile(projectFilesPath, deviceProjectDir);
                transferredLocalToDevicePaths = localToDevicePaths;
            }
            else {
                transferredLocalToDevicePaths = yield this.pushFiles(localToDevicePaths);
            }
            if (transferredLocalToDevicePaths.length) {
                const filesToChmodOnDevice = transferredLocalToDevicePaths.map((localToDevicePath) => localToDevicePath.getDevicePath());
                yield this.chmodFiles(deviceProjectDir, filesToChmodOnDevice);
            }
            return transferredLocalToDevicePaths;
        });
    }
    chmodFiles(deviceProjectRoot, filesToChmodOnDevice) {
        return __awaiter(this, void 0, void 0, function* () {
            const commandsDeviceFilePath = this.$mobileHelper.buildDevicePath(deviceProjectRoot, "nativescript.commands.sh");
            yield this.createFileOnDevice(commandsDeviceFilePath, `chmod 0777 ${filesToChmodOnDevice.join(" ")}`);
            yield this.adb.executeShellCommand([commandsDeviceFilePath]);
        });
    }
    pushFiles(localToDevicePaths) {
        return __awaiter(this, void 0, void 0, function* () {
            this.$logger.trace("Changed hashes are:", localToDevicePaths);
            const transferredFiles = [];
            const transferFileAction = (localToDevicePathData) => __awaiter(this, void 0, void 0, function* () {
                transferredFiles.push(localToDevicePathData);
                yield this.transferFile(localToDevicePathData.getLocalPath(), localToDevicePathData.getDevicePath());
            });
            yield (0, helpers_1.executeActionByChunks)(localToDevicePaths, constants_1.DEFAULT_CHUNK_SIZE, transferFileAction);
            return transferredFiles;
        });
    }
    transferFile(localPath, devicePath) {
        return __awaiter(this, void 0, void 0, function* () {
            this.$logger.trace(`Transfering ${localPath} to ${devicePath}`);
            const stats = this.$fs.getFsStats(localPath);
            if (stats.isDirectory()) {
                yield this.adb.executeShellCommand(["mkdir", path.dirname(devicePath)]);
            }
            else {
                yield this.adb.pushFile(localPath, devicePath);
            }
        });
    }
    createFileOnDevice(deviceFilePath, fileContent) {
        return __awaiter(this, void 0, void 0, function* () {
            const hostTmpDir = yield this.$tempService.mkdirSync("application-");
            const commandsFileHostPath = path.join(hostTmpDir, "temp.commands.file");
            this.$fs.writeFile(commandsFileHostPath, fileContent);
            // copy it to the device
            yield this.transferFile(commandsFileHostPath, deviceFilePath);
            yield this.adb.executeShellCommand(["chmod", "0777", deviceFilePath]);
        });
    }
    deleteFile(deviceFilePath, appIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.adb.executeShellCommand(["rm", "-rf", deviceFilePath]);
        });
    }
    updateHashesOnDevice(hashes, appIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            const deviceHashService = this.getDeviceHashService(appIdentifier);
            yield deviceHashService.uploadHashFileToDevice(hashes);
        });
    }
    getDeviceHashService(appIdentifier) {
        if (!this._deviceHashServices[appIdentifier]) {
            this._deviceHashServices[appIdentifier] = this.$injector.resolve(android_device_hash_service_1.AndroidDeviceHashService, {
                adb: this.adb,
                appIdentifier,
            });
        }
        return this._deviceHashServices[appIdentifier];
    }
}
exports.AndroidDeviceFileSystem = AndroidDeviceFileSystem;
//# sourceMappingURL=android-device-file-system.js.map