#pragma once

#include "../../core/net/Context.hpp"

#include <functional>

namespace net::server {

enum queue: uint8_t {
    GLOBAL = 0,
    ENTITY,
    CHUNK,
    EDIT,
    count
};

class Peer final: public Connection {
public:
    Peer(picoquic_cnx_t *cnx): Connection(cnx, false, queue::count) { }
    ~Peer() { assert(ctx == nullptr); }

    template <typename C>
    constexpr C *getCtx() {
        assert(ctx != nullptr);
        return (C *)ctx;
    }
    void *ctx = nullptr;
};
class Server final: Context {
public:
    Server(const exposure& ct,
        std::function<std::optional<uint16_t>(Peer*)> onConnect,
        std::function<bool(Peer*, bool, uint16_t)> onDisconnect,
        std::function<bool(Peer *, const data::out_view&, PacketFlags)> onPacket);
    ~Server();

    /// Send unreliable data to all peers
    void emitBroadcast(const uint8_t *ptr, size_t size);
    /// Send reliable data to all peers
    void sendBroadcast(const data::out_buffer& buffer, uint8_t queue = 0, size_t queue_size = 0) {
        iterPeers([&](Peer* peer){ peer->send(buffer, queue, queue_size); });
    }

    /// Register peer or return nullptr
    Peer *connect(picoquic_cnx_t *);

    /// Read-write on sockets and notify callbacks
    void pull() { Context::pull(1000, 50, 20); }

    int connectionCallback(Peer* client, uint64_t stream_id, uint8_t *bytes, size_t length,
        picoquic_call_back_event_t fin_or_event, void *v_stream_ctx);

    bool isRunning() const { return true; }

    template<typename C>
    void iterPeers(C call) {
        for(auto& peer: peers) {
            call(&peer);
        }
    }
    template<typename P>
    bool anyPeer(P predicate) {
        for(auto& peer: peers) {
            if(predicate(&peer))
                return true;
        }
        return false;
    }

private:
    std::forward_list<Peer> peers;

    std::function<std::optional<uint16_t>(Peer *)> onConnect;
    std::function<bool(Peer *, bool, uint16_t)> onDisconnect;
    std::function<bool(Peer *, const data::out_view &, PacketFlags)> onPacket;

    uint32_t max_connections;
};

}