// Copyright 2026 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: simulated_clock.h
// -----------------------------------------------------------------------------

#ifndef ABSL_TIME_SIMULATED_CLOCK_H_
#define ABSL_TIME_SIMULATED_CLOCK_H_

#include <cstdint>
#include <map>
#include <memory>
#include <optional>

#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/base/nullability.h"
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/clock_interface.h"
#include "absl/time/time.h"

namespace absl {
ABSL_NAMESPACE_BEGIN

// A simulated clock is a concrete Clock implementation that does not "tick"
// on its own.  Time is advanced by explicit calls to the AdvanceTime() or
// SetTime() functions.
//
// Example:
//   absl::SimulatedClock sim_clock;
//   absl::Time now = sim_clock.TimeNow();
//   // now == absl::UnixEpoch()
//
//   now = sim_clock.TimeNow();
//   // now == absl::UnixEpoch() (still)
//
//   sim_clock.AdvanceTime(absl::Seconds(3));
//   now = sim_clock.TimeNow();
//   // now == absl::UnixEpoch() + absl::Seconds(3)
//
// This class is thread-safe.
class SimulatedClock : public Clock {
 public:
  explicit SimulatedClock(absl::Time t);
  SimulatedClock() : SimulatedClock(absl::UnixEpoch()) {}

  // The destructor should be called only if all Sleep(), etc. and
  // AdvanceTime() calls have completed. The code does its best to let
  // any pending calls finish gracefully, but there are no guarantees.
  ~SimulatedClock() override;

  // Returns the simulated time.
  absl::Time TimeNow() override;

  // Sleeps until the specified duration has elapsed according to this clock.
  void Sleep(absl::Duration d) override;

  // Sleeps until the specified wakeup_time.
  void SleepUntil(absl::Time wakeup_time) override;

  // Sets the simulated time to the argument.  Wakes up any threads whose
  // sleeps have now expired. Returns the number of woken threads.
  int64_t SetTime(absl::Time t);

  // Advances the simulated time by the specified duration.  Wakes up any
  // threads whose sleeps have now expired. Returns the number of woken threads.
  int64_t AdvanceTime(absl::Duration d);

  // Blocks until the condition is true or until the simulated clock is
  // advanced to or beyond the wakeup time (or both).
  bool AwaitWithDeadline(absl::Mutex* absl_nonnull mu,
                         const absl::Condition& cond,
                         absl::Time deadline) override
      ABSL_SHARED_LOCKS_REQUIRED(mu);

  // Returns the earliest wakeup time.
  std::optional<absl::Time> GetEarliestWakeupTime() const;

 private:
  template <class T>
  int64_t UpdateTime(const T& now_updater) ABSL_LOCKS_EXCLUDED(lock_);

  class WakeUpInfo;
  using WaiterList = std::multimap<absl::Time, std::shared_ptr<WakeUpInfo>>;

  mutable absl::Mutex lock_;
  absl::Time now_ ABSL_GUARDED_BY(lock_);
  WaiterList waiters_ ABSL_GUARDED_BY(lock_);
  int64_t num_await_calls_ ABSL_GUARDED_BY(lock_) = 0;
};

ABSL_NAMESPACE_END
}  // namespace absl

#endif  // ABSL_TIME_SIMULATED_CLOCK_H_
