"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
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 decorators_1 = require("../../decorators");
const os_1 = require("os");
class AndroidDebugBridge {
    constructor($childProcess, $errors, $logger, $staticConfig, $androidDebugBridgeResultHandler) {
        this.$childProcess = $childProcess;
        this.$errors = $errors;
        this.$logger = $logger;
        this.$staticConfig = $staticConfig;
        this.$androidDebugBridgeResultHandler = $androidDebugBridgeResultHandler;
        this.adbFilePath = null;
    }
    init() {
        return __awaiter(this, void 0, void 0, function* () {
            this.adbFilePath = yield this.$staticConfig.getAdbFilePath();
        });
    }
    executeCommand(args, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let event = "close";
            const deviceIdentifier = options && options.deviceIdentifier;
            const command = yield this.composeCommand(args, deviceIdentifier);
            let treatErrorsAsWarnings = false;
            let childProcessOptions = undefined;
            if (options) {
                event = options.fromEvent || event;
                treatErrorsAsWarnings = options.treatErrorsAsWarnings;
                childProcessOptions = options.childProcessOptions;
                if (options.returnChildProcess) {
                    return this.$childProcess.spawn(command.command, command.args);
                }
            }
            const result = yield this.$childProcess.spawnFromEvent(command.command, command.args, event, childProcessOptions, { throwError: false });
            const errors = this.$androidDebugBridgeResultHandler.checkForErrors(result);
            if (errors && errors.length > 0) {
                this.$androidDebugBridgeResultHandler.handleErrors(errors, treatErrorsAsWarnings);
            }
            return (result.stdout === undefined || result.stdout === null) ? result : result.stdout;
        });
    }
    getPropertyValue(deviceId, propertyName) {
        return this.$childProcess.execFile(this.adbFilePath, ["-s", deviceId, "shell", "getprop", propertyName]);
    }
    getDevices() {
        return __awaiter(this, void 0, void 0, function* () {
            const result = yield this.executeCommand(["devices"], { returnChildProcess: true });
            return new Promise((resolve, reject) => {
                let adbData = "";
                let errorData = "";
                let isSettled = false;
                result.stdout.on("data", (data) => {
                    adbData += data.toString();
                });
                result.stderr.on("data", (data) => {
                    errorData += (data || "").toString();
                });
                result.on("error", (error) => {
                    if (reject && !isSettled) {
                        isSettled = true;
                        reject(error);
                    }
                });
                result.on("close", (exitCode) => __awaiter(this, void 0, void 0, function* () {
                    if (errorData && !isSettled) {
                        isSettled = true;
                        reject(errorData);
                        return;
                    }
                    if (!isSettled) {
                        isSettled = true;
                        const adbDevices = adbData
                            .split(os_1.EOL)
                            .filter(line => !!line && line.indexOf("List of devices attached") === -1 && line.indexOf("* daemon ") === -1 && line.indexOf("adb server") === -1);
                        resolve(adbDevices);
                    }
                }));
            });
        });
    }
    getDevicesSafe() {
        return __awaiter(this, void 0, void 0, function* () {
            let adbDevices = [];
            try {
                adbDevices = yield this.getDevices();
            }
            catch (err) {
                this.$logger.trace(`Getting adb devices failed with error: ${err}`);
            }
            return adbDevices;
        });
    }
    composeCommand(params, identifier) {
        return __awaiter(this, void 0, void 0, function* () {
            const command = yield this.$staticConfig.getAdbFilePath();
            let deviceIdentifier = [];
            if (identifier) {
                deviceIdentifier = ["-s", `${identifier}`];
            }
            const args = deviceIdentifier.concat(params);
            return { command, args };
        });
    }
    executeShellCommand(args, options) {
        return __awaiter(this, void 0, void 0, function* () {
            args.unshift("shell");
            const result = yield this.executeCommand(args, options);
            return result;
        });
    }
    pushFile(localFilePath, deviceFilePath) {
        return __awaiter(this, void 0, void 0, function* () {
            const fileDirectory = path.normalize(path.dirname(deviceFilePath));
            yield this.executeShellCommand(["mkdir", "-p", fileDirectory]);
            yield this.executeCommand(["push", localFilePath, deviceFilePath]);
            yield this.executeShellCommand(["chmod", "0777", fileDirectory]);
        });
    }
    removeFile(deviceFilePath) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.executeShellCommand(["rm", "-rf", deviceFilePath]);
        });
    }
}
__decorate([
    decorators_1.cache()
], AndroidDebugBridge.prototype, "init", null);
__decorate([
    decorators_1.invokeInit()
], AndroidDebugBridge.prototype, "getPropertyValue", null);
__decorate([
    decorators_1.invokeInit()
], AndroidDebugBridge.prototype, "getDevices", null);
exports.AndroidDebugBridge = AndroidDebugBridge;
$injector.register("adb", AndroidDebugBridge);
