"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 android_device_livesync_service_base_1 = require("./android-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");
const semver = require("semver");
class AndroidDeviceSocketsLiveSyncService extends android_device_livesync_service_base_1.AndroidDeviceLiveSyncServiceBase {
    constructor(data, $injector, $platformsData, $staticConfig, $logger, device, $options, $processService, $fs, $devicePlatformsConstants, $filesHashService) {
        super($injector, $platformsData, $filesHashService, $logger, device);
        this.data = data;
        this.$platformsData = $platformsData;
        this.$staticConfig = $staticConfig;
        this.device = device;
        this.$options = $options;
        this.$processService = $processService;
        this.$fs = $fs;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.livesyncTool = this.$injector.resolve(android_livesync_tool_1.AndroidLivesyncTool);
    }
    beforeLiveSyncAction(deviceAppData) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.livesyncTool.hasConnection()) {
                try {
                    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(this.data.projectIdentifiers.android, deviceAppData.connectTimeout);
                }
                catch (err) {
                    yield this.device.fileSystem.deleteFile(this.getPathToLiveSyncFileOnDevice(deviceAppData.appIdentifier), deviceAppData.appIdentifier);
                    throw err;
                }
            }
        });
    }
    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);
                if (!semver.gte(this.livesyncTool.protocolVersion, AndroidDeviceSocketsLiveSyncService.MINIMAL_VERSION_LONG_LIVING_CONNECTION)) {
                    this.livesyncTool.end();
                }
                return result;
            }
            catch (e) {
                this.livesyncTool.end();
                throw e;
            }
        });
    }
    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, liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform);
                const doSyncPromise = this.livesyncTool.sendDoSyncOperation({ doRefresh: canExecuteFastSync, 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 result = { didRestart: false };
            const canExecuteFastSync = !liveSyncInfo.isFullSync && this.canExecuteFastSyncForPaths(liveSyncInfo, liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform);
            if (!canExecuteFastSync || !liveSyncInfo.didRefresh) {
                yield this.device.applicationManager.restartApplication({ appId: liveSyncInfo.deviceAppData.appIdentifier, projectName: projectData.projectName });
                if (!this.$options.justlaunch && this.livesyncTool.protocolVersion && semver.gte(this.livesyncTool.protocolVersion, AndroidDeviceSocketsLiveSyncService.MINIMAL_VERSION_LONG_LIVING_CONNECTION)) {
                    try {
                        yield this.connectLivesyncTool(liveSyncInfo.deviceAppData.appIdentifier);
                    }
                    catch (e) {
                        this.$logger.trace("Failed to connect after app restart.");
                    }
                }
                result.didRestart = true;
            }
            return result;
        });
    }
    removeFiles(deviceAppData, localToDevicePaths, projectFilesPath) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.livesyncTool.removeFiles(_.map(localToDevicePaths, (element) => element.filePath));
            const deviceHashService = this.device.fileSystem.getDeviceHashService(deviceAppData.appIdentifier);
            yield deviceHashService.removeHashes(localToDevicePaths);
        });
    }
    transferFilesOnDevice(deviceAppData, localToDevicePaths) {
        return __awaiter(this, void 0, void 0, function* () {
            const files = _.map(localToDevicePaths, localToDevicePath => localToDevicePath.getLocalPath());
            yield this.livesyncTool.sendFiles(files);
        });
    }
    transferDirectoryOnDevice(deviceAppData, localToDevicePaths, projectFilesPath) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.livesyncTool.sendDirectory(projectFilesPath);
        });
    }
    connectLivesyncTool(appIdentifier, connectTimeout) {
        return __awaiter(this, void 0, void 0, function* () {
            const platformData = this.$platformsData.getPlatformData(this.$devicePlatformsConstants.Android, this.data);
            const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants_1.APP_FOLDER_NAME);
            if (!this.livesyncTool.hasConnection()) {
                yield this.livesyncTool.connect({
                    appIdentifier,
                    deviceIdentifier: this.device.deviceInfo.identifier,
                    appPlatformsPath: projectFilesPath,
                    connectTimeout
                });
            }
        });
    }
}
AndroidDeviceSocketsLiveSyncService.STATUS_UPDATE_INTERVAL = 10000;
AndroidDeviceSocketsLiveSyncService.MINIMAL_VERSION_LONG_LIVING_CONNECTION = "0.2.0";
exports.AndroidDeviceSocketsLiveSyncService = AndroidDeviceSocketsLiveSyncService;
