"use strict";
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);
    }
    async listFiles(devicePath, appIdentifier) {
        let listCommandArgs = ["ls", "-a", devicePath];
        if (appIdentifier) {
            listCommandArgs = ["run-as", appIdentifier].concat(listCommandArgs);
        }
        return this.adb.executeShellCommand(listCommandArgs);
    }
    async getFile(deviceFilePath, appIdentifier, outputPath) {
        const stdout = !outputPath;
        if (stdout) {
            outputPath = await this.$tempService.path({
                prefix: "sync",
                suffix: ".tmp",
            });
        }
        await this.adb.executeCommand(["pull", deviceFilePath, outputPath]);
        if (stdout) {
            await new Promise((resolve, reject) => {
                const readStream = this.$fs.createReadStream(outputPath);
                readStream.pipe(process.stdout);
                readStream.on("end", () => {
                    resolve();
                });
                readStream.on("error", (err) => {
                    reject(err);
                });
            });
        }
    }
    async getFileContent(deviceFilePath, appIdentifier) {
        const result = await this.adb.executeShellCommand(["cat", deviceFilePath]);
        return result;
    }
    async putFile(localFilePath, deviceFilePath, appIdentifier) {
        await this.adb.pushFile(localFilePath, deviceFilePath);
    }
    async transferFiles(deviceAppData, localToDevicePaths) {
        const directoriesToChmod = [];
        const transferredFiles = [];
        const action = async (localToDevicePathData) => {
            const fstat = this.$fs.getFsStats(localToDevicePathData.getLocalPath());
            if (fstat.isFile()) {
                const devicePath = localToDevicePathData.getDevicePath();
                await this.adb.pushFile(localToDevicePathData.getLocalPath(), devicePath);
                transferredFiles.push(localToDevicePathData);
            }
            else if (fstat.isDirectory()) {
                const dirToChmod = localToDevicePathData.getDevicePath();
                directoriesToChmod.push(dirToChmod);
            }
        };
        await (0, helpers_1.executeActionByChunks)(localToDevicePaths, constants_1.DEFAULT_CHUNK_SIZE, action);
        const dirsChmodAction = (directoryToChmod) => this.adb.executeShellCommand(["chmod", "0777", directoryToChmod]);
        await (0, helpers_1.executeActionByChunks)(_.uniq(directoriesToChmod), constants_1.DEFAULT_CHUNK_SIZE, dirsChmodAction);
        return transferredFiles;
    }
    async transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath) {
        const minAndroidWithoutAdbPushDir = "9.0.0";
        const isAdbPushDirSupported = semver.lt(semver.coerce(deviceAppData.device.deviceInfo.version), minAndroidWithoutAdbPushDir);
        const deviceProjectDir = await deviceAppData.getDeviceProjectRootPath();
        let transferredLocalToDevicePaths = [];
        if (isAdbPushDirSupported) {
            await this.adb.pushFile(projectFilesPath, deviceProjectDir);
            transferredLocalToDevicePaths = localToDevicePaths;
        }
        else {
            transferredLocalToDevicePaths = await this.pushFiles(localToDevicePaths);
        }
        if (transferredLocalToDevicePaths.length) {
            const filesToChmodOnDevice = transferredLocalToDevicePaths.map((localToDevicePath) => localToDevicePath.getDevicePath());
            await this.chmodFiles(deviceProjectDir, filesToChmodOnDevice);
        }
        return transferredLocalToDevicePaths;
    }
    async chmodFiles(deviceProjectRoot, filesToChmodOnDevice) {
        const commandsDeviceFilePath = this.$mobileHelper.buildDevicePath(deviceProjectRoot, "nativescript.commands.sh");
        await this.createFileOnDevice(commandsDeviceFilePath, `chmod 0777 ${filesToChmodOnDevice.join(" ")}`);
        await this.adb.executeShellCommand([commandsDeviceFilePath]);
    }
    async pushFiles(localToDevicePaths) {
        this.$logger.trace("Changed hashes are:", localToDevicePaths);
        const transferredFiles = [];
        const transferFileAction = async (localToDevicePathData) => {
            transferredFiles.push(localToDevicePathData);
            await this.transferFile(localToDevicePathData.getLocalPath(), localToDevicePathData.getDevicePath());
        };
        await (0, helpers_1.executeActionByChunks)(localToDevicePaths, constants_1.DEFAULT_CHUNK_SIZE, transferFileAction);
        return transferredFiles;
    }
    async transferFile(localPath, devicePath) {
        this.$logger.trace(`Transfering ${localPath} to ${devicePath}`);
        const stats = this.$fs.getFsStats(localPath);
        if (stats.isDirectory()) {
            await this.adb.executeShellCommand(["mkdir", path.dirname(devicePath)]);
        }
        else {
            await this.adb.pushFile(localPath, devicePath);
        }
    }
    async createFileOnDevice(deviceFilePath, fileContent) {
        const hostTmpDir = await this.$tempService.mkdirSync("application-");
        const commandsFileHostPath = path.join(hostTmpDir, "temp.commands.file");
        this.$fs.writeFile(commandsFileHostPath, fileContent);
        await this.transferFile(commandsFileHostPath, deviceFilePath);
        await this.adb.executeShellCommand(["chmod", "0777", deviceFilePath]);
    }
    async deleteFile(deviceFilePath, appIdentifier) {
        await this.adb.executeShellCommand(["rm", "-rf", deviceFilePath]);
    }
    async updateHashesOnDevice(hashes, appIdentifier) {
        const deviceHashService = this.getDeviceHashService(appIdentifier);
        await 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;
