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

#include "quiche/quic/test_tools/simulator/alarm_factory.h"

#include <algorithm>
#include <string>
#include <utility>

#include "absl/strings/str_format.h"
#include "quiche/quic/core/quic_alarm.h"

namespace quic {
namespace simulator {

// Alarm is an implementation of QuicAlarm which can schedule alarms in the
// simulation timeline.
class Alarm : public QuicAlarm {
 public:
  Alarm(Simulator* simulator, std::string name,
        QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
      : QuicAlarm(std::move(delegate)), adapter_(simulator, name, this) {}
  ~Alarm() override {}

  void SetImpl() override {
    QUICHE_DCHECK(deadline().IsInitialized());
    adapter_.Set(deadline());
  }

  void CancelImpl() override { adapter_.Cancel(); }

 private:
  // An adapter class triggering a QuicAlarm using a simulation time system.
  // An adapter is required here because neither Actor nor QuicAlarm are pure
  // interfaces.
  class Adapter : public Actor {
   public:
    Adapter(Simulator* simulator, std::string name, Alarm* parent)
        : Actor(simulator, name), parent_(parent) {}
    ~Adapter() override {}

    void Set(QuicTime time) { Schedule(std::max(time, clock_->Now())); }
    void Cancel() { Unschedule(); }

    void Act() override {
      QUICHE_DCHECK(clock_->Now() >= parent_->deadline());
      parent_->Fire();
    }

   private:
    Alarm* parent_;
  };
  Adapter adapter_;
};

AlarmFactory::AlarmFactory(Simulator* simulator, std::string name)
    : simulator_(simulator), name_(std::move(name)), counter_(0) {}

AlarmFactory::~AlarmFactory() {}

std::string AlarmFactory::GetNewAlarmName() {
  ++counter_;
  return absl::StrFormat("%s (alarm %i)", name_, counter_);
}

QuicAlarm* AlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
  return new Alarm(simulator_, GetNewAlarmName(),
                   QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
}

QuicArenaScopedPtr<QuicAlarm> AlarmFactory::CreateAlarm(
    QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
    QuicConnectionArena* arena) {
  if (arena != nullptr) {
    return arena->New<Alarm>(simulator_, GetNewAlarmName(),
                             std::move(delegate));
  }
  return QuicArenaScopedPtr<QuicAlarm>(
      new Alarm(simulator_, GetNewAlarmName(), std::move(delegate)));
}

}  // namespace simulator
}  // namespace quic
