/** @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.
 */

#include "catch.hpp"

#include "quic/QUICFlowController.h"
#include "quic/Mock.h"
#include <memory>

static constexpr int DEFAULT_RTT = 1 * HRTIME_SECOND;

class MockRTTProvider : public QUICRTTProvider
{
public:
  ink_hrtime
  smoothed_rtt() const override
  {
    return this->_smoothed_rtt;
  }

  MockRTTProvider(ink_hrtime rtt) : _smoothed_rtt(rtt) {}
  void
  set_smoothed_rtt(ink_hrtime rtt)
  {
    this->_smoothed_rtt = rtt;
  }

  ink_hrtime
  latest_rtt() const override
  {
    return HRTIME_MSECONDS(1);
  }

  ink_hrtime
  rttvar() const override
  {
    return HRTIME_MSECONDS(1);
  }

  ink_hrtime
  congestion_period(uint32_t period) const override
  {
    return HRTIME_MSECONDS(1);
  }

private:
  ink_hrtime _smoothed_rtt = 0;
};

TEST_CASE("QUICFlowController_Local_Connection", "[quic]")
{
  int ret = 0;
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  MockRTTProvider rp(DEFAULT_RTT);
  QUICLocalConnectionFlowController fc(&rp, 1024);

  // Check initial state
  CHECK(fc.current_offset() == 0);
  CHECK(fc.current_limit() == 1024);

  ret = fc.update(256);
  CHECK(fc.current_offset() == 256);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Retransmit
  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(1024);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Delay
  ret = fc.update(512);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Exceed limit
  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret != 0);

  // MAX_STREAM_DATA
  fc.forward_limit(2048);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 2048);
  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
  CHECK(frame);
  CHECK(frame->type() == QUICFrameType::MAX_DATA);

  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1280);
  CHECK(fc.current_limit() == 2048);
  CHECK(ret == 0);
}

TEST_CASE("QUICFlowController_Remote_Connection", "[quic]")
{
  int ret = 0;
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  QUICRemoteConnectionFlowController fc(1024);

  // Check initial state
  CHECK(fc.current_offset() == 0);
  CHECK(fc.current_limit() == 1024);

  ret = fc.update(256);
  CHECK(fc.current_offset() == 256);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Retransmit
  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(1000);
  CHECK(fc.current_offset() == 1000);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Delay
  ret = fc.update(512);
  CHECK(fc.current_offset() == 1000);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Exceed limit
  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1000);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret != 0);
  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
  CHECK(frame);
  CHECK(frame->type() == QUICFrameType::DATA_BLOCKED);

  // MAX_STREAM_DATA
  fc.forward_limit(2048);
  CHECK(fc.current_offset() == 1000);
  CHECK(fc.current_limit() == 2048);

  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1280);
  CHECK(fc.current_limit() == 2048);
  CHECK(ret == 0);
}

TEST_CASE("QUICFlowController_Remote_Connection_ZERO_Credit", "[quic]")
{
  int ret = 0;
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  QUICRemoteConnectionFlowController fc(1024);

  // Check initial state
  CHECK(fc.current_offset() == 0);
  CHECK(fc.current_limit() == 1024);

  // Zero credit
  ret = fc.update(1024);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  CHECK(fc.will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, true, 0));
  // if there're anything to send
  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
  CHECK(frame);
  CHECK(frame->type() == QUICFrameType::DATA_BLOCKED);

  // MAX_STREAM_DATA
  fc.forward_limit(2048);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 2048);

  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1280);
  CHECK(fc.current_limit() == 2048);
  CHECK(ret == 0);
}

TEST_CASE("QUICFlowController_Remote_Connection_Insufficient_maximum_frame_size", "[quic]")
{
  int ret = 0;
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  QUICRemoteConnectionFlowController fc(1024);

  // Check initial state
  CHECK(fc.current_offset() == 0);
  CHECK(fc.current_limit() == 1024);

  // Zero credit
  ret = fc.update(1024);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  CHECK(fc.will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, true, 0));
  // if there're anything to send
  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 0, 0, 0);
  CHECK(frame == nullptr);
  frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
  CHECK(frame);
  CHECK(frame->type() == QUICFrameType::DATA_BLOCKED);

  // MAX_STREAM_DATA
  fc.forward_limit(2048);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 2048);

  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1280);
  CHECK(fc.current_limit() == 2048);
  CHECK(ret == 0);
}

TEST_CASE("QUICFlowController_Local_Stream", "[quic]")
{
  int ret = 0;
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  MockRTTProvider rp(DEFAULT_RTT);
  QUICLocalStreamFlowController fc(&rp, 1024, 0);

  // Check initial state
  CHECK(fc.current_offset() == 0);
  CHECK(fc.current_limit() == 1024);

  ret = fc.update(256);
  CHECK(fc.current_offset() == 256);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Retransmit
  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(1024);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Delay
  ret = fc.update(512);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Exceed limit
  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret != 0);

  // MAX_STREAM_DATA
  fc.forward_limit(2048);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 2048);
  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
  CHECK(frame);
  CHECK(frame->type() == QUICFrameType::MAX_STREAM_DATA);

  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1280);
  CHECK(fc.current_limit() == 2048);
  CHECK(ret == 0);
}

TEST_CASE("QUICFlowController_Remote_Stream", "[quic]")
{
  int ret = 0;
  QUICRemoteStreamFlowController fc(1024, 0);

  // Check initial state
  CHECK(fc.current_offset() == 0);
  CHECK(fc.current_limit() == 1024);

  ret = fc.update(256);
  CHECK(fc.current_offset() == 256);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Retransmit
  ret = fc.update(512);
  CHECK(fc.current_offset() == 512);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  ret = fc.update(1024);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  CHECK(fc.credit() == 0);
  CHECK(fc.will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, true, 0));

  // Delay
  ret = fc.update(512);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret == 0);

  // Exceed limit
  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 1024);
  CHECK(ret != 0);

  // MAX_STREAM_DATA
  fc.forward_limit(2048);
  CHECK(fc.current_offset() == 1024);
  CHECK(fc.current_limit() == 2048);

  ret = fc.update(1280);
  CHECK(fc.current_offset() == 1280);
  CHECK(fc.current_limit() == 2048);
  CHECK(ret == 0);
}

TEST_CASE("Frame retransmission", "[quic]")
{
  QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT;

  SECTION("BLOCKED frame")
  {
    int ret = 0;
    uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
    QUICRemoteConnectionFlowController fc(1024);

    // Check initial state
    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    CHECK(!frame);

    ret = fc.update(1024);
    CHECK(ret == 0);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICDataBlockedFrame *>(frame)->offset() == 1024);
    QUICFrameId id = frame->id();

    // Don't retransmit unless the frame is lost
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(!frame);

    // Retransmit
    fc.on_frame_lost(id);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICDataBlockedFrame *>(frame)->offset() == 1024);

    // Don't send if it was not blocked
    fc.on_frame_lost(frame->id());
    fc.forward_limit(2048);
    ret   = fc.update(1536);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    CHECK(!frame);

    // This should not be retransmission
    ret   = fc.update(2048);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICDataBlockedFrame *>(frame)->offset() == 2048);
  }

  SECTION("STREAM_DATA_BLOCKED frame")
  {
    int ret = 0;
    uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
    QUICRemoteStreamFlowController fc(1024, 0);

    // Check initial state
    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    CHECK(!frame);

    ret = fc.update(1024);
    CHECK(ret == 0);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICStreamDataBlockedFrame *>(frame)->offset() == 1024);
    QUICFrameId id = frame->id();

    // Don't retransmit unless the frame is lost
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(!frame);

    // Retransmit
    fc.on_frame_lost(id);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICStreamDataBlockedFrame *>(frame)->offset() == 1024);

    // Don't send if it was not blocked
    fc.on_frame_lost(frame->id());
    fc.forward_limit(2048);
    ret   = fc.update(1536);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    CHECK(!frame);

    // This should not be retransmission
    ret   = fc.update(2048);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICStreamDataBlockedFrame *>(frame)->offset() == 2048);
  }

  SECTION("MAX_DATA frame")
  {
    uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
    MockRTTProvider rp(DEFAULT_RTT);
    QUICLocalConnectionFlowController fc(&rp, 1024);

    // Check initial state
    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    CHECK(!frame);

    fc.update(1024);
    fc.forward_limit(1024);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICMaxDataFrame *>(frame)->maximum_data() == 1024);
    QUICFrameId id = frame->id();

    // Don't retransmit unless the frame is lost
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(!frame);

    // Retransmit
    fc.on_frame_lost(id);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICMaxDataFrame *>(frame)->maximum_data() == 1024);

    // Send new one if it was updated
    fc.on_frame_lost(id);
    fc.forward_limit(2048);
    fc.update(2048);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICMaxDataFrame *>(frame)->maximum_data() == 2048);
  }

  SECTION("MAX_STREAM_DATA frame")
  {
    uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
    MockRTTProvider rp(DEFAULT_RTT);
    QUICLocalStreamFlowController fc(&rp, 1024, 0);

    // Check initial state
    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    CHECK(!frame);

    fc.update(1024);
    fc.forward_limit(1024);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICMaxStreamDataFrame *>(frame)->maximum_stream_data() == 1024);
    QUICFrameId id = frame->id();

    // Don't retransmit unless the frame is lost
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(!frame);

    // Retransmit
    fc.on_frame_lost(id);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICMaxStreamDataFrame *>(frame)->maximum_stream_data() == 1024);

    // Send new one if it was updated
    fc.on_frame_lost(id);
    fc.forward_limit(2048);
    fc.update(2048);
    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
    REQUIRE(frame);
    CHECK(static_cast<QUICMaxStreamDataFrame *>(frame)->maximum_stream_data() == 2048);
  }
}
