/** @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 "I_EventSystem.h"
#include "QUICAckFrameCreator.h"
#include "QUICConfig.h"
#include <algorithm>

QUICAckFrameManager::QUICAckFrameManager()
{
  for (auto i = 0; i < QUIC_N_PACKET_SPACES; i++) {
    this->_ack_creator[i] = std::make_unique<QUICAckFrameCreator>(static_cast<QUICPacketNumberSpace>(i), this);
  }
}

QUICAckFrameManager::~QUICAckFrameManager() {}

void
QUICAckFrameManager::set_ack_delay_exponent(uint8_t ack_delay_exponent)
{
  // This function should be called only once
  ink_assert(this->_ack_delay_exponent == 0);
  this->_ack_delay_exponent = ack_delay_exponent;
}

int
QUICAckFrameManager::update(QUICEncryptionLevel level, QUICPacketNumber packet_number, size_t size, bool ack_only)
{
  if (!this->_is_level_matched(level)) {
    return 0;
  }

  auto index        = QUICTypeUtil::pn_space(level);
  auto &ack_creator = this->_ack_creator[static_cast<int>(index)];
  ack_creator->push_back(packet_number, size, ack_only);
  return 0;
}

/**
 * @param connection_credit This is not used. Because ACK frame is not flow-controlled
 */
QUICFrame *
QUICAckFrameManager::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)
{
  QUICAckFrame *ack_frame = nullptr;

  if (!this->_is_level_matched(level) || level == QUICEncryptionLevel::ZERO_RTT) {
    return ack_frame;
  }

  auto index        = QUICTypeUtil::pn_space(level);
  auto &ack_creator = this->_ack_creator[static_cast<int>(index)];
  ack_frame         = ack_creator->generate_ack_frame(buf, maximum_frame_size);

  if (ack_frame != nullptr) {
    QUICFrameInformationUPtr info  = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
    AckFrameInfo *ack_info         = reinterpret_cast<AckFrameInfo *>(info->data);
    ack_info->largest_acknowledged = ack_frame->largest_acknowledged();

    info->level = level;
    info->type  = ack_frame->type();
    this->_records_frame(ack_frame->id(), std::move(info));
  }

  return ack_frame;
}

bool
QUICAckFrameManager::will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting,
                                         uint32_t seq_num)
{
  // No ACK frame on ZERO_RTT level
  if (!this->_is_level_matched(level) || level == QUICEncryptionLevel::ZERO_RTT) {
    return false;
  }

  auto index = QUICTypeUtil::pn_space(level);
  return this->_ack_creator[static_cast<int>(index)]->is_ack_frame_ready();
}

void
QUICAckFrameManager::_on_frame_acked(QUICFrameInformationUPtr &info)
{
  ink_assert(info->type == QUICFrameType::ACK);
  AckFrameInfo *ack_info = reinterpret_cast<AckFrameInfo *>(info->data);
  auto index             = QUICTypeUtil::pn_space(info->level);
  this->_ack_creator[static_cast<int>(index)]->forget(ack_info->largest_acknowledged);
}

void
QUICAckFrameManager::_on_frame_lost(QUICFrameInformationUPtr &info)
{
  ink_assert(info->type == QUICFrameType::ACK);
  auto index = QUICTypeUtil::pn_space(info->level);
  // when ack frame lost. Force to refresh the frame.
  this->_ack_creator[static_cast<int>(index)]->refresh_state();
}

QUICFrameId
QUICAckFrameManager::issue_frame_id()
{
  return this->_issue_frame_id();
}

uint8_t
QUICAckFrameManager::ack_delay_exponent() const
{
  return this->_ack_delay_exponent;
}

void
QUICAckFrameManager::set_max_ack_delay(uint16_t delay)
{
  for (auto i = 0; i < QUIC_N_PACKET_SPACES; i++) {
    this->_ack_creator[i]->set_max_ack_delay(delay);
  }
}

//
// QUICAckFrameManager::QUICAckFrameCreator
//
void
QUICAckFrameManager::QUICAckFrameCreator::refresh_state()
{
  if (this->_packet_numbers.empty() || !this->_available) {
    return;
  }

  // we have something to send
  this->_should_send = true;
}

void
QUICAckFrameManager::QUICAckFrameCreator::forget(QUICPacketNumber largest_acknowledged)
{
  this->_available = false;
  this->sort();
  std::list<RecvdPacket> remove_list;
  for (auto it = this->_packet_numbers.begin(); it != this->_packet_numbers.end(); it++) {
    if ((*it).packet_number == largest_acknowledged) {
      remove_list.splice(remove_list.begin(), this->_packet_numbers, it, this->_packet_numbers.end());
      break;
    }
    this->_available |= !(*it).ack_only;
  }

  if (this->_packet_numbers.empty() || !this->_available) {
    this->_should_send = false;
  }
}

void
QUICAckFrameManager::QUICAckFrameCreator::push_back(QUICPacketNumber packet_number, size_t size, bool ack_only)
{
  if (packet_number == 0 || packet_number > this->_largest_ack_number) {
    this->_largest_ack_received_time = ink_get_hrtime();
    this->_largest_ack_number        = packet_number;
  }

  if (!this->_latest_packet_received_time) {
    this->_latest_packet_received_time = ink_get_hrtime();
  }

  // unorder packet should send ack immediately to accelerate the recovery
  if (this->_expect_next != packet_number) {
    this->_should_send = true;
  }

  // every 2 ack-eliciting packet should send a ack frame
  if (!ack_only && ++this->_ack_eliciting_count % 2 == 0) {
    this->_should_send = true;
  }

  // can not delay handshake packet
  if ((this->_pn_space == QUICPacketNumberSpace::INITIAL || this->_pn_space == QUICPacketNumberSpace::HANDSHAKE) && !ack_only) {
    this->_should_send = true;
  }

  if (!ack_only) {
    this->_available    = true;
    this->_has_new_data = true;
  } else {
    this->_should_send = this->_available ? this->_should_send : false;
  }

  this->_expect_next = packet_number + 1;
  this->_packet_numbers.push_back({ack_only, packet_number});
}

size_t
QUICAckFrameManager::QUICAckFrameCreator::size() const
{
  return this->_packet_numbers.size();
}

void
QUICAckFrameManager::QUICAckFrameCreator::clear()
{
  this->_packet_numbers.clear();
  this->_largest_ack_number          = 0;
  this->_largest_ack_received_time   = 0;
  this->_latest_packet_received_time = 0;
  this->_ack_eliciting_count         = 0;
  this->_should_send                 = false;
  this->_available                   = false;
}

QUICPacketNumber
QUICAckFrameManager::QUICAckFrameCreator::largest_ack_number() const
{
  return this->_largest_ack_number;
}

ink_hrtime
QUICAckFrameManager::QUICAckFrameCreator::largest_ack_received_time() const
{
  return this->_largest_ack_received_time;
}

void
QUICAckFrameManager::QUICAckFrameCreator::sort()
{
  //  TODO Find more smart way
  this->_packet_numbers.sort([](const RecvdPacket &a, const RecvdPacket &b) -> bool { return a.packet_number > b.packet_number; });
}

QUICAckFrame *
QUICAckFrameManager::QUICAckFrameCreator::generate_ack_frame(uint8_t *buf, uint16_t maximum_frame_size)
{
  QUICAckFrame *ack_frame = nullptr;
  if (!this->_available) {
    this->_should_send = false;
    return ack_frame;
  }

  ack_frame = this->_create_ack_frame(buf);
  if (ack_frame == nullptr || ack_frame->size() < maximum_frame_size) {
    this->_should_send                 = false;
    this->_latest_packet_received_time = 0;
  } else {
    return nullptr;
  }

  return ack_frame;
}

QUICAckFrame *
QUICAckFrameManager::QUICAckFrameCreator::_create_ack_frame(uint8_t *buf)
{
  ink_assert(!this->_packet_numbers.empty());
  QUICAckFrame *ack_frame = nullptr;
  this->sort();
  std::list<RecvdPacket> &list = this->_packet_numbers;

  this->_has_new_data = false;

  uint8_t gap     = 0;
  uint64_t length = 0;
  auto it         = list.begin();

  // skip ack_only packets
  for (; it != list.end(); it++) {
    if (!(*it).ack_only) {
      break;
    }
  }

  if (it == list.end()) {
    return ack_frame;
  }

  QUICPacketNumber largest_ack_number = (*it).packet_number;
  QUICPacketNumber last_ack_number    = largest_ack_number;

  while (it != list.end()) {
    QUICPacketNumber pn = (*it).packet_number;
    if (pn == last_ack_number) {
      last_ack_number--;
      length++;
      it++;
      continue;
    }

    ink_assert(length > 0);

    if (ack_frame) {
      ack_frame->ack_block_section()->add_ack_block({static_cast<uint8_t>(gap - 1), length - 1});
    } else {
      uint64_t delay = this->_calculate_delay();
      ack_frame      = QUICFrameFactory::create_ack_frame(buf, largest_ack_number, delay, length - 1,
                                                     this->_ack_manager->issue_frame_id(), this->_ack_manager);
    }

    gap             = last_ack_number - pn;
    last_ack_number = pn;
    length          = 0;
  }

  if (ack_frame) {
    ack_frame->ack_block_section()->add_ack_block({static_cast<uint8_t>(gap - 1), length - 1});
  } else {
    uint64_t delay = this->_calculate_delay();
    ack_frame = QUICFrameFactory::create_ack_frame(buf, largest_ack_number, delay, length - 1, this->_ack_manager->issue_frame_id(),
                                                   this->_ack_manager);
  }

  return ack_frame;
}

uint64_t
QUICAckFrameManager::QUICAckFrameCreator::_calculate_delay()
{
  // Ack delay is in microseconds and scaled
  ink_hrtime now             = ink_get_hrtime();
  uint64_t delay             = (now - this->_largest_ack_received_time) / 1000;
  uint8_t ack_delay_exponent = 3;
  if (this->_pn_space != QUICPacketNumberSpace::INITIAL && this->_pn_space != QUICPacketNumberSpace::HANDSHAKE) {
    ack_delay_exponent = this->_ack_manager->ack_delay_exponent();
  }
  return delay >> ack_delay_exponent;
}

bool
QUICAckFrameManager::QUICAckFrameCreator::available() const
{
  return this->_available;
}

bool
QUICAckFrameManager::QUICAckFrameCreator::is_ack_frame_ready()
{
  if (this->_available && this->_has_new_data && !this->_packet_numbers.empty() &&
      this->_latest_packet_received_time + this->_max_ack_delay * HRTIME_MSECOND <= ink_get_hrtime()) {
    // when we has new data and the data is available to send (not ack only). and we delay for too much time. Send it out
    this->_should_send = true;
  }

  return this->_should_send && this->_available && !this->_packet_numbers.empty();
}

void
QUICAckFrameManager::QUICAckFrameCreator::set_max_ack_delay(uint16_t delay)
{
  this->_max_ack_delay = delay;
}

QUICAckFrameManager::QUICAckFrameCreator::QUICAckFrameCreator(QUICPacketNumberSpace pn_space, QUICAckFrameManager *ack_manager)
  : _ack_manager(ack_manager), _pn_space(pn_space)
{
}

QUICAckFrameManager::QUICAckFrameCreator::~QUICAckFrameCreator() {}

/*
   No limit of encryption level.
   ```
   std::array<QUICEncryptionLevel, 4> _encryption_level_filter = {
     QUICEncryptionLevel::INITIAL,
     QUICEncryptionLevel::ZERO_RTT,
     QUICEncryptionLevel::HANDSHAKE,
     QUICEncryptionLevel::ONE_RTT,
   };
   ```
*/
bool
QUICAckFrameManager::_is_level_matched(QUICEncryptionLevel level)
{
  return true;
}
