// Copyright (c) 2011, 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.

part of "dart:core";

/// A stopwatch which measures time while it's running.
///
/// A stopwatch is either running or stopped.
/// It measures the elapsed time that passes while the stopwatch is running.
///
/// When a stopwatch is initially created, it is stopped and has measured no
/// elapsed time.
///
/// The elapsed time can be accessed in various formats using
/// [elapsed], [elapsedMilliseconds], [elapsedMicroseconds] or [elapsedTicks].
///
/// The stopwatch is started by calling [start].
///
/// Example:
/// ```dart
/// final stopwatch = Stopwatch();
/// print(stopwatch.elapsedMilliseconds); // 0
/// print(stopwatch.isRunning); // false
/// stopwatch.start();
/// print(stopwatch.isRunning); // true
/// ```
/// To stop or pause the stopwatch, use [stop].
/// Use [start] to continue again when only pausing temporarily.
/// ```
/// stopwatch.stop();
/// print(stopwatch.isRunning); // false
/// Duration elapsed = stopwatch.elapsed;
/// await Future.delayed(const Duration(seconds: 1));
/// assert(stopwatch.elapsed == elapsed); // No measured time elapsed.
/// stopwatch.start(); // Continue measuring.
/// ```
/// The [reset] method sets the elapsed time back to zero.
/// It can be called whether the stopwatch is running or not,
/// and doesn't change whether it's running.
/// ```
/// // Do some work.
/// stopwatch.stop();
/// print(stopwatch.elapsedMilliseconds); // Likely > 0.
/// stopwatch.reset();
/// print(stopwatch.elapsedMilliseconds); // 0
/// ```
class Stopwatch {
  /// Cached frequency of the system in Hz (ticks per second).
  ///
  /// Value must be returned by [_initTicker], which is called only once.
  @pragma("vm:shared")
  static final int _frequency = _initTicker();

  // The _start and _stop fields capture the time when [start] and [stop]
  // are called respectively.
  // If _stop is null, the stopwatch is running.
  int _start = 0;
  int? _stop = 0;

  /// Creates a [Stopwatch] in stopped state with a zero elapsed count.
  ///
  /// The following example shows how to start a [Stopwatch]
  /// immediately after allocation.
  /// ```dart
  /// final stopwatch = Stopwatch()..start();
  /// ```
  Stopwatch() {
    _frequency; // Ensures initialization before using any method.
  }

  /// Frequency of the elapsed counter in Hz.
  int get frequency => _frequency;

  /// Starts the [Stopwatch].
  ///
  /// The [elapsed] count increases monotonically. If the [Stopwatch] has
  /// been stopped, then calling start again restarts it without resetting the
  /// [elapsed] count.
  ///
  /// If the [Stopwatch] is currently running, then calling start does nothing.
  void start() {
    int? stop = _stop;
    if (stop != null) {
      // (Re)start this stopwatch.
      // Don't count the time while the stopwatch has been stopped.
      _start += _now() - stop;
      _stop = null;
    }
  }

  /// Stops the [Stopwatch].
  ///
  /// The [elapsedTicks] count stops increasing after this call. If the
  /// [Stopwatch] is currently not running, then calling this method has no
  /// effect.
  void stop() {
    _stop ??= _now();
  }

  /// Resets the [elapsed] count to zero.
  ///
  /// This method does not stop or start the [Stopwatch].
  void reset() {
    _start = _stop ?? _now();
  }

  /// The elapsed number of clock ticks since calling [start] while the
  /// [Stopwatch] is running.
  ///
  /// This is the elapsed number of clock ticks between calling [start] and
  /// calling [stop].
  ///
  /// Is 0 if the [Stopwatch] has never been started.
  ///
  /// The elapsed number of clock ticks increases by [frequency] every second.
  int get elapsedTicks {
    return (_stop ?? _now()) - _start;
  }

  /// The [elapsedTicks] counter converted to a [Duration].
  Duration get elapsed {
    return Duration(microseconds: elapsedMicroseconds);
  }

  /// The [elapsedTicks] counter converted to microseconds.
  external int get elapsedMicroseconds;

  /// The [elapsedTicks] counter converted to milliseconds.
  external int get elapsedMilliseconds;

  /// Whether the [Stopwatch] is currently running.
  bool get isRunning => _stop == null;

  /// Initializes the time-measuring system. *Must* return the [_frequency]
  /// variable. May do other necessary initialization.
  external static int _initTicker();
  external static int _now();
}
