#include "./Serializer.hpp"
#include "./Universe.hpp"
#include "./Chunk.hpp"

using namespace world::server;

constexpr auto PREDICTABLE = true;
constexpr auto COMPRESSION_PREFIX = net::server_packet_type::COMPRESSION;
constexpr auto CHUNK_PREFIX = net::server_packet_type::CHUNK;

Serializer::Serializer(const std::string& folderPath): dict_content({folderPath + "/zstd.dict", "content/zstd.dict"}, memory::read_view::Of(COMPRESSION_PREFIX)),
    dicts(dict_content.content()), dict_write_ctx(dicts.make_writer()) { }

Serializer::out Serializer::writeRegion(const area_region_ref &id, const std::shared_ptr<Region> &r) const {
    auto packet = memory::write_buffer(net::server_packet_type::REGION, sizeof(id));
    packet.write(id);
    packet.pushPart([&](memory::write_buffer::part part) {
        r->averages(part); });
    return packet.finish();
}
Serializer::out Serializer::writeChunk(const node_chunk_ref& id, const std::shared_ptr<Chunk>& ck) const {
    std::ostringstream out;
    ck->write(out);
    auto packet = memory::write_buffer(net::server_packet_type::CHUNK, sizeof(id));
    packet.write(id);
    packet.pushPart([&](memory::write_buffer::part part){
        dict_write_ctx.compress(out.str(), part); });
    return packet.finish();
}
Serializer::out Serializer::writeEdits(node_ref id, const fill_edits& edits) const {
    size_t size = sizeof(area_id);
    for(const auto& part: edits) {
        size += sizeof(chunk_pos);
        size += sizeof(chunk_voxel_idx);
        size += sizeof(ChunkEdits::Edit) * part.second.size();
    }
    auto packet = memory::write_buffer(net::server_packet_type::RAW_EDITS, size);
    packet.write(id);
    for(const auto& part: edits) {
        packet.write(part.first);
        packet.write<chunk_voxel_idx>(part.second.size());
        packet.write(part.second.data(), part.second.size() * sizeof(ChunkEdits::Edit));
    }
    return packet.finish();
}
Serializer::out Serializer::writeChunkReset() const {
    return out(memory::read_view::Of(CHUNK_PREFIX), nullptr);
}

Serializer::out Serializer::writeCapabilities(uint16_t loadDist, size_t flood) const {
    auto packet = memory::write_buffer(net::server_packet_type::CAPABILITIES, sizeof(loadDist) + sizeof(bool) + sizeof(flood) * PREDICTABLE);
    packet.write(loadDist);
    packet.write(PREDICTABLE);
    if constexpr (PREDICTABLE) {
        packet.write(flood);
    }
    return packet.finish();
}

Serializer::out Serializer::writeDict() const {
    return out(memory::read_view((uint8_t*)dict_content.data(), dict_content.size()), nullptr);
}

Serializer::out Serializer::writeTeleport(node_id id, const relative_transform& tf) const {
    auto packet = memory::write_buffer(net::server_packet_type::TELEPORT, sizeof(node_id) + sizeof(tf));
    packet.write(id);
    packet.write(tf);
    return packet.finish();
}

Serializer::out Serializer::writerMessage(const std::string& msg) const {
    return out::Of(net::server_packet_type::MESSAGE, msg.data(), msg.size());
}