/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2024, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */
#pragma once

#include <chrono>

namespace mrpt
{
/** C++11-clock that is compatible with MRPT TTimeStamp representation
 *
 * State-aware methods in this class are thread-safe.
 *
 * \ingroup mrpt_core_grp
 */
class Clock
{
 public:
  using rep = int64_t;
  // 100-nanoseconds
  using period = std::ratio<1, 10000000>;
  using duration = std::chrono::duration<rep, period>;
  using time_point = std::chrono::time_point<Clock>;

  static constexpr bool is_steady = std::chrono::system_clock::is_steady;

  /** Returns the current time using the currently selected Clock source.
   *  (Performance: call typ. takes 33 nanoseconds)
   * \sa setActiveClock(), mrpt::Clock::Source, nowDouble()  */
  static time_point now() noexcept;

  /** Equivalent to `Clock::toDouble(Clock::now())`.
   *  (Performance: call typ. takes 38 nanoseconds)
   * \sa setActiveClock(), mrpt::Clock::Source, now()
   * \note (New in MRPT 2.1.5)
   */
  inline static double nowDouble() noexcept { return toDouble(now()); }

  /** Create a timestamp from its double representation. \sa toDouble */
  static time_point fromDouble(const double t) noexcept;

  /** Converts a timestamp to a UNIX time_t-like number, with fractional part.
   *  If t is an invalid (default-contructed) time_point, `0.0` will be
   * returned.
   * \sa fromDouble */
  static double toDouble(const time_point t) noexcept;

  /** Options for setting the source of all timestamps across MRPT:
   * setActiveClock(), now()
   */
  enum Source
  {
    /** Realtime: POSIX `CLOCK_REALTIME`. The highest resolution available
     *  Typically this is better than 1 microsecond. Selected by default.
     */
    Realtime = 0,

    /** Monotonic: POSIX `CLOCK_MONOTONIC`. Only available in Linux systems.
     */
    Monotonic,

    /** Simulated time: Manual control of time via setSimulatedTime()
     * [New in MRPT 2.1.1]
     */
    Simulated
  };

  /** Changes the selected clock to get time from when calling now().
   * Default: Realtime.
   *
   * Monotonic is only available on Linux systems.
   * RealTime and Simulated are available on any platform.
   *
   * It is strongly recommended to call setSimulatedTime()
   * before setting the clock source to Simulated to ensure that any
   * subsequent call to now(), perhaps in a parallel thread, does not
   * return an undefined time_point value.
   */
  static void setActiveClock(const Source s);

  /** Returns the currently selected clock. */
  static Source getActiveClock();

  /** Monotonic clock *might* drift over time with respect to Realtime.
   * The first time a time is requested to now() with Monotonic clock enabled,
   * MRPT internally saves the current values of both, Realtime and Monotonic
   * clocks, then use the difference in all subsequent calls to set Monotonic
   * timepoints in the same epoch than Realtime.
   *
   * By explicitly calling this static method, a user is able to force a
   * resynchrnization between the two clocks. Potentially useful for systems
   * that run for very long periods of time without interruption.
   *
   * \return The mismatch between the former and the new estimations of the
   * epochs differences between the two clocks, in units of nanoseconds.
   *
   * \note This method is ignored in non-Linux systems.
   */
  static int64_t resetMonotonicToRealTimeEpoch() noexcept;

  /** Returns the number of nanoseconds that are added to the output of the
   * POSIX `CLOCK_MONOTONIC` to make timestamps match the epoch of POSIX
   * `CLOCK_REALTIME`. */
  [[nodiscard]] static uint64_t getMonotonicToRealtimeOffset();

  /** When setActiveClock() is set to `Simulated`, sets the simulated time
   * that will be returned in subsequent calls to now().
   * [New in MRPT 2.1.1]
   */
  static void setSimulatedTime(const time_point& t);
};
}  // namespace mrpt
