/** @file
 *
 *  A brief file description
 *
 *  @section license License
 *
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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
 *
 *      http://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.
 */

#pragma once

#include "I_EventSystem.h"
#include "QUICTypes.h"
#include "QUICFrame.h"
#include "QUICFrameGenerator.h"
#include "QUICLossDetector.h"

class QUICRateAnalyzer
{
public:
  void update(QUICOffset offset);
  uint64_t expect_recv_bytes(ink_hrtime time) const;

private:
  double _rate           = 0.0;
  ink_hrtime _start_time = ink_get_hrtime();
};

class QUICFlowController : public QUICFrameGenerator
{
public:
  uint64_t credit() const;
  QUICOffset current_offset() const;
  virtual QUICOffset current_limit() const;

  /*
   * Returns 0 if succeed
   */
  virtual int update(QUICOffset offset);
  virtual void forward_limit(QUICOffset limit);

  /**
   * This is only for flow controllers initialized without a limit (== UINT64_MAX).
   * Once a limit is set, it should be updated with forward_limit().
   */
  virtual void set_limit(QUICOffset limit);

  // QUICFrameGenerator
  bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
  QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
                            size_t current_packet_size, uint32_t seq_num) override;

protected:
  QUICFlowController(uint64_t initial_limit) : _limit(initial_limit) {}
  virtual QUICFrame *_create_frame(uint8_t *buf) = 0;

  QUICOffset _offset        = 0; //< Largest sent/received offset
  QUICOffset _limit         = 0; //< Maximum amount of data to send/receive
  bool _should_create_frame = false;
};

class QUICRemoteFlowController : public QUICFlowController
{
public:
  QUICRemoteFlowController(uint64_t initial_limit) : QUICFlowController(initial_limit) {}
  int update(QUICOffset offset) override;
  void forward_limit(QUICOffset new_limit) override;

private:
  void _on_frame_lost(QUICFrameInformationUPtr &info) override;
  bool _blocked = false;
};

class QUICLocalFlowController : public QUICFlowController
{
public:
  QUICLocalFlowController(QUICRTTProvider *rtt_provider, uint64_t initial_limit)
    : QUICFlowController(initial_limit), _rtt_provider(rtt_provider)
  {
  }
  QUICOffset current_limit() const override;

  /**
   * Unlike QUICRemoteFlowController::forward_limit(), this function forwards limit if needed.
   */
  void forward_limit(QUICOffset new_limit) override;
  int update(QUICOffset offset) override;
  void set_limit(QUICOffset limit) override;

private:
  void _on_frame_lost(QUICFrameInformationUPtr &info) override;
  bool _need_to_forward_limit();

  QUICRateAnalyzer _analyzer;
  QUICRTTProvider *_rtt_provider = nullptr;
};

class QUICRemoteConnectionFlowController : public QUICRemoteFlowController
{
public:
  QUICRemoteConnectionFlowController(uint64_t initial_limit) : QUICRemoteFlowController(initial_limit) {}
  QUICFrame *_create_frame(uint8_t *buf) override;
};

class QUICLocalConnectionFlowController : public QUICLocalFlowController
{
public:
  QUICLocalConnectionFlowController(QUICRTTProvider *rtt_provider, uint64_t initial_limit)
    : QUICLocalFlowController(rtt_provider, initial_limit)
  {
  }
  QUICFrame *_create_frame(uint8_t *buf) override;
};

class QUICRemoteStreamFlowController : public QUICRemoteFlowController
{
public:
  QUICRemoteStreamFlowController(uint64_t initial_limit, QUICStreamId stream_id)
    : QUICRemoteFlowController(initial_limit), _stream_id(stream_id)
  {
  }
  QUICFrame *_create_frame(uint8_t *buf) override;

private:
  QUICStreamId _stream_id = 0;
};

class QUICLocalStreamFlowController : public QUICLocalFlowController
{
public:
  QUICLocalStreamFlowController(QUICRTTProvider *rtt_provider, uint64_t initial_limit, QUICStreamId stream_id)
    : QUICLocalFlowController(rtt_provider, initial_limit), _stream_id(stream_id)
  {
  }
  QUICFrame *_create_frame(uint8_t *buf) override;

private:
  QUICStreamId _stream_id = 0;
};
