"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const device_android_debug_bridge_1 = require("../../common/mobile/android/device-android-debug-bridge");
const android_device_hash_service_1 = require("../../common/mobile/android/android-device-hash-service");
const device_livesync_service_base_1 = require("./device-livesync-service-base");
const constants_1 = require("../../constants");
const constants_2 = require("../../common/constants");
const android_livesync_tool_1 = require("./android-livesync-tool");
const path = require("path");
const temp = require("temp");
class AndroidDeviceSocketsLiveSyncService extends device_livesync_service_base_1.DeviceLiveSyncServiceBase {
    constructor(data, $injector, $platformsData, $staticConfig, $logger, device, $options, $processService, $fs) {
        super($platformsData, device);
        this.data = data;
        this.$injector = $injector;
        this.$platformsData = $platformsData;
        this.$staticConfig = $staticConfig;
        this.$logger = $logger;
        this.device = device;
        this.$options = $options;
        this.$processService = $processService;
        this.$fs = $fs;
        this.livesyncTool = this.$injector.resolve(android_livesync_tool_1.AndroidLivesyncTool);
    }
    beforeLiveSyncAction(deviceAppData) {
        return __awaiter(this, void 0, void 0, function* () {
            const platformData = this.$platformsData.getPlatformData(deviceAppData.platform, this.data);
            const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants_1.APP_FOLDER_NAME);
            const pathToLiveSyncFile = temp.path({ prefix: "livesync" });
            this.$fs.writeFile(pathToLiveSyncFile, "");
            yield this.device.fileSystem.putFile(pathToLiveSyncFile, this.getPathToLiveSyncFileOnDevice(deviceAppData.appIdentifier), deviceAppData.appIdentifier);
            yield this.device.applicationManager.startApplication({ appId: deviceAppData.appIdentifier, projectName: this.data.projectName, justLaunch: true });
            yield this.connectLivesyncTool(projectFilesPath, this.data.projectId);
        });
    }
    getPathToLiveSyncFileOnDevice(appIdentifier) {
        return `${constants_2.LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appIdentifier}-livesync-in-progress`;
    }
    finalizeSync(liveSyncInfo, projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const result = yield this.doSync(liveSyncInfo, projectData);
                return result;
            }
            finally {
                this.livesyncTool.end();
            }
        });
    }
    doSync(liveSyncInfo, projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            const operationId = this.livesyncTool.generateOperationIdentifier();
            let result = { operationId, didRefresh: true };
            if (liveSyncInfo.modifiedFilesData.length) {
                const canExecuteFastSync = !liveSyncInfo.isFullSync && this.canExecuteFastSyncForPaths(liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform);
                const doSyncPromise = this.livesyncTool.sendDoSyncOperation(canExecuteFastSync, null, operationId);
                const syncInterval = setInterval(() => {
                    if (this.livesyncTool.isOperationInProgress(operationId)) {
                        this.$logger.info("Sync operation in progress...");
                    }
                }, AndroidDeviceSocketsLiveSyncService.STATUS_UPDATE_INTERVAL);
                const actionOnEnd = () => __awaiter(this, void 0, void 0, function* () {
                    clearInterval(syncInterval);
                    yield this.device.fileSystem.deleteFile(this.getPathToLiveSyncFileOnDevice(liveSyncInfo.deviceAppData.appIdentifier), liveSyncInfo.deviceAppData.appIdentifier);
                });
                this.$processService.attachToProcessExitSignals(this, actionOnEnd);
                yield doSyncPromise.then(actionOnEnd.bind(this), actionOnEnd.bind(this));
                result = yield doSyncPromise;
            }
            else {
                yield this.device.fileSystem.deleteFile(this.getPathToLiveSyncFileOnDevice(liveSyncInfo.deviceAppData.appIdentifier), liveSyncInfo.deviceAppData.appIdentifier);
            }
            return result;
        });
    }
    refreshApplication(projectData, liveSyncInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            const canExecuteFastSync = !liveSyncInfo.isFullSync && this.canExecuteFastSyncForPaths(liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform);
            if (!canExecuteFastSync || !liveSyncInfo.didRefresh) {
                yield this.device.applicationManager.restartApplication({ appId: liveSyncInfo.deviceAppData.appIdentifier, projectName: projectData.projectName });
            }
        });
    }
    removeFiles(deviceAppData, localToDevicePaths, projectFilesPath) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.livesyncTool.removeFiles(_.map(localToDevicePaths, (element) => element.filePath));
            yield this.getDeviceHashService(deviceAppData.appIdentifier).removeHashes(localToDevicePaths);
        });
    }
    transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, isFullSync) {
        return __awaiter(this, void 0, void 0, function* () {
            let transferredFiles;
            if (isFullSync) {
                transferredFiles = yield this._transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath);
            }
            else {
                transferredFiles = yield this._transferFiles(deviceAppData, localToDevicePaths);
            }
            return transferredFiles;
        });
    }
    _transferFiles(deviceAppData, localToDevicePaths) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.livesyncTool.sendFiles(localToDevicePaths.map(localToDevicePathData => localToDevicePathData.getLocalPath()));
            const deviceHashService = this.getDeviceHashService(deviceAppData.appIdentifier);
            if (!(yield deviceHashService.updateHashes(localToDevicePaths))) {
                this.$logger.trace("Unable to find hash file on device. The next livesync command will create it.");
            }
            return localToDevicePaths;
        });
    }
    _transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath) {
        return __awaiter(this, void 0, void 0, function* () {
            let transferredLocalToDevicePaths;
            const deviceHashService = this.getDeviceHashService(deviceAppData.appIdentifier);
            const currentShasums = yield deviceHashService.generateHashesFromLocalToDevicePaths(localToDevicePaths);
            const oldShasums = yield deviceHashService.getShasumsFromDevice();
            if (this.$options.force || !oldShasums) {
                yield this.livesyncTool.sendDirectory(projectFilesPath);
                yield deviceHashService.uploadHashFileToDevice(currentShasums);
                transferredLocalToDevicePaths = localToDevicePaths;
            }
            else {
                const changedShasums = deviceHashService.getChangedShasums(oldShasums, currentShasums);
                const changedFiles = _.keys(changedShasums);
                if (changedFiles.length) {
                    yield this.livesyncTool.sendFiles(changedFiles);
                    yield deviceHashService.uploadHashFileToDevice(currentShasums);
                    transferredLocalToDevicePaths = localToDevicePaths.filter(localToDevicePathData => changedFiles.indexOf(localToDevicePathData.getLocalPath()) >= 0);
                }
                else {
                    transferredLocalToDevicePaths = [];
                }
            }
            return transferredLocalToDevicePaths;
        });
    }
    connectLivesyncTool(projectFilesPath, appIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.livesyncTool.connect({
                appIdentifier,
                deviceIdentifier: this.device.deviceInfo.identifier,
                appPlatformsPath: projectFilesPath
            });
        });
    }
    getDeviceHashService(appIdentifier) {
        const adb = this.$injector.resolve(device_android_debug_bridge_1.DeviceAndroidDebugBridge, { identifier: this.device.deviceInfo.identifier });
        return this.$injector.resolve(android_device_hash_service_1.AndroidDeviceHashService, { adb, appIdentifier });
    }
}
AndroidDeviceSocketsLiveSyncService.STATUS_UPDATE_INTERVAL = 10000;
exports.AndroidDeviceSocketsLiveSyncService = AndroidDeviceSocketsLiveSyncService;
