#pragma once

#include "PeerLink.hpp"
#include "TcpListener.hpp"
#include "TcpStream.hpp"

#include <list>
#include <optional>
#include <unordered_map>
#include <vector>

namespace tw::p2p {

// TCP implementation of PeerLink.
//
// Wire format (stream transport requires length-prefix framing):
//   [uint32_t total_len][uint32_t type][protobuf payload]
//   total_len = sizeof(type) + sizeof(payload)
class TcpPeerLink : public PeerLink {
public:
    TcpPeerLink(uint32_t self_id, const tw::net::Address& listen_addr);

    void connect_to(uint32_t peer_id, const tw::net::Address& addr) override;
    void send_batch(uint32_t peer_id, const mmo::peer::PeerActionBatch& batch) override;
    void poll      () override;

private:
    enum class MsgType : uint32_t { Hello = 1, ActionBatch = 2, Bye = 3 };

    struct Conn {
        tw::net::TcpStream      stream;
        std::optional<uint32_t> peer_id;
        std::vector<std::byte>  recv_buf{64 * 1024};
        size_t                  recv_filled{0};
    };

    uint32_t             m_self_id;
    tw::net::TcpListener m_listener;

    // std::list: nodes are pointer-stable so m_by_id can safely point into it.
    std::list<Conn> m_conns;
    std::unordered_map<uint32_t, tw::net::TcpStream*> m_by_id;

    void send_raw    (tw::net::TcpStream& stream, MsgType type,
                      const google::protobuf::MessageLite& msg);
    void dispatch    (Conn& c);
    void drain_listener();
    void poll_conns  ();
};

} // namespace tw::p2p
