#pragma once

#include <string>
#include <shared_mutex>
#include "core/world/Area.hpp"
#include "core/world/Voxel.hpp"
#include "core/memory.hpp"
#include "core/utils/zctx.hpp"

namespace world::server {
    ///Group of chunks saved as a single file in memory
    class Region {
    public:
        Region(const std::string& folderPath, const area_region_ref &id);
        ~Region();

        template<typename D>
        void averages(D &out) {
            std::shared_lock lock(mutex);
            out.resize(content.size() * (sizeof(region_chunk_pos) + sizeof(Voxel)));
            memory::write_view in((uint8_t*)out.data(), out.size() * sizeof(D));
            for(const auto& r: content) {
                if (r.second.average.has_value()) {
                    in.write(r.first);
                    in.write(r.second.average.value());
                }
            }
            out.resize(in.current());
        }
        bool read(const region_chunk_pos &pos, const zstd::read_ctx &ctx, std::vector<char> &out);
        void write(const region_chunk_pos &pos, const zstd::write_ctx& ctx, const std::string_view &in, const std::optional<Voxel>& average);

        static void Read(const std::string& folder, const part_ref& id, const zstd::read_ctx &ctx, const std::function<void(const region_chunk_pos&, const std::vector<char>&)>& out);
        static void Write(const std::string& folder, const part_ref& id, const zstd::write_ctx &ctx, size_t size, const std::function<std::pair<region_chunk_pos, std::string>()>& next);

    private:
        static size_t Read(const std::string &path, const std::function<void(const region_chunk_pos &, const std::optional<Voxel> &, std::vector<char> &&)> &out);
        void save(bool force = true);

        std::string path;
        //TODO: use tickets to remove unused regions

        enum Flags: unsigned char {
            ZERO = 0,
            HAS_AVERAGE = 1 << 0,
            EMPTY = 1 << 1
        };

        std::shared_mutex mutex;
        struct node {
            node(const std::optional<Voxel>& a, std::vector<char>&& d):
                average(a), data(std::move(d)) { }
            std::optional<Voxel> average;
            std::vector<char> data;
        };
        robin_hood::unordered_map<region_chunk_pos, node> content;
        bool changed = false;

        void load();
    };
}