#include "SharedUniverse.hpp"
#include "SharedParts.hpp"

using namespace world::server;

SharedUniverse::SharedUniverse(const options &o, server_handle *const localHandle): Universe(o), localHandle(localHandle) {
    // Local player
    [[maybe_unused]]
    const auto id = entities.at(PLAYER_ENTITY_ID).instances.emplace(Entity::Instance{spawnPoint, glm::vec3(0)});
    assert(id == PLAYER_ENTITY_ID);
    localHandle->teleport = spawnPoint;
    movedPlayers.insert(id);

    localHandle->areas = (world::client::area_map*)(&areas); //WONT FIX: templated area
    localHandle->entities = &entities;
    localHandle->emit = std::function([&](const world::action::packet &packet) {
        if(const auto fill = std::get_if<world::action::FillShape>(&packet)) {
            //NOTE: LocalUniverse had already check for entities
            this->set(fill->pos, fill->radius, fill->shape, fill->val);
        } else if(const auto message = std::get_if<world::action::Message>(&packet)) {
            this->broadcastMessage("Player" + std::to_string(id.index) + ": " + message->text);
        } else if(const auto move = std::get_if<world::action::Move>(&packet)) {
            if(!movePlayer(PLAYER_ENTITY_ID, move->pos)) {
                LOG_W("Bad move of player " << PLAYER_ENTITY_ID.index);
            }
        } else {
            LOG_W("Bad packet");
        }
    });
    localHandle->raycast = std::function([&](const geometry::Ray& ray){
        return this->raycast(ray); });
    localHandle->running = true;
    localHandle->entityChange = true;
}
SharedUniverse::~SharedUniverse() {
    LOG_D("Breaking shared universe");
    saveAll(true); //NOTE: save thread requires some virtual function calls
}

void SharedUniverse::loadChunk(area_<chunk_pos> area, chunk_pos diff, const world::ChunkContainer& chunks) {
    if(localHandle->onUpdate)
        localHandle->onUpdate(area, diff, chunks, Faces::All);
}
void SharedUniverse::updateChunk(area_map::iterator &it, world::ChunkContainer::iterator &it_c, chunk_pos diff, float deltaTime) {
    if (const auto neighbors = std::dynamic_pointer_cast<SharedChunk>(it_c->second)->update(deltaTime, true)) {
        localHandle->onUpdate(std::make_pair(it->first, it_c->first), diff, it->second->getChunks(), neighbors.value());
    }
}

void SharedUniverse::broadcastMessage(const std::string& text) {
    localHandle->message = text;
    Universe::broadcastMessage(text);
}
void SharedUniverse::broadcastEntities() {
    localHandle->entityChange = true;
    Universe::broadcastEntities();
}

std::shared_ptr<Chunk> SharedUniverse::createChunk(const chunk_pos &pos, const std::unique_ptr<world::generator::Abstract> &rnd) const {
    return std::make_shared<SharedChunk>(pos, rnd);
}
std::shared_ptr<Chunk> SharedUniverse::createChunk(std::istream &str) const {
    return std::make_shared<SharedChunk>(str);
}
std::shared_ptr<Chunk> SharedUniverse::createChunk() const {
    return std::make_shared<SharedChunk>();
}