#include "Dimension.h"

#include "Constants.h"
#include "Datei.h"
#include "Game.h"
#include "NoBlock.h"

using namespace Framework;

Dimension::Dimension(int id)
    : Thread(),
      nextStructureId(0),
      dimensionId(id),
      gravity(9.8f),
      chunks(new RCTrie<Chunk>()),
      entities(new RCArray<Entity>()),
      map(new DimensionMap(id)),
      stop(0),
      currentDayTime(0.0),
      nightDuration(0.0),
      nightTransitionDuration(0.0),
      dayDuration(1000.0)
{
    Datei d;
    d.setDatei(
        Game::INSTANCE->getWorldDirectory() + "/dim/" + Text(id) + "/meta.dim");
    if (d.existiert())
    {
        d.open(Datei::Style::lesen);
        d.lese((char*)&nextStructureId, 8);
        d.lese((char*)&currentDayTime, 8);
        d.close();
    }
    start();
}

Dimension::~Dimension()
{
    entities->release();
    chunks->release();
    map->release();
}

void Dimension::configureDayNightCyncle(
    double nightDuration, double nightTransitionDuration, double dayDuration)
{
    this->nightDuration = nightDuration;
    this->nightTransitionDuration = nightTransitionDuration;
    this->dayDuration = dayDuration;
}

void Dimension::api(Framework::InMemoryBuffer* zRequest,
    NetworkMessage* zResponse,
    Entity* zSource)
{
    DoLaterHandler laterHandler;
    char type;
    zRequest->lese(&type, 1);
    switch (type)
    {
    case 0: // chunk message
        {
            Punkt center;
            zRequest->lese((char*)&center.x, 4);
            zRequest->lese((char*)&center.y, 4);
            cs.lock();
            Chunk* cC = zChunk(Game::getChunkCenter(center.x, center.y));
            if (!cC)
            {
                // TODO: have a max amount of waiting requests per player
                waitingRequests.add(
                    {dynamic_cast<InMemoryBuffer*>(zRequest->getThis()),
                        center,
                        zSource->getId()});
                Game::INSTANCE->requestArea({center.x - CHUNK_SIZE / 2,
                    center.y - CHUNK_SIZE / 2,
                    center.x + CHUNK_SIZE / 2 - 1,
                    center.y + CHUNK_SIZE / 2 - 1,
                    dimensionId});
            }
            else
            {
                cC->api(zRequest, zSource, laterHandler);
            }
            cs.unlock();
            break;
        }
    case 1: // block message
        {
            Vec3<int> location;
            zRequest->lese((char*)&location.x, 4);
            zRequest->lese((char*)&location.y, 4);
            zRequest->lese((char*)&location.z, 4);
            Framework::Either<Block*, int> block = zBlock(location);
            if (block.isA())
            {
                block.getA()->api(zRequest, zResponse);
            }
            break;
        }
    case 2: // map request
        {
            map->api(zRequest, zResponse, zSource, this);
            break;
        }
    }
}

void Dimension::tickEntities()
{
    this->currentDayTime += 1.0 / MAX_TICKS_PER_SECOND;
    if (this->currentDayTime
        > dayDuration + nightDuration + nightTransitionDuration * 2)
    {
        this->currentDayTime
            -= dayDuration + nightDuration + nightTransitionDuration * 2;
    }
    for (auto entity : *entities)
    {
        if (!entity->isRemoved()
            && (entity->isMoving()
                || zChunk(Game::getChunkCenter((int)entity->getPosition().x,
                    (int)entity->getPosition().y))))
            entity->prepareTick(this);
    }
    int index = 0;
    for (auto entity : *entities)
    {
        if (!entity->isRemoved()
            && (entity->isMoving()
                || zChunk(Game::getChunkCenter((int)entity->getPosition().x,
                    (int)entity->getPosition().y))))
            entity->tick(this);
        index++;
    }
}

void Dimension::thread()
{
    // light calculation
    int index = 0;
    ZeitMesser messer;
    messer.messungStart();
    double time = 0;
    bool isForeground = 0;
    Framework::Array<Framework::Vec3<int>> internalLightUpdateQueue;
    while (!stop)
    {
        Vec3<int> position;
        if (internalLightUpdateQueue.getEintragAnzahl())
        {
            position = internalLightUpdateQueue.get(0);
            internalLightUpdateQueue.remove(0);
        }
        else
        {
            removedChunksCs.lock();
            if (removedChunks.getEintragAnzahl() > 0)
            {
                Chunk* removedChunk = removedChunks.z(0);
                removedChunksCs.unlock();
                Text filePath = Game::INSTANCE->getWorldDirectory() + "/dim/"
                              + getDimensionId() + "/";
                filePath.appendHex(removedChunk->getCenter().x);
                filePath += "_";
                filePath.appendHex(removedChunk->getCenter().y);
                filePath += ".chunk";
                Datei d;
                d.setDatei(filePath);
                d.erstellen();
                d.open(Datei::Style::schreiben);
                removedChunk->save(&d);
                char addr[8];
                getAddrOfWorld(removedChunk->getCenter(), addr);
                map->removeMap(addr, 8);
                d.close();
                removedChunksCs.lock();
                removedChunks.remove(0);
            }
            removedChunksCs.unlock();
            if (priorizedLightUpdateQueue.getEintragAnzahl())
            {
                prioLightCs.lock();
                position = priorizedLightUpdateQueue.get(0);
                priorizedLightUpdateQueue.remove(0);
                prioLightCs.unlock();
                isForeground = 1;
            }
            else
            {
                if (!lightUpdateQueue.getEintragAnzahl())
                {
                    messer.messungEnde();
                    time += messer.getSekunden();
                    Sleep(500);
                    messer.messungStart();
                    continue;
                }
                lightCs.lock();
                position = lightUpdateQueue.get(0);
                lightUpdateQueue.remove(0);
                lightCs.unlock();
                isForeground = 0;
            }
        }
        Chunk* chunk
            = zChunk(Game::INSTANCE->getChunkCenter(position.x, position.y));
        if (position.z >= 0 && position.z < WORLD_HEIGHT)
        {
            if (chunk)
            {
                Vec3<int> chunkPos = chunkCoordinates(position);
                unsigned char* light = chunk->getLightData(chunkPos);
                unsigned char dayLight[6] = {255, 255, 255, 0, 0, 0};
                unsigned char noLight[6] = {0, 0, 0, 0, 0, 0};
                unsigned char newLight[6] = {0, 0, 0, 0, 0, 0};
                // add neighbor light emission
                for (int i = 0; i < 6; i++)
                {
                    unsigned char* neighborLeight;
                    Vec3<int> neighborPos
                        = position + getDirection(getDirectionFromIndex(i));
                    if (neighborPos.z < 0)
                    {
                        neighborLeight = noLight;
                    }
                    else if (neighborPos.z >= WORLD_HEIGHT)
                    {
                        neighborLeight = dayLight;
                    }
                    else
                    {
                        Chunk* neighborChunk
                            = zChunk(Game::INSTANCE->getChunkCenter(
                                neighborPos.x, neighborPos.y));
                        if (neighborChunk)
                            neighborLeight = neighborChunk->getLightData(
                                chunkCoordinates(neighborPos));
                        else
                            neighborLeight = noLight;
                    }
                    for (int j = 0; j < 3; j++)
                        newLight[j] = (unsigned char)MAX(newLight[j],
                            i == getDirectionIndex(TOP)
                                ? neighborLeight[j]
                                : (unsigned char)((float)neighborLeight[j]
                                                  * 0.8f));
                    for (int j = 3; j < 6; j++)
                        newLight[j] = (unsigned char)MAX(newLight[j],
                            (unsigned char)((float)neighborLeight[j] * 0.85f));
                }
                const Block* current = zBlockOrDefault(position);
                // add own light emission
                for (int j = 3; j < 6; j++)
                    newLight[j] = (unsigned char)MAX(
                        newLight[j], current->getLightEmisionColor()[j - 3]);
                current->filterPassingLight(newLight);
                current->filterPassingLight(newLight + 3);
                for (int i = 0; i < 6; i++)
                {
                    if (newLight[i] != light[i])
                    {
                        chunk->setLightData(chunkPos, newLight, isForeground);
                        for (int j = 0; j < 6; j++)
                            internalLightUpdateQueue.add(
                                position
                                    + getDirection(getDirectionFromIndex(j)),
                                0);
                        break;
                    }
                }
            }
        }
        index++;
        if (index > 100000)
        {
            messer.messungEnde();
            time += messer.getSekunden();
            std::cout << "100000 light updates needed " << time << " seconds\n";
            time = 0;
            index = 0;
            messer.messungStart();
        }
    }
    std::cout << Text("Dimension ") + this->getDimensionId()
                     + " update Thread exited.\n";
}

void Dimension::getAddrOf(Punkt cPos, char* addr) const
{
    *(int*)addr = cPos.x;
    *((int*)addr + 1) = cPos.y;
}

void Dimension::getAddrOfWorld(Punkt wPos, char* addr) const
{
    if (wPos.x < 0) wPos.x -= CHUNK_SIZE;
    if (wPos.y < 0) // needed because otherwise would (-8, -8) have the same
                    // adress as (8, 8)
        wPos.y -= CHUNK_SIZE;
    wPos /= CHUNK_SIZE;
    getAddrOf(wPos, addr);
}

void Dimension::saveStructure(MultiblockStructure* zStructure) const
{
    Datei d;
    Text path = Game::INSTANCE->getWorldDirectory() + "/dim/"
              + Text(dimensionId) + "/structures/";
    path.appendHex(zStructure->getStructureId());
    path += ".str";
    d.setDatei(path);
    d.erstellen();
    d.open(Datei::Style::schreiben);
    auto uPos = zStructure->getUniquePosition();
    d.schreibe((char*)&uPos.x, 4);
    d.schreibe((char*)&uPos.y, 4);
    d.schreibe((char*)&uPos.z, 4);
    int typeId = zStructure->getStructureTypeId();
    d.schreibe((char*)&typeId, 4);
    __int64 strId = zStructure->getStructureId();
    d.schreibe((char*)&strId, 8);
    StaticRegistry<MultiblockStructureType>::INSTANCE
        .zElement(zStructure->getStructureTypeId())
        ->saveStructure(zStructure, &d);
    d.close();
}

Chunk* Dimension::zChunk(Punkt wPos) const
{
    char addr[8];
    getAddrOfWorld(wPos, addr);
    return chunks->z(addr, 8);
}

Framework::Either<Block*, int> Dimension::zBlock(Vec3<int> location)
{
    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
    if (c)
    {
        location = chunkCoordinates(location);
        return c->zBlockAt(location);
    }
    return 0;
}

Block* Dimension::zRealBlockInstance(Framework::Vec3<int> location)
{
    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
    if (c)
    {
        location = chunkCoordinates(location);
        c->instantiateBlock(location);
        auto result = c->zBlockAt(location);
        return result.isA() ? result.getA() : 0;
    }
    return 0;
}

const Block* Dimension::zBlockOrDefault(Framework::Vec3<int> location)
{
    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
    if (c)
    {
        location = chunkCoordinates(location);
        return c->zBlockConst(location);
    }
    return &NoBlock::INSTANCE;
}

int Dimension::getBlockType(Framework::Vec3<int> location)
{
    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
    if (c)
    {
        location = chunkCoordinates(location);
        return c->getBlockTypeAt(location);
    }
    return BlockTypeEnum::NO_BLOCK;
}

void Dimension::placeBlock(
    Framework::Vec3<int> location, Framework::Either<Block*, int> block)
{
    Chunk* c = zChunk(Game::getChunkCenter(location.x, location.y));
    if (c)
    {
        location = chunkCoordinates(location);
        if (block.isA())
            c->putBlockAt(location, block);
        else
        {
            c->putBlockAt(location, 0);
            c->putBlockTypeAt(location, block);
        }
    }
    else if (block.isA())
        block.getA()->release();
}

void Dimension::sendBlockInfo(Framework::Vec3<int> location)
{
    Chunk* c = zChunk(Game::getChunkCenter(location.x, location.y));
    if (c)
    {
        location = chunkCoordinates(location);
        c->sendBlockInfo(location);
    }
}

void Dimension::addEntity(Entity* entity)
{
    entities->add(entity);
}

void Dimension::setChunk(Chunk* chunk, Punkt center)
{
    char addr[8];
    getAddrOfWorld(center, addr);
    if (chunk) map->loadMap(addr, 8, chunk);
    chunkCs.lock();
    Chunk* old = chunks->get(addr, 8);
    if (old)
    {
        Game::INSTANCE->zTickOrganizer()->removeTickSource(old);
        old->prepareRemove();
        for (int i = 0; i < chunkList.getEintragAnzahl(); i++)
        {
            if (chunkList.get(i) == old)
            {
                chunkList.remove(i);
                break;
            }
        }
    }
    chunks->set(addr, 8, chunk);
    if (chunk)
    {
        chunkList.add(chunk);
        chunk->setAdded();
    }
    getAddrOfWorld(center + Punkt(CHUNK_SIZE, 0), addr);
    Chunk* zChunk = chunks->z(addr, 8);
    if (zChunk)
    {
        zChunk->setNeighbor(WEST, chunk);
        if (chunk)
        {
            chunk->setNeighbor(EAST, zChunk);
        }
    }
    getAddrOfWorld(center + Punkt(-CHUNK_SIZE, 0), addr);
    zChunk = chunks->z(addr, 8);
    if (zChunk)
    {
        zChunk->setNeighbor(EAST, chunk);
        if (chunk) chunk->setNeighbor(WEST, zChunk);
    }
    getAddrOfWorld(center + Punkt(0, CHUNK_SIZE), addr);
    zChunk = chunks->z(addr, 8);
    if (zChunk)
    {
        zChunk->setNeighbor(NORTH, chunk);
        if (chunk) chunk->setNeighbor(SOUTH, zChunk);
    }
    getAddrOfWorld(center + Punkt(0, -CHUNK_SIZE), addr);
    zChunk = chunks->z(addr, 8);
    if (zChunk)
    {
        zChunk->setNeighbor(SOUTH, chunk);
        if (chunk) chunk->setNeighbor(NORTH, zChunk);
    }
    DoLaterHandler laterHandler;
    if (chunk)
    {
        cs.lock();
        int index = 0;
        for (Iterator<RequestQueue> iterator = waitingRequests.begin();
             iterator;)
        {
            Entity* zE = Game::INSTANCE->zEntity(iterator.val().sourceId);
            if (zE)
            {
                if (iterator.val().chunkCenter == chunk->getCenter())
                {
                    chunk->api(iterator.val().request, zE, laterHandler);
                    iterator.val().request->release();
                    iterator.remove();
                    continue;
                }
            }
            else
            {
                iterator.val().request->release();
                iterator.remove();
                continue;
            }
            iterator++;
            index++;
        }
        cs.unlock();
        Game::INSTANCE->zTickOrganizer()->addTickSource(chunk);
    }
    chunkCs.unlock();
    if (old)
    {
        old->onUnloaded();
        removedChunksCs.lock();
        removedChunks.add(old);
        removedChunksCs.unlock();
    }
    if (chunk) chunk->onLoaded();
    laterHandler.execute();
    if (chunk) updateLightAtChunkBorders(center);
}

void Dimension::save(Text worldDir) const
{
    Datei d;
    d.setDatei(Game::INSTANCE->getWorldDirectory() + "/dim/" + Text(dimensionId)
               + "/meta.dim");
    d.erstellen();
    d.open(Datei::Style::schreiben);
    d.schreibe((char*)&nextStructureId, 8);
    d.schreibe((char*)&currentDayTime, 8);
    d.close();
    for (auto chunk = chunkList.begin(); chunk; chunk++)
    {
        if (!chunk._) continue;
        Datei* file = new Datei();
        Text filePath = worldDir + "/dim/" + dimensionId + "/";
        filePath.appendHex(chunk->getCenter().x);
        filePath += "_";
        filePath.appendHex(chunk->getCenter().y);
        filePath += ".chunk";
        file->setDatei(filePath);
        if (file->open(Datei::Style::schreiben)) chunk->save(file);
        file->close();
        file->release();
        char addr[8];
        getAddrOfWorld(chunk->getCenter(), addr);
        map->saveMap(addr, 8);
    }
    Text filePath = worldDir + "/dim/" + dimensionId + "/entities";
    Datei* file = new Datei();
    file->setDatei(filePath);
    if (file->open(Datei::Style::schreiben))
    {
        for (Entity* entity : *entities)
        {
            if (entity->zType()->getId() != EntityTypeEnum::PLAYER)
            {
                if (!entity->isRemoved())
                {
                    int type = entity->zType()->getId();
                    file->schreibe((char*)&type, 4);
                    StaticRegistry<EntityType>::INSTANCE.zElement(type)
                        ->saveEntity(entity, file);
                }
            }
            else
            {
                Datei pFile;
                pFile.setDatei(worldDir + "/player/"
                               + Game::INSTANCE->getPlayerId(
                                   ((Player*)entity)->getName()));
                if (pFile.open(Datei::Style::schreiben))
                    StaticRegistry<EntityType>::INSTANCE
                        .zElement(EntityTypeEnum::PLAYER)
                        ->saveEntity(entity, &pFile);
            }
        }
        file->close();
    }
    for (MultiblockStructure* structure : structures)
    {
        saveStructure(structure);
    }
}

int Dimension::getDimensionId() const
{
    return dimensionId;
}

bool Dimension::hasChunck(int x, int y)
{
    if (zChunk(Punkt(x, y))) return 1;
    removedChunksCs.lock();
    for (Chunk* c : removedChunks)
    {
        if (c->getCenter().x == x && c->getCenter().y == y)
        {
            removedChunksCs.unlock();
            return 1;
        }
    }
    removedChunksCs.unlock();
    return 0;
}

bool Dimension::reviveChunk(int x, int y)
{
    chunkCs.lock();
    if (zChunk(Punkt(x, y)))
    {
        chunkCs.unlock();
        return 1;
    }
    removedChunksCs.lock();
    int index = 0;
    for (Iterator<Chunk*> i = removedChunks.begin(); i; i++)
    {
        if (i->getCenter().x == x && i->getCenter().y == y)
        {
            setChunk(dynamic_cast<Chunk*>(i->getThis()), Punkt(x, y));
            if (index > 0) i.remove();
            removedChunksCs.unlock();
            chunkCs.unlock();
            return 1;
        }
        index++;
    }
    removedChunksCs.unlock();
    chunkCs.unlock();
    return 0;
}

float Dimension::getGravity() const
{
    return gravity;
}

void Dimension::removeOldChunks()
{
    chunkCs.lock();
    int index = 0;
    for (Chunk* chunk : chunkList)
    {
        if (!chunk->hasObservers()) setChunk(0, chunk->getCenter());
        index++;
    }
    chunkCs.unlock();
    structurCs.lock();
    Iterator<MultiblockStructure*> i = structures.begin();
    while (i)
    {
        if (i->isEmpty())
        {
            i.remove();
            continue;
        }
        else if (i->isFullyUnloaded())
        {
            saveStructure(i);
            i.remove();
            continue;
        }
        i++;
    }
    structurCs.unlock();
}

Entity* Dimension::zEntity(int id)
{
    for (auto entity : *entities)
    {
        if (!entity->isRemoved() && entity->getId() == id) return entity;
    }
    return 0;
}

Entity* Dimension::zNearestEntity(
    Framework::Vec3<float> pos, std::function<bool(Entity*)> filter)
{
    Entity* result = 0;
    float sqDist = 0;
    for (auto entity : *entities)
    {
        if (!entity->isRemoved() && filter(entity))
        {
            float d = pos.abstandSq(entity->getPosition());
            if (!result || d < sqDist)
            {
                result = entity;
                sqDist = d;
            }
        }
    }
    return result;
}

void Dimension::removeEntity(int id)
{
    int index = 0;
    for (auto entity : *entities)
    {
        if (entity->getId() == id)
        {
            entities->remove(index);
            return;
        }
        index++;
    }
}

void Dimension::removeSubscriptions(Entity* zEntity)
{
    for (Chunk* chunk : chunkList)
        chunk->removeObserver(zEntity);
}

void Dimension::updateLightning(Vec3<int> location)
{
    lightCs.lock();
    lightUpdateQueue.add(location, 0);
    lightCs.unlock();
}

void Dimension::updateLightningWithoutWait(Framework::Vec3<int> location)
{
    prioLightCs.lock();
    priorizedLightUpdateQueue.add(location, 0);
    prioLightCs.unlock();
}

void Dimension::updateLightAtChunkBorders(Punkt chunkCenter)
{
    if (lightUpdateQueue.getEintragAnzahl() > 300000)
    {
        std::cout
            << "warning: light calculation queue is over 300000 blocks long\n";
    }
    for (int i = WORLD_HEIGHT - 1; i >= 0; i--)
    {
        for (int j = 0; j < CHUNK_SIZE; j++)
        {
            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 - 1,
                chunkCenter.y - CHUNK_SIZE / 2 + j,
                i));
            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2,
                chunkCenter.y - CHUNK_SIZE / 2 + j,
                i));
            updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2 - 1,
                chunkCenter.y - CHUNK_SIZE / 2 + j,
                i));
            updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2,
                chunkCenter.y - CHUNK_SIZE / 2 + j,
                i));
            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
                chunkCenter.y - CHUNK_SIZE / 2 - 1,
                i));
            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
                chunkCenter.y - CHUNK_SIZE / 2,
                i));
            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
                chunkCenter.y + CHUNK_SIZE / 2 - 1,
                i));
            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
                chunkCenter.y + CHUNK_SIZE / 2,
                i));
        }
    }
}

__int64 Dimension::getNextStructureId()
{
    return nextStructureId++;
}

void Dimension::addStructure(MultiblockStructure* structure)
{
    structurCs.lock();
    structures.add(structure);
    structurCs.unlock();
}

MultiblockStructure* Dimension::zStructureByPosition(
    Framework::Vec3<int> uniquePosition)
{
    structurCs.lock();
    for (MultiblockStructure* str : structures)
    {
        if (str->getUniquePosition() == uniquePosition)
        {
            structurCs.unlock();
            return str;
        }
    }
    // search for structure file
    Datei dir(Game::INSTANCE->getWorldDirectory() + "/dim/" + Text(dimensionId)
              + "/structures");
    RCArray<Text>* names = dir.getDateiListe();
    if (names)
    {
        Vec3<int> uPos;
        for (Text* name : *names)
        {
            Datei d(Text(dir.zPfad()->getText()) + "/" + name->getText());
            if (d.open(Datei::Style::lesen))
            {
                d.lese((char*)&uPos.x, 4);
                d.lese((char*)&uPos.y, 4);
                d.lese((char*)&uPos.z, 4);
                if (uPos == uniquePosition)
                {
                    int type;
                    d.lese((char*)&type, 4);
                    __int64 strId;
                    d.lese((char*)&strId, 8);
                    MultiblockStructure* str
                        = StaticRegistry<MultiblockStructureType>::INSTANCE
                              .zElement(type)
                              ->loadStructure(
                                  dimensionId, strId, uniquePosition, &d);
                    d.close();
                    structures.add(str);
                    names->release();
                    structurCs.unlock();
                    return str;
                }
                d.close();
            }
        }
        names->release();
    }
    structurCs.unlock();
    return 0;
}

MultiblockStructure* Dimension::zStructureById(__int64 id)
{
    structurCs.lock();
    for (MultiblockStructure* str : structures)
    {
        if (str->getStructureId() == id)
        {
            structurCs.unlock();
            return str;
        }
    }
    // search for structure file
    Text path = Game::INSTANCE->getWorldDirectory() + "/dim/"
              + Text(dimensionId) + "/structures/";
    path.appendHex(id);
    path += ".str";
    Datei d(path);
    Vec3<int> uPos;
    if (d.open(Datei::Style::lesen))
    {
        d.lese((char*)&uPos.x, 4);
        d.lese((char*)&uPos.y, 4);
        d.lese((char*)&uPos.z, 4);
        int type;
        d.lese((char*)&type, 4);
        __int64 strId;
        d.lese((char*)&strId, 8);
        MultiblockStructure* str
            = StaticRegistry<MultiblockStructureType>::INSTANCE.zElement(type)
                  ->loadStructure(dimensionId, strId, uPos, &d);
        d.close();
        structures.add(str);
        structurCs.unlock();
        return str;
    }
    std::cout << "WARNING: did not find Structure information file '" << path
              << "'.\n";
    structurCs.unlock();
    return 0;
}

void Dimension::requestStopAndWait()
{
    stop = 1;
    warteAufThread(1000000);
}

void Dimension::updateMap(int x, int y, int height)
{
    chunkCs.lock();
    int h1 = height % 2 == 0 ? height : height - 1;
    int h2 = h1 + 1;
    const Block* b1 = zBlockOrDefault({x, y, h1});
    const Block* b2 = zBlockOrDefault({x, y, h2});
    bool visible = 1;
    if (h2 != WORLD_HEIGHT - 1)
    {
        const Block* b3 = zBlockOrDefault({x, y, h2 + 1});
        visible = b3->isPassable() || b3->isTransparent();
    }
    int color1
        = (b2->isPassable() || b2->isTransparent()) ? b1->getMapColor() : 0;
    int color2 = visible ? b2->getMapColor() : 0;
    int color1m = 0;
    int color2m = 0;
    if (h1 > 0)
    {
        const Block* b1m = zBlockOrDefault({x, y, h1 - 2});
        const Block* b2m = zBlockOrDefault({x, y, h1 - 1});
        color1m = (b2m->isPassable() || b2m->isTransparent())
                    ? b1m->getMapColor()
                    : 0;
        color2m = (b1->isPassable() || b1->isTransparent()) ? b2m->getMapColor()
                                                            : 0;
    }
    char addr[8];
    Punkt center = Game::INSTANCE->getChunkCenter(x, y);
    getAddrOfWorld(center, addr);
    ChunkMap* cMap = map->getMap(addr, 8, center);
    if (cMap)
    {
        Framework::Vec3<int> chunkLocation = chunkCoordinates({x, y, height});
        if (cMap->update((char)chunkLocation.x,
                (char)chunkLocation.y,
                (unsigned char)(chunkLocation.z / 2),
                color1,
                color2)
            || (h1 > 0
                && cMap->update((char)chunkLocation.x,
                    (char)chunkLocation.y,
                    (unsigned char)(chunkLocation.z / 2) - 1,
                    color1m,
                    color2m)))
        {
            map->onMapUpdated(addr, 8);
        }
    }
    chunkCs.unlock();
}

int Dimension::getChunkCount()
{
    return chunkList.getEintragAnzahl();
}

double Dimension::getCurrentDayTime() const
{
    return currentDayTime;
}

double Dimension::getNightDuration() const
{
    return nightDuration;
}

double Dimension::getNightTransitionDuration() const
{
    return nightTransitionDuration;
}

double Dimension::getDayDuration() const
{
    return dayDuration;
}

DimensionFactory::DimensionFactory(int dimensionId)
    : ReferenceCounter(),
      dimensionId(dimensionId)
{}

int DimensionFactory::getDimensionId() const
{
    return dimensionId;
}