// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';
import 'dart:convert';

import 'package:path/path.dart' as path;

import 'snapshot_test_helper.dart';

Future<List<TimelineEvent>> runAndCollectTimeline(
  String streams,
  List<String> args,
) async {
  return await withTempDir("timeline-test", (String tmp) async {
    final String timelinePath = path.join(tmp, 'timeline.json');
    final p = await Process.run(Platform.executable, [
      ...Platform.executableArguments,
      '--trace_timeline',
      '--timeline_recorder=file:$timelinePath',
      '--timeline_streams=$streams',
      Platform.script.toFilePath(),
      ...args,
    ]);
    print(p.stdout);
    print(p.stderr);
    if (p.exitCode != 0) {
      throw 'Child process failed: ${p.exitCode}';
    }
    // On Android, --trace_timeline goes to syslog instead of stderr.
    if (!Platform.isAndroid) {
      if (!p.stderr.contains('Using the File timeline recorder')) {
        throw 'Failed to select file recorder';
      }
    }

    final timeline = jsonDecode(await new File(timelinePath).readAsString());
    if (timeline is! List) throw 'Timeline should be a JSON list';

    return parseTimeline(timeline);
  });
}

List<TimelineEvent> parseTimeline(List l) {
  final events = <TimelineEvent>[];

  for (final event in l) {
    events.add(TimelineEvent.from(event));
  }
  return events;
}

String findMainIsolateId(List<TimelineEvent> events) {
  return events
      .firstWhere(
        (e) => e.name == 'InitializeIsolate' && e.args['isolateName'] == 'main',
      )
      .isolateId!;
}

class TimelineEvent {
  final String name;
  final String cat;
  final int tid;
  final int pid;
  final int ts;
  final int? tts;
  final String ph;
  final Map<String, String> args;

  TimelineEvent._(
    this.name,
    this.cat,
    this.tid,
    this.pid,
    this.ts,
    this.tts,
    this.ph,
    this.args,
  );

  factory TimelineEvent.from(Map m) {
    return TimelineEvent._(
      m['name'] as String,
      m['cat'] as String,
      m['tid'] as int,
      m['pid'] as int,
      m['ts'] as int,
      m['tts'] as int?,
      m['ph'] as String,
      m['args'].cast<String, String>(),
    );
  }

  bool get isStart => ph == 'B';
  bool get isEnd => ph == 'E';

  String? get isolateId => args['isolateId'];

  String toString() =>
      'TimelineEvent($name, $cat, $tid, $pid, $ts, $tts, $ph, $args)';
}
