#pragma once

#include "core/world/EdittableChunk.hpp"
#include <memory>
#include "core/world/generator/Abstract.hpp"

namespace world::server {

    using fill_edits = robin_hood::unordered_map<chunk_pos, std::vector<world::ChunkEdits::Edit>>;

    /// Server side chunk
    class Chunk: public world::EdittableChunk {
    public:
        Chunk(owner<world::ChunkEdits*>);
        Chunk(owner<world::ChunkEdits*>, const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd);
        Chunk(owner<world::ChunkEdits*>, std::istream& str, bool rle = RLE);
        virtual ~Chunk();

        /// Set voxel from index
        void set(chunk_voxel_idx idx, const Voxel& val);
        /// Set voxel from position
        void setAt(const chunk_voxel_pos& pos, const Voxel& val);

        /// Break voxel
        virtual std::optional<Item> replace(chunk_voxel_idx idx, const Voxel &val, float delay = 0);

        /// Is player modified
        inline bool isModified() const { return modified; }
        /// Write to file and return average (majorant material)
        /// Voxel swap bit indicate perfect majority
        Voxel write(std::ostream& str, bool rle = RLE) const;

    private:
        /// Modified by player
        bool modified = false;
    };

    struct ChunkFactory {
        virtual std::shared_ptr<Chunk> create(const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd) const = 0;
        virtual std::shared_ptr<Chunk> create(std::istream &str) const = 0;
        virtual std::shared_ptr<Chunk> create() const = 0;
    };

    /// Build for server only chunk
    /// Not really edittable (get/set)Edits will fail
    struct StandaloneChunkFactory final: public ChunkFactory {
        std::shared_ptr<Chunk> create(const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd) const override {
            return std::make_shared<Chunk>(nullptr, pos, rnd);
        }
        std::shared_ptr<Chunk> create(std::istream &str) const override { return std::make_shared<Chunk>(nullptr, str); }
        std::shared_ptr<Chunk> create() const override { return std::make_shared<Chunk>(nullptr); }
    };
}
