"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 path = require("path");
const util = require("util");
const constants_1 = require("../../constants");
const helpers_1 = require("../../common/helpers");
class PlatformLiveSyncServiceBase {
    constructor($fs, $logger, $platformsData, $projectFilesManager, $devicePathProvider, $projectFilesProvider) {
        this.$fs = $fs;
        this.$logger = $logger;
        this.$platformsData = $platformsData;
        this.$projectFilesManager = $projectFilesManager;
        this.$devicePathProvider = $devicePathProvider;
        this.$projectFilesProvider = $projectFilesProvider;
        this._deviceLiveSyncServicesCache = {};
    }
    getDeviceLiveSyncService(device, projectData) {
        const platform = device.deviceInfo.platform.toLowerCase();
        const platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData);
        const frameworkVersion = platformData.platformProjectService.getFrameworkVersion(projectData);
        const key = helpers_1.getHash(`${device.deviceInfo.identifier}${projectData.projectIdentifiers[platform]}${projectData.projectDir}${frameworkVersion}`);
        if (!this._deviceLiveSyncServicesCache[key]) {
            this._deviceLiveSyncServicesCache[key] = this._getDeviceLiveSyncService(device, projectData, frameworkVersion);
        }
        return this._deviceLiveSyncServicesCache[key];
    }
    refreshApplication(projectData, liveSyncInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            if (liveSyncInfo.isFullSync || liveSyncInfo.modifiedFilesData.length) {
                const deviceLiveSyncService = this.getDeviceLiveSyncService(liveSyncInfo.deviceAppData.device, projectData);
                this.$logger.info(`Refreshing application on device ${liveSyncInfo.deviceAppData.device.deviceInfo.identifier}...`);
                yield deviceLiveSyncService.refreshApplication(projectData, liveSyncInfo);
            }
        });
    }
    fullSync(syncInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectData = syncInfo.projectData;
            const device = syncInfo.device;
            const deviceLiveSyncService = this.getDeviceLiveSyncService(device, syncInfo.projectData);
            const platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData);
            const deviceAppData = yield this.getAppData(syncInfo);
            if (deviceLiveSyncService.beforeLiveSyncAction) {
                yield deviceLiveSyncService.beforeLiveSyncAction(deviceAppData);
            }
            const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants_1.APP_FOLDER_NAME);
            const localToDevicePaths = yield this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, []);
            const modifiedFilesData = yield this.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, projectData, syncInfo.liveSyncDeviceInfo, { isFullSync: true, force: syncInfo.force });
            return {
                modifiedFilesData,
                isFullSync: true,
                deviceAppData,
                useHotModuleReload: syncInfo.useHotModuleReload
            };
        });
    }
    liveSyncWatchAction(device, liveSyncInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectData = liveSyncInfo.projectData;
            const deviceLiveSyncService = this.getDeviceLiveSyncService(device, projectData);
            const syncInfo = _.merge({ device, watch: true }, liveSyncInfo);
            const deviceAppData = yield this.getAppData(syncInfo);
            if (deviceLiveSyncService.beforeLiveSyncAction) {
                yield deviceLiveSyncService.beforeLiveSyncAction(deviceAppData);
            }
            let modifiedLocalToDevicePaths = [];
            if (liveSyncInfo.filesToSync.length) {
                const filesToSync = liveSyncInfo.filesToSync;
                const mappedFiles = _.map(filesToSync, filePath => this.$projectFilesProvider.mapFilePath(filePath, device.deviceInfo.platform, projectData));
                const existingFiles = mappedFiles.filter(m => m && this.$fs.exists(m));
                this.$logger.trace("Will execute livesync for files: ", existingFiles);
                const skippedFiles = _.difference(mappedFiles, existingFiles);
                if (skippedFiles.length) {
                    this.$logger.trace("The following files will not be synced as they do not exist:", skippedFiles);
                }
                if (existingFiles.length) {
                    const platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData);
                    const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants_1.APP_FOLDER_NAME);
                    const localToDevicePaths = yield this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, existingFiles, []);
                    modifiedLocalToDevicePaths.push(...localToDevicePaths);
                    modifiedLocalToDevicePaths = yield this.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, projectData, liveSyncInfo.liveSyncDeviceInfo, { isFullSync: false, force: liveSyncInfo.force });
                }
            }
            if (liveSyncInfo.filesToRemove.length) {
                const filePaths = liveSyncInfo.filesToRemove;
                const platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData);
                const mappedFiles = _(filePaths)
                    .map(filePath => this.$projectFilesProvider.mapFilePath(filePath, device.deviceInfo.platform, projectData))
                    .filter(filePath => !!filePath)
                    .value();
                const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants_1.APP_FOLDER_NAME);
                const localToDevicePaths = yield this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, mappedFiles, []);
                modifiedLocalToDevicePaths.push(...localToDevicePaths);
                yield deviceLiveSyncService.removeFiles(deviceAppData, localToDevicePaths, projectFilesPath);
            }
            return {
                modifiedFilesData: modifiedLocalToDevicePaths,
                isFullSync: liveSyncInfo.isReinstalled,
                deviceAppData,
                useHotModuleReload: liveSyncInfo.useHotModuleReload
            };
        });
    }
    transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, projectData, liveSyncDeviceInfo, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let transferredFiles = [];
            const deviceLiveSyncService = this.getDeviceLiveSyncService(deviceAppData.device, projectData);
            transferredFiles = yield deviceLiveSyncService.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, projectData, liveSyncDeviceInfo, options);
            this.logFilesSyncInformation(transferredFiles, "Successfully transferred %s on device %s.", this.$logger.info, deviceAppData.device.deviceInfo.identifier);
            return transferredFiles;
        });
    }
    getAppData(syncInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            const platform = syncInfo.device.deviceInfo.platform.toLowerCase();
            const appIdentifier = syncInfo.projectData.projectIdentifiers[platform];
            const deviceProjectRootOptions = _.assign({ appIdentifier }, syncInfo);
            return {
                appIdentifier,
                device: syncInfo.device,
                platform: syncInfo.device.deviceInfo.platform,
                getDeviceProjectRootPath: () => this.$devicePathProvider.getDeviceProjectRootPath(syncInfo.device, deviceProjectRootOptions),
                deviceSyncZipPath: this.$devicePathProvider.getDeviceSyncZipPath(syncInfo.device),
                isLiveSyncSupported: () => __awaiter(this, void 0, void 0, function* () { return true; }),
                connectTimeout: syncInfo.connectTimeout
            };
        });
    }
    logFilesSyncInformation(localToDevicePaths, message, action, deviceIdentifier) {
        if (localToDevicePaths && localToDevicePaths.length < 10) {
            _.each(localToDevicePaths, (file) => {
                action.call(this.$logger, util.format(message, path.basename(file.getLocalPath()).yellow), deviceIdentifier);
            });
        }
        else {
            action.call(this.$logger, util.format(message, "all files", deviceIdentifier));
        }
    }
}
exports.PlatformLiveSyncServiceBase = PlatformLiveSyncServiceBase;
