#include "WorldLoader.h"

#include <Datei.h>
#include <Logging.h>
#include <Punkt.h>
#include <Text.h>

#include "Dimension.h"
#include "Entity.h"
#include "EntityType.h"
#include "Game.h"
#include "WorldGenerator.h"

using namespace Framework;

WorldLoader::WorldLoader()
    : Thread(),
      exit(0)
{
    setName("World Loader");
    Framework::Datei d;
    d.setDatei(Game::INSTANCE->getWorldDirectory() + "/dim");
    RCArray<Text>* names = d.getDateiListe();
    if (names)
    {
        for (Text* name : *names)
        {
            Framework::Datei entities;
            entities.setDatei(Game::INSTANCE->getWorldDirectory() + "/dim/"
                              + Text(name->getText()) + "/entities");
            if (entities.open(Framework::Datei::Style::lesen))
            {
                Dimension* dim
                    = Game::INSTANCE->zGenerator()->createDimension((int)*name);
                if (dim)
                {
                    while (!entities.istEnde())
                    {
                        int type = 0;
                        entities.lese((char*)&type, 4);
                        dim->addEntity(
                            Game::INSTANCE->zEntityType(type)->loadEntity(
                                &entities));
                    }
                    Game::INSTANCE->addDimension(dim);
                }
                else
                {
                    Framework::Logging::error()
                        << "could not create dimension " << *name
                        << ". No Factory was provided.";
                }
            }
        }
        names->release();
    }
    start();
}

WorldLoader::~WorldLoader() {}

void WorldLoader::thread()
{
    while (!exit)
    {
        cs.lock();
        Area next;
        bool hasNext = 0;
        if (requestQueue.getEintragAnzahl() > 0)
        {
            next = requestQueue.get(0);
            requestQueue.remove(0);
            hasNext = 1;
        }
        cs.unlock();
        if (!hasNext)
        {
            Sleep(1000);
            continue;
        }
        Punkt start = Game::INSTANCE->getChunkCenter(next.startX, next.startY);
        Punkt end = Game::INSTANCE->getChunkCenter(next.endX, next.endY);
        int xDir = start.x > end.x ? -1 : 1;
        int yDir = start.y > end.y ? -1 : 1;
        for (int x = start.x; xDir < 0 ? x >= end.x : x <= end.x;
             x += CHUNK_SIZE * xDir)
        {
            for (int y = start.y; yDir < 0 ? y >= end.y : y <= end.y;
                 y += CHUNK_SIZE * yDir)
            {
                Dimension* zDim = Game::INSTANCE->zDimension(next.dimensionId);
                bool revived = 0;
                if (!zDim->zChunk(Punkt(x, y)) && zDim->hasChunck(x, y))
                {
                    revived = zDim->reviveChunk(x, y);
                }
                if (!revived
                    && !Game::INSTANCE->isChunkLoaded(x, y, next.dimensionId))
                {
                    Datei* file = new Datei();
                    Text filePath = Game::INSTANCE->getWorldDirectory()
                                  + "/dim/" + next.dimensionId + "/";
                    filePath.appendHex(x);
                    filePath += "_";
                    filePath.appendHex(y);
                    filePath += ".chunk";
                    file->setDatei(filePath);
                    if (file->open(Datei::Style::lesen))
                    {
                        Chunk* chunk = new Chunk(
                            Framework::Punkt(x, y), next.dimensionId, file);
                        Dimension* dim
                            = Game::INSTANCE->zDimension(next.dimensionId);
                        if (!dim)
                        {
                            dim = new Dimension(next.dimensionId);
                            Game::INSTANCE->addDimension(dim);
                        }
                        dim->setChunk(chunk, Punkt(x, y));
                    }
                    file->close();
                    file->release();
                }
            }
        }
    }
    Framework::Logging::info() << "World Loader thread exited";
}

void WorldLoader::requestLoading(Area request)
{
    cs.lock();
    requestQueue.add(request);
    cs.unlock();
}

void WorldLoader::exitAndWait()
{
    exit = 1;
    warteAufThread(10000);
    ende();
}

bool WorldLoader::existsChunk(int x, int y, int dimension) const
{
    Text filePath
        = Game::INSTANCE->getWorldDirectory() + "/dim/" + dimension + "/";
    filePath.appendHex(x);
    filePath += "_";
    filePath.appendHex(y);
    filePath += ".chunk";
    return DateiExistiert(filePath);
}