/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "Bbr.h"
#include "InboundTransfer.h"
#include "LossStats.h"
#include "OutboundTransfer.h"
#include "Pacer.h"
#include "RttStats.h"
#include "common/bitstring.h"
#include "td/utils/buffer.h"
#include "td/utils/Heap.h"
#include "td/utils/VectorQueue.h"
#include 
namespace ton {
namespace rldp2 {
using TransferId = td::Bits256;
class ConnectionCallback {
 public:
  virtual ~ConnectionCallback() {
  }
  virtual void send_raw(td::BufferSlice small_datagram) = 0;
  virtual void receive(TransferId transfer_id, td::Result r_data) = 0;
  virtual void on_sent(TransferId transfer_id, td::Result state) = 0;
};
class RldpConnection {
 public:
  RldpConnection();
  RldpConnection(RldpConnection &&other) = delete;
  RldpConnection &operator=(RldpConnection &&other) = delete;
  void send(TransferId tranfer_id, td::BufferSlice data, td::Timestamp timeout = td::Timestamp::never());
  void set_receive_limits(TransferId transfer_id, td::Timestamp timeout, td::uint64 max_size);
  void receive_raw(td::BufferSlice packet);
  td::Timestamp run(ConnectionCallback &callback);
  void set_default_mtu(td::uint64 mtu) {
    default_mtu_ = mtu;
  }
  td::uint64 default_mtu() const {
    return default_mtu_;
  }
 private:
  td::uint64 default_mtu_ = 7680;
  std::map outbound_transfers_;
  td::uint32 in_flight_count_{0};
  std::map inbound_transfers_;
  struct Limit : public td::HeapNode {
    TransferId transfer_id;
    td::uint64 max_size;
    bool is_inbound;
    bool operator<(const Limit &other) const {
      return transfer_id < other.transfer_id;
    }
  };
  td::KHeap limits_heap_;
  std::set limits_set_;
  struct CompletedId {
    TransferId transfer_id;
    td::Timestamp timeout;
  };
  td::VectorQueue completed_queue_;
  std::set completed_set_;
  void add_limit(td::Timestamp timeout, Limit limit);
  td::Timestamp next_limit_expires_at();
  void drop_limits(TransferId id);
  void on_inbound_completed(TransferId transfer_id, td::Timestamp now);
  td::Timestamp loop_limits(td::Timestamp now);
  void loop_bbr(td::Timestamp now);
  RttStats rtt_stats_;
  BdwStats bdw_stats_;
  LossStats loss_stats_;
  Bbr bbr_;
  Pacer pacer_;
  td::uint32 congestion_window_{0};
  std::vector to_send_raw_;
  std::vector>> to_receive_;
  std::vector>> to_on_sent_;
  void send_packet(td::BufferSlice packet) {
    to_send_raw_.push_back(std::move(packet));
  };
  td::Timestamp run(const TransferId &transfer_id, InboundTransfer &inbound);
  struct Guard {
    td::uint32 &in_flight_count;
    const RldpSender &sender;
    td::uint32 before_in_flight{sender.get_inflight_symbols_count()};
    Guard(td::uint32 &in_flight_count, const RldpSender &sender) : in_flight_count(in_flight_count), sender(sender){};
    ~Guard() {
      in_flight_count -= before_in_flight;
      in_flight_count += sender.get_inflight_symbols_count();
    }
  };
  td::optional step(const TransferId &transfer_id, OutboundTransfer &outbound, td::Timestamp now);
  void receive_raw_obj(ton::ton_api::rldp2_messagePart &part);
  void receive_raw_obj(ton::ton_api::rldp2_complete &part);
  void receive_raw_obj(ton::ton_api::rldp2_confirm &part);
};
}  // namespace rldp2
}  // namespace ton