// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'percentile_utils.dart';
import 'timeline.dart';

const String _kPlatformVsyncEvent = 'VSYNC';
const String _kUIThreadVsyncProcessEvent = 'VsyncProcessCallback';

/// Event names for frame lag related timeline events.
const Set<String> kVsyncTimelineEventNames = <String>{
  _kUIThreadVsyncProcessEvent,
  _kPlatformVsyncEvent,
};

/// Summarizes [TimelineEvents]s corresponding to [kVsyncTimelineEventNames] events.
///
/// `VsyncFrameLag` is the time between when a platform vsync event is received to
/// when the frame starts getting processed by the Flutter Engine. This delay is
/// typically seen due to non-frame workload related dart tasks being scheduled
/// on the UI thread.
class VsyncFrameLagSummarizer {
  /// Creates a VsyncFrameLagSummarizer given the timeline events.
  VsyncFrameLagSummarizer(this.vsyncEvents);

  /// Timeline events with names in [kVsyncTimelineEventNames].
  final List<TimelineEvent> vsyncEvents;

  /// Computes the average `VsyncFrameLag` over the period of the timeline.
  double computeAverageVsyncFrameLag() {
    final List<double> vsyncFrameLags =
        _computePlatformToFlutterVsyncBeginLags();
    if (vsyncFrameLags.isEmpty) {
      return 0;
    }

    final double total = vsyncFrameLags.reduce((double a, double b) => a + b);
    return total / vsyncFrameLags.length;
  }

  /// Computes the [percentile]-th percentile `VsyncFrameLag` over the
  /// period of the timeline.
  double computePercentileVsyncFrameLag(double percentile) {
    final List<double> vsyncFrameLags =
        _computePlatformToFlutterVsyncBeginLags();
    if (vsyncFrameLags.isEmpty) {
      return 0;
    }
    return findPercentile(vsyncFrameLags, percentile);
  }

  List<double> _computePlatformToFlutterVsyncBeginLags() {
    int platformIdx = -1;
    final List<double> result = <double>[];
    for (int i = 0; i < vsyncEvents.length; i++) {
      final TimelineEvent event = vsyncEvents[i];
      if (event.phase != 'B') {
        continue;
      }
      if (event.name == _kPlatformVsyncEvent) {
        // There was a vsync that resulted in a frame not being built.
        // This needs to be penalized.
        if (platformIdx != -1) {
          final int prevTS = vsyncEvents[platformIdx].timestampMicros!;
          result.add((event.timestampMicros! - prevTS).toDouble());
        }
        platformIdx = i;
      } else if (platformIdx != -1) {
        final int platformTS = vsyncEvents[platformIdx].timestampMicros!;
        result.add((event.timestampMicros! - platformTS).toDouble());
        platformIdx = -1;
      }
    }
    return result;
  }
}