#pragma once

#include "core/world/EdittableChunk.hpp"

namespace world::client {

/// ChunkEdits with passive rollback
class ChunkFutureEdits: public ChunkEdits {
public:
    ChunkFutureEdits(world::EdittableChunk* ptr): ChunkEdits(ptr) { }
    ~ChunkFutureEdits() { }

    /// Update voxels
    /// @return if modified neighbors to update
    std::optional<Faces> update(float deltaTime, bool animate) override {
        for (auto it = futureEdits.begin(); it != futureEdits.end();) {
            it->second.second -= deltaTime;
            if (it->second.second <= 0 && animate) {
                invalidate(it->first);
                edits.emplace(it->first, it->second.first);
                it = futureEdits.erase(it);
            } else {
                ++it;
            }
        }
        return ChunkEdits::update(deltaTime, animate);
    }

    /// Add future edit
    void addFutureEdit(const ChunkEdits::Edit& edit, float delay) { futureEdits.insert_or_assign(edit.idx, std::make_pair(EditBody(edit), delay)); }

private:
    /// Temporary animated changes
    robin_hood::unordered_map<chunk_voxel_idx, std::pair<EditBody, float>> futureEdits;
};

/// Special chunk with additional flags for client only
class Chunk final: public world::EdittableChunk {
public:
    Chunk(std::istream &is): EdittableChunk(new ChunkFutureEdits(this), is) { }
    /// Create from average
    Chunk(Voxel val): EdittableChunk(new ChunkFutureEdits(this)), isAverage(true), isMajorant(val.swap()) { voxels.fill(Voxel(val.material(), val.density())); }
    Chunk(): EdittableChunk(new ChunkFutureEdits(this)) { }

    constexpr bool isTrusted(bool allowMajorant) const { return !isAverage || (allowMajorant && isMajorant); }
    void unsetMajorant() {
        assert(isMajorant);
        isMajorant = false;
    }

    ChunkFutureEdits &setEdits() { assert(edits); return *(ChunkFutureEdits*)edits.get(); }

private:
    /// Is temporary average
    const bool isAverage = false;
    /// Is temporary full valued
    bool isMajorant = false;
};

/// Chunk full of air
static const std::shared_ptr<const Chunk> EMPTY_CHUNK = std::make_shared<Chunk>();

}