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

#ifndef QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_
#define QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_

#include "quiche/quic/core/quic_alarm.h"
#include "quiche/quic/core/quic_alarm_factory.h"
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_one_block_arena.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/platform/api/quic_export.h"

namespace quic {

namespace test {
class QuicConnectionPeer;
class QuicPingManagerPeer;
}  // namespace test

// QuicPingManager manages an alarm that has two modes:
// 1) keep-alive. When alarm fires, send packet to extend idle timeout to keep
// connection alive.
// 2) retransmittable-on-wire. When alarm fires, send packets to detect path
// degrading (used in IP/port migrations).
class QUIC_EXPORT_PRIVATE QuicPingManager {
 public:
  // Interface that get notified when |alarm_| fires.
  class QUIC_EXPORT_PRIVATE Delegate {
   public:
    virtual ~Delegate() {}

    // Called when alarm fires in keep-alive mode.
    virtual void OnKeepAliveTimeout() = 0;
    // Called when alarm fires in retransmittable-on-wire mode.
    virtual void OnRetransmittableOnWireTimeout() = 0;
  };

  QuicPingManager(Perspective perspective, Delegate* delegate,
                  QuicConnectionArena* arena, QuicAlarmFactory* alarm_factory,
                  QuicConnectionContext* context);

  // Called to set |alarm_|.
  void SetAlarm(QuicTime now, bool should_keep_alive,
                bool has_in_flight_packets);

  // Called when |alarm_| fires.
  void OnAlarm();

  // Called to stop |alarm_| permanently.
  void Stop();

  void set_keep_alive_timeout(QuicTime::Delta keep_alive_timeout) {
    QUICHE_DCHECK(!alarm_->IsSet());
    keep_alive_timeout_ = keep_alive_timeout;
  }

  void set_initial_retransmittable_on_wire_timeout(
      QuicTime::Delta retransmittable_on_wire_timeout) {
    QUICHE_DCHECK(!alarm_->IsSet());
    initial_retransmittable_on_wire_timeout_ = retransmittable_on_wire_timeout;
  }

  void reset_consecutive_retransmittable_on_wire_count() {
    consecutive_retransmittable_on_wire_count_ = 0;
  }

 private:
  friend class test::QuicConnectionPeer;
  friend class test::QuicPingManagerPeer;

  // Update |retransmittable_on_wire_deadline_| and |keep_alive_deadline_|.
  void UpdateDeadlines(QuicTime now, bool should_keep_alive,
                       bool has_in_flight_packets);

  // Get earliest deadline of |retransmittable_on_wire_deadline_| and
  // |keep_alive_deadline_|. Returns 0 if both deadlines are not initialized.
  QuicTime GetEarliestDeadline() const;

  Perspective perspective_;

  Delegate* delegate_;  // Not owned.

  // Initial timeout for how long the wire can have no retransmittable packets.
  QuicTime::Delta initial_retransmittable_on_wire_timeout_ =
      QuicTime::Delta::Infinite();

  // Indicates how many consecutive retransmittable-on-wire has been armed
  // (since last reset).
  int consecutive_retransmittable_on_wire_count_ = 0;

  // Indicates how many retransmittable-on-wire has been armed in total.
  int retransmittable_on_wire_count_ = 0;

  QuicTime::Delta keep_alive_timeout_ =
      QuicTime::Delta::FromSeconds(kPingTimeoutSecs);

  QuicTime retransmittable_on_wire_deadline_ = QuicTime::Zero();

  QuicTime keep_alive_deadline_ = QuicTime::Zero();

  QuicArenaScopedPtr<QuicAlarm> alarm_;
};

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_
