"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.perfettoLocalService = exports.PerfettoLocalService = void 0;
const child_process_1 = require("child_process");
const path_1 = __importDefault(require("path"));
const promises_1 = __importDefault(require("fs/promises"));
const events_1 = require("events");
// Prevent unhandled error events from crashing the process
events_1.EventEmitter.defaultMaxListeners = 20;
class PerfettoLocalService extends events_1.EventEmitter {
    constructor() {
        super();
        this.process = null;
        this.port = 9001;
        this.currentTraceFile = null;
        this.startTime = null;
        // Use the trace_processor_shell - prefer locally built version with viz stdlib modules
        // Can be overridden via TRACE_PROCESSOR_PATH environment variable
        // Path: backend/src/services/ -> ../../../ -> perfetto/out/ui/
        this.traceProcessorPath = process.env.TRACE_PROCESSOR_PATH ||
            path_1.default.resolve(__dirname, '../../../perfetto/out/ui/trace_processor_shell');
        // Handle error events to prevent crashes
        this.on('error', (error) => {
            console.error('PerfettoLocalService error:', error);
        });
    }
    async startServer(traceFile) {
        if (this.process && !this.process.killed) {
            throw new Error('Perfetto server is already running');
        }
        // Find an available port
        this.port = await this.findAvailablePort(9001);
        const args = ['--httpd', `--http-port=${this.port}`];
        if (traceFile) {
            args.push(traceFile);
            this.currentTraceFile = traceFile;
        }
        console.log(`Starting Perfetto trace_processor with args:`, args);
        console.log(`Using trace_processor at: ${this.traceProcessorPath}`);
        return new Promise((resolve, reject) => {
            try {
                this.process = (0, child_process_1.spawn)(this.traceProcessorPath, args, {
                    stdio: ['pipe', 'pipe', 'pipe']
                });
                this.startTime = Date.now();
                if (!this.process.pid) {
                    reject(new Error('Failed to start Perfetto trace_processor'));
                    return;
                }
                console.log(`Perfetto trace_processor started with PID: ${this.process.pid}`);
                // Handle output
                this.process.stdout?.on('data', (data) => {
                    const output = data.toString();
                    console.log(`[Perfetto] ${output.trim()}`);
                    // Detect when server is ready
                    if (output.includes('HTTP server listening on')) {
                        this.emit('started', { port: this.port, pid: this.process?.pid ?? 0 });
                        resolve(this.getStatus());
                    }
                });
                this.process.stderr?.on('data', (data) => {
                    const error = data.toString().trim();
                    // Check if it's actually an error or just informational output
                    if (error.includes('[ERROR]') || error.includes('FATAL') || error.includes('Failed')) {
                        console.error(`[Perfetto Error] ${error}`);
                        this.emit('error', error);
                    }
                    else {
                        // Treat as informational output
                        console.log(`[Perfetto] ${error}`);
                    }
                });
                this.process.on('exit', (code) => {
                    console.log(`Perfetto trace_processor exited with code: ${code}`);
                    this.process = null;
                    this.currentTraceFile = null;
                    this.startTime = null;
                    this.emit('stopped', { code });
                });
                this.process.on('error', (error) => {
                    console.error('Failed to start Perfetto trace_processor:', error);
                    this.process = null;
                    // Don't emit 'error' event to avoid unhandled error crash
                    // Just reject the promise
                    reject(error);
                });
                // Check if the process started successfully after a short delay
                setTimeout(() => {
                    if (this.process && !this.process.killed) {
                        resolve(this.getStatus());
                    }
                    else {
                        reject(new Error('Perfetto trace_processor failed to start'));
                    }
                }, 5000); // 增加等待时间到5秒
            }
            catch (error) {
                reject(error);
            }
        });
    }
    async stopServer() {
        if (!this.process || this.process.killed) {
            return;
        }
        console.log('Stopping Perfetto trace_processor...');
        return new Promise((resolve) => {
            if (this.process) {
                this.process.on('exit', () => {
                    resolve();
                });
                this.process.kill('SIGTERM');
                // Force kill after 5 seconds
                setTimeout(() => {
                    if (this.process && !this.process.killed) {
                        this.process.kill('SIGKILL');
                    }
                }, 5000);
            }
            else {
                resolve();
            }
        });
    }
    async loadTrace(traceFile) {
        // Check if file exists
        try {
            await promises_1.default.access(traceFile);
        }
        catch (error) {
            throw new Error(`Trace file not found: ${traceFile}`);
        }
        // Always restart with new trace file
        await this.stopServer();
        return this.startServer(traceFile);
    }
    getStatus() {
        const uptime = this.startTime ? Date.now() - this.startTime : 0;
        const isRunning = !!(this.process && !this.process.killed);
        return {
            running: isRunning,
            port: isRunning ? this.port : undefined,
            pid: this.process?.pid,
            traceFile: this.currentTraceFile || undefined,
            uptime
        };
    }
    async findAvailablePort(startPort) {
        const net = await Promise.resolve().then(() => __importStar(require('net')));
        return new Promise((resolve, reject) => {
            const server = net.createServer();
            server.listen(startPort, () => {
                const port = server.address()?.port;
                server.close(() => {
                    resolve(port);
                });
            });
            server.on('error', () => {
                // Port is in use, try next port
                resolve(this.findAvailablePort(startPort + 1));
            });
        });
    }
    get getPort() {
        return this.port;
    }
    get getTraceProcessorUrl() {
        return `http://localhost:${this.port}`;
    }
}
exports.PerfettoLocalService = PerfettoLocalService;
// Singleton instance
exports.perfettoLocalService = new PerfettoLocalService();
//# sourceMappingURL=perfettoLocalService.js.map