#pragma once
#include <string>
#include <optional>
#include "../utils/logger.hpp"
#include <picoquic/picosocks.h>

/// Network protocol
namespace net {

/// Application Protocol Negotiation
constexpr auto ALPN = "univerxel_quic";

/// Reasons for client server connection to break
enum class disconnect_reason: uint16_t {
    UNEXPECTED = 0,
    // Client quit
    QUIT = 1,
    // Server close
    CLOSE = 2,
    // Server full
    FULL = 3,
    PROTOCOL_VIOLATION = 15,
};

/// Packets from server to client
enum class server_packet_type: uint8_t {
    /// MAYBE: HELLO = 0,

    /// Must close connection
    /// bool(is_application) reason(uint16_t)
    QUIT = UINT8_MAX,

    /// Set client position and instance id
    /// size_t(playerId) voxel_pos
    TELEPORT = 1,

    /// List all areas
    /// {area_id, world::Area::params}[]
    AREAS = 16,
    /// Average of preloaded chunks in region
    /// {area_<region_pos>, {region_chunk_pos, Voxel}[]}
    /// Voxel swap bit indicate a chunk full of Voxel without flag bit
    REGION = 17,
    /// Full chunk update
    /// {area_<chunk_pos>, zstd<chunk rle>}
    /// empty: all sent
    CHUNK = 18,
    /// Uncompressed chunk changes
    /// {area_id, {chunk_pos, uint16_t(count), Chunk::Edit[]}[]}
    RAW_EDITS = 19,
    /// Chunk changes instruction
    /// action::FillShape
    EDITS = 20,

    /// Declare entities types
    /// {size_t(index), usvec3(size), vec3(scale), flags(000000, area, permanant)}[]
    ENTITY_TYPES = 32,

    /// Update entities instances position and velocity
    /// {size_t(entity), size_t(count), {size_t(index), ifvec3(pos), vec3(velocity)}[]}[]
    ENTITIES = 33,

    /// Entity type model data
    /// {size_t(entity), bool(area), render::Data::Model || {size_t(size), zstd<chunk rle>}[]}
    ENTITY_SHAPE = 34,

    /// World compression dictionary
    /// zstd dict
    COMPRESSION = 64,
    /// Server capabilities
    /// uint16_t(loadDistance), bool(predictable), (if predictable) size_t(floodfillLimit)
    //MAYBE: use uint8_t flags
    CAPABILITIES = 65,

    /// Public chat message
    /// char[] (not null terminated)
    MESSAGE = 129,
};
/// Packets from client to server
enum class client_packet_type: uint8_t {
    /// Interact with voxels
    /// actions::FillShape
    FILL_SHAPE = 0,

    /// Request entity chunks
    /// size_t(id)
    MISSING_ENTITY = 6,

    /// Request missing regions
    /// area_id, region_pos[]
    MISSING_REGIONS = 7,

    /// Request missing chunks
    /// area_id, chunk_pos[max: MAX_PENDING_CHUNK_COUNT]
    MISSING_CHUNKS = 8,

    /// Send public text message
    /// char[] (not null terminated)
    MESSAGE = 9,

    /// Position update (unreliable)
    /// (sequence)uint64_t voxel_pos
    MOVE = 16,

    /// Client capabilities
    /// bool(edit_handling)
    //MAYBE: use uint8_t flags
    CAPABILITIES = 65,
};

constexpr auto MAX_PENDING_CHUNK_COUNT = 256;

/// Server address as name(ip or dns name) and port
struct address {
    std::string host;
    int port;

    /// Return address_storage(ip+port) and isName
    std::optional<std::pair<sockaddr_storage, bool>> toAddress() const {
        std::pair<sockaddr_storage, bool> res;
        int isName = 0;
        if (picoquic_get_server_address(host.c_str(), port, &res.first, &isName) != 0)
            return {};

        res.second = isName;
        return res;
    }

    friend inline std::ostream& operator<<(std::ostream& os, const address& ct) {
        return os << ct.host << ':' << ct.port;
    }
};
/// Server properties
struct exposure: address {
    uint32_t max_connections;
    /// TLS private key
    std::string key;
    /// TLS public key
    std::string cert;
};

}