"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogcatHelper = void 0;
const byline = require("byline");
const semver = require("semver");
const yok_1 = require("../../yok");
const device_android_debug_bridge_1 = require("./device-android-debug-bridge");
class LogcatHelper {
    constructor($deviceLogProvider, $devicePlatformsConstants, $logger, $injector, $devicesService) {
        this.$deviceLogProvider = $deviceLogProvider;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.$logger = $logger;
        this.$injector = $injector;
        this.$devicesService = $devicesService;
        this.mapDevicesLoggingData = Object.create(null);
    }
    start(options) {
        return __awaiter(this, void 0, void 0, function* () {
            const deviceIdentifier = options.deviceIdentifier;
            if (deviceIdentifier && !this.mapDevicesLoggingData[deviceIdentifier]) {
                this.mapDevicesLoggingData[deviceIdentifier] = {
                    loggingProcess: null,
                    lineStream: null,
                    keepSingleProcess: options.keepSingleProcess,
                    appStartTrackingProcess: null,
                    rawLineStream: null,
                };
                const logcatStream = yield this.getLogcatStream(deviceIdentifier, options.pid);
                const lineStream = byline(logcatStream.stdout);
                this.mapDevicesLoggingData[deviceIdentifier].loggingProcess =
                    logcatStream;
                this.mapDevicesLoggingData[deviceIdentifier].lineStream = lineStream;
                logcatStream.stderr.on("data", (data) => {
                    this.$logger.trace("ADB logcat stderr: " + data.toString());
                });
                logcatStream.on("close", (code) => {
                    try {
                        this.forceStop(deviceIdentifier);
                        if (code !== 0) {
                            this.$logger.trace("ADB process exited with code " + code.toString());
                        }
                    }
                    catch (err) {
                    }
                });
                lineStream.on("data", (lineBuffer) => {
                    const lines = (lineBuffer.toString() || "").split("\n");
                    for (const line of lines) {
                        this.$deviceLogProvider.logData(line, this.$devicePlatformsConstants.Android, deviceIdentifier);
                    }
                });
                const appStartTrackingStream = yield this.getAppStartTrackingLogcatStream(deviceIdentifier, options.appId);
                this.mapDevicesLoggingData[deviceIdentifier].appStartTrackingProcess =
                    appStartTrackingStream;
                const rawLineStream = byline(appStartTrackingStream.stdout);
                this.mapDevicesLoggingData[deviceIdentifier].rawLineStream =
                    rawLineStream;
                rawLineStream.on("data", (lineBuffer) => {
                    var _a, _b;
                    if (!((_a = this.mapDevicesLoggingData[deviceIdentifier]) === null || _a === void 0 ? void 0 : _a.loggingProcess))
                        return;
                    const lines = (lineBuffer.toString() || "").split("\n");
                    for (let line of lines) {
                        if (line.includes("START") &&
                            line.includes(options.appId) &&
                            !line.includes(options.pid)) {
                            this.forceStop(deviceIdentifier);
                            (_b = options.onAppRestarted) === null || _b === void 0 ? void 0 : _b.call(options);
                        }
                    }
                });
            }
        });
    }
    dump(deviceIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            const adb = this.$injector.resolve(device_android_debug_bridge_1.DeviceAndroidDebugBridge, { identifier: deviceIdentifier });
            const logcatDumpStream = yield adb.executeCommand(["logcat", "-d"], {
                returnChildProcess: true,
            });
            const lineStream = byline(logcatDumpStream.stdout);
            lineStream.on("data", (line) => {
                const lineText = line.toString();
                this.$logger.trace(lineText);
            });
            logcatDumpStream.on("close", (code) => {
                logcatDumpStream.removeAllListeners();
                lineStream.removeAllListeners();
            });
        });
    }
    stop(deviceIdentifier) {
        if (this.mapDevicesLoggingData[deviceIdentifier] &&
            !this.mapDevicesLoggingData[deviceIdentifier].keepSingleProcess) {
            this.forceStop(deviceIdentifier);
        }
    }
    forceStop(deviceIdentifier) {
        var _a, _b, _c, _d, _e;
        const loggingData = this.mapDevicesLoggingData[deviceIdentifier];
        (_a = loggingData.loggingProcess) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
        (_b = loggingData.loggingProcess) === null || _b === void 0 ? void 0 : _b.kill("SIGINT");
        (_c = loggingData.lineStream) === null || _c === void 0 ? void 0 : _c.removeAllListeners();
        (_d = loggingData.appStartTrackingProcess) === null || _d === void 0 ? void 0 : _d.kill("SIGINT");
        (_e = loggingData.lineStream) === null || _e === void 0 ? void 0 : _e.removeAllListeners();
        delete this.mapDevicesLoggingData[deviceIdentifier];
    }
    isLogcatPidSupported(deviceIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            const device = yield this.$devicesService.getDevice(deviceIdentifier);
            const minAndroidWithLogcatPidSupport = "7.0.0";
            return (!!device.deviceInfo.version &&
                semver.gte(semver.coerce(device.deviceInfo.version), minAndroidWithLogcatPidSupport));
        });
    }
    getLogcatStream(deviceIdentifier, pid) {
        return __awaiter(this, void 0, void 0, function* () {
            const isLogcatPidSupported = yield this.isLogcatPidSupported(deviceIdentifier);
            const adb = this.$injector.resolve(device_android_debug_bridge_1.DeviceAndroidDebugBridge, { identifier: deviceIdentifier });
            const logcatCommand = ["logcat", "-T", "1"];
            const acceptedTags = [
                "chromium",
                '"Web Console"',
                "JS",
                "System.err",
                "TNS.Native",
                "TNS.Java",
            ];
            if (pid && isLogcatPidSupported) {
                logcatCommand.push(`--pid=${pid}`);
                acceptedTags.forEach((tag) => {
                    logcatCommand.push("-s", tag);
                });
            }
            const logcatStream = yield adb.executeCommand(logcatCommand, {
                returnChildProcess: true,
            });
            return logcatStream;
        });
    }
    getAppStartTrackingLogcatStream(deviceIdentifier, appId) {
        return __awaiter(this, void 0, void 0, function* () {
            const adb = this.$injector.resolve(device_android_debug_bridge_1.DeviceAndroidDebugBridge, { identifier: deviceIdentifier });
            const logcatCommand = [`logcat`, `-b`, `system`, `-T`, `1`];
            if (appId) {
                logcatCommand.push(`--regex=START.*${appId}`);
            }
            const appStartTrackingStream = yield adb.executeCommand(logcatCommand, {
                returnChildProcess: true,
            });
            return appStartTrackingStream;
        });
    }
}
exports.LogcatHelper = LogcatHelper;
yok_1.injector.register("logcatHelper", LogcatHelper);
