#include "FactoryClient.h"

#include <File.h>
#include <FileSystem.h>
#include <Image.h>
#include <M3File.h>
#include <RenderThread.h>
#include <Texture.h>

#include "Globals.h"
#include "Load.h"

using namespace Network;
using namespace Framework;

void createDefaultCube(Screen* zScreen)
{
    Model3DData* data = zScreen->zGraphicsApi()->createModel("cube");
    data->setAmbientFactor(0.f);
    data->setDiffusFactor(1.f);
    data->setSpecularFactor(0.f);
    float size = 1;
    float left, right, top, bottom;
    // Calculate the screen coordinates of the left side of the bitmap.
    right = (float)((-size / 2.0));
    // Calculate the screen coordinates of the right side of the bitmap.
    left = right + (float)size;
    // Calculate the screen coordinates of the top of the bitmap.
    top = (float)(size / 2.0);
    // Calculate the screen coordinates of the bottom of the bitmap.
    bottom = top - (float)size;
    float front = -size / 2;
    float back = front + size;

    Vertex3D* vertecies = new Vertex3D[24];
    for (int i = 0; i < 24; i++)
        vertecies[i].knochenId = 0;
    // front side
    vertecies[0].pos = Vec3<float>(left, front, top);
    vertecies[0].tPos = Vec2<float>(0.f, 0.f);
    vertecies[1].pos = Vec3<float>(right, front, top);
    vertecies[1].tPos = Vec2<float>(1.f, 0.f);
    vertecies[2].pos = Vec3<float>(left, front, bottom);
    vertecies[2].tPos = Vec2<float>(0.f, 1.f);
    vertecies[3].pos = Vec3<float>(right, front, bottom);
    vertecies[3].tPos = Vec2<float>(1.f, 1.f);
    // back side
    vertecies[4].pos = Vec3<float>(right, back, top);
    vertecies[4].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[5].pos = Vec3<float>(left, back, top);
    vertecies[5].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[6].pos = Vec3<float>(right, back, bottom);
    vertecies[6].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[7].pos = Vec3<float>(left, back, bottom);
    vertecies[7].tPos = Vec2<float>(1.0f, 1.0f);
    // left side
    vertecies[8].pos = Vec3<float>(left, back, top);
    vertecies[8].tPos = Vec2<float>(0.f, 0.f);
    vertecies[9].pos = Vec3<float>(left, front, top);
    vertecies[9].tPos = Vec2<float>(1.f, 0.f);
    vertecies[10].pos = Vec3<float>(left, back, bottom);
    vertecies[10].tPos = Vec2<float>(0.f, 1.f);
    vertecies[11].pos = Vec3<float>(left, front, bottom);
    vertecies[11].tPos = Vec2<float>(1.f, 1.f);
    // right side
    vertecies[12].pos = Vec3<float>(right, front, top);
    vertecies[12].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[13].pos = Vec3<float>(right, back, top);
    vertecies[13].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[14].pos = Vec3<float>(right, front, bottom);
    vertecies[14].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[15].pos = Vec3<float>(right, back, bottom);
    vertecies[15].tPos = Vec2<float>(1.0f, 1.0f);
    // top side
    vertecies[16].pos = Vec3<float>(left, back, top);
    vertecies[16].tPos = Vec2<float>(0.f, 0.f);
    vertecies[17].pos = Vec3<float>(right, back, top);
    vertecies[17].tPos = Vec2<float>(1.f, 0.f);
    vertecies[18].pos = Vec3<float>(left, front, top);
    vertecies[18].tPos = Vec2<float>(0.f, 1.f);
    vertecies[19].pos = Vec3<float>(right, front, top);
    vertecies[19].tPos = Vec2<float>(1.f, 1.f);
    // botom side
    vertecies[20].pos = Vec3<float>(left, front, bottom);
    vertecies[20].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[21].pos = Vec3<float>(right, front, bottom);
    vertecies[21].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[22].pos = Vec3<float>(left, back, bottom);
    vertecies[22].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[23].pos = Vec3<float>(right, back, bottom);
    vertecies[23].tPos = Vec2<float>(1.0f, 1.0f);

    data->setVertecies(vertecies, 24);
    // the order of the polygons has to be NORTH (front), EAST (left), SOUTH
    // (back), WEST (right), TOP, BOTTOM according to the Area definition
    // front side
    Polygon3D* p
        = new Polygon3D(); // looking from (0,0,0) to (1,0,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0;
    p->indexList[1] = 1;
    p->indexList[2] = 2;
    p->indexList[3] = 1;
    p->indexList[4] = 3;
    p->indexList[5] = 2;
    data->addPolygon(p);
    // left side
    p = new Polygon3D(); // looking from (0,0,0) to (0,1,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 8;
    p->indexList[1] = 1 + 8;
    p->indexList[2] = 2 + 8;
    p->indexList[3] = 1 + 8;
    p->indexList[4] = 3 + 8;
    p->indexList[5] = 2 + 8;
    data->addPolygon(p);
    // back side
    p = new Polygon3D(); // looking from (0,0,0) to (-1,0,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 4;
    p->indexList[1] = 1 + 4;
    p->indexList[2] = 2 + 4;
    p->indexList[3] = 1 + 4;
    p->indexList[4] = 3 + 4;
    p->indexList[5] = 2 + 4;
    data->addPolygon(p);
    // right side
    p = new Polygon3D(); // looking from (0,0,0) to (0,-1,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 12;
    p->indexList[1] = 1 + 12;
    p->indexList[2] = 2 + 12;
    p->indexList[3] = 1 + 12;
    p->indexList[4] = 3 + 12;
    p->indexList[5] = 2 + 12;
    data->addPolygon(p);
    // top side
    p = new Polygon3D(); // looking from (0,0,0) to (0,0,-1) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 16;
    p->indexList[1] = 1 + 16;
    p->indexList[2] = 2 + 16;
    p->indexList[3] = 1 + 16;
    p->indexList[4] = 3 + 16;
    p->indexList[5] = 2 + 16;
    data->addPolygon(p);
    // botom side
    p = new Polygon3D(); // looking from (0,0,0) to (0,0,1) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 20;
    p->indexList[1] = 1 + 20;
    p->indexList[2] = 2 + 20;
    p->indexList[3] = 1 + 20;
    p->indexList[4] = 3 + 20;
    p->indexList[5] = 2 + 20;
    data->addPolygon(p);
    data->calculateNormals();
    data->release();
}

void createGrass(Screen* zScreen)
{
    Model3DData* data = zScreen->zGraphicsApi()->createModel("grass");
    data->setAmbientFactor(0.f);
    data->setDiffusFactor(1.f);
    data->setSpecularFactor(0.f);

    float size = 1;
    float left, right, top, bottom;
    // Calculate the screen coordinates of the left side of the bitmap.
    right = (float)((-size / 2.0));
    // Calculate the screen coordinates of the right side of the bitmap.
    left = right + (float)size;
    // Calculate the screen coordinates of the top of the bitmap.
    top = (float)(size / 2.0);
    // Calculate the screen coordinates of the bottom of the bitmap.
    bottom = top - (float)size;
    float front = -size / 2;
    float back = front + size;

    Vertex3D* vertecies = new Vertex3D[32];
    for (int i = 0; i < 32; i++)
        vertecies[i].knochenId = 0;

    // first y plane
    vertecies[0].pos = Vec3<float>(left, front + 0.2f, top);
    vertecies[0].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[1].pos = Vec3<float>(right, front + 0.2f, top);
    vertecies[1].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[2].pos = Vec3<float>(left, front + 0.2f, bottom);
    vertecies[2].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[3].pos = Vec3<float>(right, front + 0.2f, bottom);
    vertecies[3].tPos = Vec2<float>(0.99f, 0.99f);
    // second y plane
    vertecies[4].pos = Vec3<float>(left, front + 0.4f, top);
    vertecies[4].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[5].pos = Vec3<float>(right, front + 0.4f, top);
    vertecies[5].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[6].pos = Vec3<float>(left, front + 0.4f, bottom);
    vertecies[6].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[7].pos = Vec3<float>(right, front + 0.4f, bottom);
    vertecies[7].tPos = Vec2<float>(0.99f, 0.99f);
    // third y plane
    vertecies[8].pos = Vec3<float>(left, front + 0.6f, top);
    vertecies[8].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[9].pos = Vec3<float>(right, front + 0.6f, top);
    vertecies[9].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[10].pos = Vec3<float>(left, front + 0.6f, bottom);
    vertecies[10].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[11].pos = Vec3<float>(right, front + 0.6f, bottom);
    vertecies[11].tPos = Vec2<float>(0.99f, 0.99f);
    // forth y plane
    vertecies[12].pos = Vec3<float>(left, front + 0.8f, top);
    vertecies[12].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[13].pos = Vec3<float>(right, front + 0.8f, top);
    vertecies[13].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[14].pos = Vec3<float>(left, front + 0.8f, bottom);
    vertecies[14].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[15].pos = Vec3<float>(right, front + 0.8f, bottom);
    vertecies[15].tPos = Vec2<float>(0.99f, 0.99f);
    // first x plane
    vertecies[16].pos = Vec3<float>(right + 0.2f, front, top);
    vertecies[16].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[17].pos = Vec3<float>(right + 0.2f, back, top);
    vertecies[17].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[18].pos = Vec3<float>(right + 0.2f, front, bottom);
    vertecies[18].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[19].pos = Vec3<float>(right + 0.2f, back, bottom);
    vertecies[19].tPos = Vec2<float>(0.99f, 0.99f);
    // second x plane
    vertecies[20].pos = Vec3<float>(right + 0.4f, front, top);
    vertecies[20].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[21].pos = Vec3<float>(right + 0.4f, back, top);
    vertecies[21].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[22].pos = Vec3<float>(right + 0.4f, front, bottom);
    vertecies[22].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[23].pos = Vec3<float>(right + 0.4f, back, bottom);
    vertecies[23].tPos = Vec2<float>(0.99f, 0.99f);
    // third x plane
    vertecies[24].pos = Vec3<float>(right + 0.6f, front, top);
    vertecies[24].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[25].pos = Vec3<float>(right + 0.6f, back, top);
    vertecies[25].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[26].pos = Vec3<float>(right + 0.6f, front, bottom);
    vertecies[26].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[27].pos = Vec3<float>(right + 0.6f, back, bottom);
    vertecies[27].tPos = Vec2<float>(0.99f, 0.99f);
    // forth x plane
    vertecies[28].pos = Vec3<float>(right + 0.8f, front, top);
    vertecies[28].tPos = Vec2<float>(0.01f, 0.01f);
    vertecies[29].pos = Vec3<float>(right + 0.8f, back, top);
    vertecies[29].tPos = Vec2<float>(0.99f, 0.01f);
    vertecies[30].pos = Vec3<float>(right + 0.8f, front, bottom);
    vertecies[30].tPos = Vec2<float>(0.01f, 0.99f);
    vertecies[31].pos = Vec3<float>(right + 0.8f, back, bottom);
    vertecies[31].tPos = Vec2<float>(0.99f, 0.99f);
    for (int i = 0; i < 16; i++)
    {
        vertecies[i].normal = Vec3<float>(0, 1, 0);
    }
    for (int i = 16; i < 32; i++)
    {
        vertecies[i].normal = Vec3<float>(1, 0, 0);
    }

    data->setVertecies(vertecies, 32);

    Polygon3D* p = new Polygon3D();
    p->indexAnz = 6 * 8;
    p->indexList = new int[p->indexAnz];
    for (int i = 0; i < 8; i++)
    {
        p->indexList[i * 6 + 0] = 0 + i * 4;
        p->indexList[i * 6 + 1] = 1 + i * 4;
        p->indexList[i * 6 + 2] = 2 + i * 4;
        p->indexList[i * 6 + 3] = 1 + i * 4;
        p->indexList[i * 6 + 4] = 3 + i * 4;
        p->indexList[i * 6 + 5] = 2 + i * 4;
    }
    data->addPolygon(p);

    // data->calculateNormals();
    data->release();
}

void createFluidCube(Screen* zScreen)
{
    Model3DData* data = zScreen->zGraphicsApi()->createModel("fluid");
    data->setAmbientFactor(0.f);
    data->setDiffusFactor(1.f);
    data->setSpecularFactor(0.f);
    float size = 1;
    float left, right, top, bottom;
    // Calculate the screen coordinates of the left side of the bitmap.
    right = (float)((-size / 2.0));
    // Calculate the screen coordinates of the right side of the bitmap.
    left = right + (float)size;
    // Calculate the screen coordinates of the top of the bitmap.
    top = (float)(size / 2.0);
    // Calculate the screen coordinates of the bottom of the bitmap.
    bottom = top - (float)size;
    float front = -size / 2;
    float back = front + size;

    Vertex3D* vertecies = new Vertex3D[24];
    for (int i = 0; i < 24; i++)
        vertecies[i].knochenId = 0;
    // front side
    vertecies[0].pos = Vec3<float>(left, front, top);
    vertecies[0].tPos = Vec2<float>(0.f, 0.f);
    vertecies[1].pos = Vec3<float>(right, front, top);
    vertecies[1].tPos = Vec2<float>(1.f, 0.f);
    vertecies[2].pos = Vec3<float>(left, front, bottom);
    vertecies[2].tPos = Vec2<float>(0.f, 1.f);
    vertecies[3].pos = Vec3<float>(right, front, bottom);
    vertecies[3].tPos = Vec2<float>(1.f, 1.f);
    // back side
    vertecies[4].pos = Vec3<float>(right, back, top);
    vertecies[4].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[5].pos = Vec3<float>(left, back, top);
    vertecies[5].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[6].pos = Vec3<float>(right, back, bottom);
    vertecies[6].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[7].pos = Vec3<float>(left, back, bottom);
    vertecies[7].tPos = Vec2<float>(1.0f, 1.0f);
    // left side
    vertecies[8].pos = Vec3<float>(left, back, top);
    vertecies[8].tPos = Vec2<float>(0.f, 0.f);
    vertecies[9].pos = Vec3<float>(left, front, top);
    vertecies[9].tPos = Vec2<float>(1.f, 0.f);
    vertecies[10].pos = Vec3<float>(left, back, bottom);
    vertecies[10].tPos = Vec2<float>(0.f, 1.f);
    vertecies[11].pos = Vec3<float>(left, front, bottom);
    vertecies[11].tPos = Vec2<float>(1.f, 1.f);
    // right side
    vertecies[12].pos = Vec3<float>(right, front, top);
    vertecies[12].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[13].pos = Vec3<float>(right, back, top);
    vertecies[13].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[14].pos = Vec3<float>(right, front, bottom);
    vertecies[14].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[15].pos = Vec3<float>(right, back, bottom);
    vertecies[15].tPos = Vec2<float>(1.0f, 1.0f);
    // top side
    vertecies[16].pos = Vec3<float>(left, back, top);
    vertecies[16].tPos = Vec2<float>(0.f, 0.f);
    vertecies[17].pos = Vec3<float>(right, back, top);
    vertecies[17].tPos = Vec2<float>(1.f, 0.f);
    vertecies[18].pos = Vec3<float>(left, front, top);
    vertecies[18].tPos = Vec2<float>(0.f, 1.f);
    vertecies[19].pos = Vec3<float>(right, front, top);
    vertecies[19].tPos = Vec2<float>(1.f, 1.f);
    // botom side
    vertecies[20].pos = Vec3<float>(left, front, bottom);
    vertecies[20].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[21].pos = Vec3<float>(right, front, bottom);
    vertecies[21].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[22].pos = Vec3<float>(left, back, bottom);
    vertecies[22].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[23].pos = Vec3<float>(right, back, bottom);
    vertecies[23].tPos = Vec2<float>(1.0f, 1.0f);

    data->setVertecies(vertecies, 24);
    // the order of the polygons has to be NORTH (front), EAST (left), SOUTH
    // (back), WEST (right), TOP, BOTTOM according to the Area definition front
    // side
    Polygon3D* p
        = new Polygon3D(); // looking from (0,0,0) to (1,0,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0;
    p->indexList[1] = 1;
    p->indexList[2] = 2;
    p->indexList[3] = 1;
    p->indexList[4] = 3;
    p->indexList[5] = 2;
    data->addPolygon(p);
    // left side
    p = new Polygon3D(); // looking from (0,0,0) to (0,1,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 8;
    p->indexList[1] = 1 + 8;
    p->indexList[2] = 2 + 8;
    p->indexList[3] = 1 + 8;
    p->indexList[4] = 3 + 8;
    p->indexList[5] = 2 + 8;
    data->addPolygon(p);
    // back side
    p = new Polygon3D(); // looking from (0,0,0) to (-1,0,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 4;
    p->indexList[1] = 1 + 4;
    p->indexList[2] = 2 + 4;
    p->indexList[3] = 1 + 4;
    p->indexList[4] = 3 + 4;
    p->indexList[5] = 2 + 4;
    data->addPolygon(p);
    // right side
    p = new Polygon3D(); // looking from (0,0,0) to (0,-1,0) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 12;
    p->indexList[1] = 1 + 12;
    p->indexList[2] = 2 + 12;
    p->indexList[3] = 1 + 12;
    p->indexList[4] = 3 + 12;
    p->indexList[5] = 2 + 12;
    data->addPolygon(p);
    // top side
    p = new Polygon3D(); // looking from (0,0,0) to (0,0,-1) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 16;
    p->indexList[1] = 1 + 16;
    p->indexList[2] = 2 + 16;
    p->indexList[3] = 1 + 16;
    p->indexList[4] = 3 + 16;
    p->indexList[5] = 2 + 16;
    data->addPolygon(p);
    // botom side
    p = new Polygon3D(); // looking from (0,0,0) to (0,0,1) to see this side
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 20;
    p->indexList[1] = 1 + 20;
    p->indexList[2] = 2 + 20;
    p->indexList[3] = 1 + 20;
    p->indexList[4] = 3 + 20;
    p->indexList[5] = 2 + 20;
    data->addPolygon(p);

    data->calculateNormals();
    data->release();
}

void createModels(Screen* zScreen)
{
    createDefaultCube(zScreen);
    createGrass(zScreen);
    createFluidCube(zScreen);
}

struct FileInfo
{
    Text path;
    __int64 size;
    int year;
    int month;
    int day;
    int seconds;
};

FactoryClient::FactoryClient()
{
    client = 0;
    background = 0;
    foreground = 0;
    backgroundReader = 0;
    foregroundReader = 0;
    bgReaderUsage = 0;
    fgReaderUsage = 0;
}

FactoryClient::~FactoryClient()
{
    if (client) disconnect();
}

extern Framework::RenderTh* renderThread;

void FactoryClient::loadServerInfo()
{
    LoadMenu* loadMenu = (LoadMenu*)(Menu*)menuRegister->get("load");
    Text resourcePath = "data/resources/";
    resourcePath.append() << client->getServerIp() << "/"
                          << client->getServerPort() << "/";
    Text tmp = resourcePath + "metainfo.dat";
    File metaInfo(tmp);
    Trie<FileInfo*> metaInfos;
    Array<FileInfo*> metaInfoList;
    Array<FileInfo*> downloadOrder;
    if (metaInfo.exists())
    {
        loadMenu->beginNextStage(
            "reading resource metadata", (int)metaInfo.getSize());
        metaInfo.open(File::Style::read);
        while (!metaInfo.isEnd())
        {
            short len = 0;
            metaInfo.read((char*)&len, 2);
            char* path = new char[len + 1];
            metaInfo.read(path, len);
            path[len] = 0;
            FileInfo* fi = new FileInfo();
            fi->path = Text(path);
            fi->size = 0;
            metaInfo.read((char*)&fi->year, 4);
            metaInfo.read((char*)&fi->month, 4);
            metaInfo.read((char*)&fi->day, 4);
            metaInfo.read((char*)&fi->seconds, 4);
            metaInfos.set(path, len, fi);
            delete[] path;
            metaInfoList.add(fi);
            loadMenu->stageProgress(18 + len);
        }
        metaInfo.close();
    }
    short len = 0;
    foreground->getMessage((char*)&len, 2);
    while (len)
    {
        char* path = new char[len + 1];
        foreground->getMessage(path, len);
        path[len] = 0;
        FileInfo* fi = metaInfos.get(path, len);
        int year;
        int month;
        int day;
        int seconds;
        foreground->getMessage((char*)&year, 4);
        foreground->getMessage((char*)&month, 4);
        foreground->getMessage((char*)&day, 4);
        foreground->getMessage((char*)&seconds, 4);
        if (!fi || fi->year != year || fi->month != month || fi->day != day
            || fi->seconds != seconds)
        {
            if (!fi)
            {
                fi = new FileInfo();
                fi->path = Text(path);
                metaInfos.set(path, len, fi);
                metaInfoList.add(fi);
            }
            fi->year = year;
            fi->month = month;
            fi->day = day;
            fi->seconds = seconds;
            foreground->send("\1", 1);
            foreground->getMessage((char*)&fi->size, 8);
            downloadOrder.add(fi);
        }
        else
        {
            foreground->send("\0", 1);
        }
        delete[] path;
        foreground->getMessage((char*)&len, 2);
    }
    loadMenu->allProgress(1);
    char* buffer = new char[4096];
    loadMenu->beginNextStage(
        "download updated resouces", downloadOrder.getEntryCount());
    for (FileInfo* info : downloadOrder)
    {
        loadMenu->beginNextStep(info->path, (int)info->size);
        tmp = resourcePath + info->path;
        File file(tmp);
        if (!file.exists())
        {
            file.create();
        }
        file.open(File::Style::write);
        __int64 remaining = info->size;
        while (remaining > 0)
        {
            int toRead = remaining > 4096 ? 4096 : (int)remaining;
            foreground->getMessage(buffer, toRead);
            file.write(buffer, toRead);
            remaining -= toRead;
            loadMenu->stepProgress(toRead);
        }
        file.close();
        loadMenu->stageProgress(1);
    }
    delete[] buffer;
    loadMenu->allProgress(1);
    loadMenu->beginNextStage(
        "writing resource metadata", metaInfoList.getEntryCount());
    if (!metaInfo.exists())
    {
        metaInfo.create();
    }
    metaInfo.open(File::Style::write);
    for (FileInfo* info : metaInfoList)
    {
        short len = (short)info->path.getLength();
        metaInfo.write((char*)&len, 2);
        metaInfo.write(info->path.getText(), len);
        metaInfo.write((char*)&info->year, 4);
        metaInfo.write((char*)&info->month, 4);
        metaInfo.write((char*)&info->day, 4);
        metaInfo.write((char*)&info->seconds, 4);
        loadMenu->stageProgress(1);
    }
    metaInfo.close();
    loadMenu->allProgress(1);
    loadMenu->beginNextStage("loading resources", metaInfoList.getEntryCount());
    for (FileInfo* info : metaInfoList)
    {
        if (info->path.endsWith(".ltdb"))
        {
            LTDBFile dat;
            dat.setFile(new Text(resourcePath + info->path));
            dat.readData(0);
            loadMenu->beginNextStep(info->path, dat.getImageCount());
            for (Text* name : *dat.zImageListe())
            {
                Image* b = dat.load(0, new Text(*name));
                uiFactory.initParam.bildschirm->zGraphicsApi()
                    ->createOrGetTexture(info->path + "/" + *name, b)
                    ->release();
                loadMenu->stepProgress(1);
            }
        }
        else if (info->path.endsWith(".m3"))
        {
            M3File dat(resourcePath + info->path);
            dat.readData();
            loadMenu->beginNextStep(info->path, dat.getModelCount());
            for (int i = 0; i < dat.getModelCount(); i++)
            {
                Model3DData* d = dat.loadModel(dat.zModelName(i)->getText(),
                    uiFactory.initParam.bildschirm->zGraphicsApi(),
                    info->path + "/" + *dat.zModelName(i));
                d->release();
                loadMenu->stepProgress(1);
            }
        }
        loadMenu->stageProgress(1);
    }
    loadMenu->allProgress(1);
    createModels(uiFactory.initParam.bildschirm);
    loadMenu->allProgress(1);
    std::cout << "downloading server type information\n";
    // receive type information
    for (int i = 0; i < blockTypeCount; i++)
        blockTypes[i]->release();
    delete[] blockTypes;
    for (int i = 0; i < itemTypeCount; i++)
        itemTypes[i]->release();
    delete[] itemTypes;
    for (int i = 0; i < entityTypeCount; i++)
        entityTypes[i]->release();
    delete[] entityTypes;
    foregroundReader->read((char*)&blockTypeCount, 4);
    blockTypes = new BlockType*[blockTypeCount];
    for (int i = 0; i < blockTypeCount; i++)
    {
        int id;
        foregroundReader->read((char*)&id, 4);
        bool needsInstance;
        foregroundReader->read((char*)&needsInstance, 1);
        bool needsSubscription;
        foregroundReader->read((char*)&needsSubscription, 1);
        bool fluid;
        foregroundReader->read((char*)&fluid, 1);
        char maxFlowDistance = 0;
        if (fluid) foregroundReader->read((char*)&maxFlowDistance, 1);
        Direction flowDirection;
        foregroundReader->read((char*)&flowDirection, 1);
        int maxHp;
        foregroundReader->read((char*)&maxHp, 4);
        blockTypes[i] = new BlockType(id,
            needsInstance,
            ModelInfo(foregroundReader),
            maxHp,
            needsSubscription,
            fluid,
            maxFlowDistance,
            flowDirection);
    }
    loadMenu->allProgress(1);
    foregroundReader->read((char*)&itemTypeCount, 4);
    itemTypes = new ItemType*[itemTypeCount];
    for (int i = 0; i < itemTypeCount; i++)
    {
        int id;
        foregroundReader->read((char*)&id, 4);
        char len;
        foregroundReader->read((char*)&len, 1);
        char* name = new char[len + 1];
        foregroundReader->read(name, len);
        name[len] = 0;
        short tlen;
        foregroundReader->read((char*)&tlen, 2);
        char* tooltipUIML = new char[tlen + 1];
        foregroundReader->read(tooltipUIML, tlen);
        tooltipUIML[tlen] = 0;
        itemTypes[i] = new ItemType(
            id, ModelInfo(foregroundReader), Text(name), Text(tooltipUIML));
        delete[] name;
        delete[] tooltipUIML;
    }
    loadMenu->allProgress(1);
    foregroundReader->read((char*)&entityTypeCount, 4);
    entityTypes = new EntityType*[entityTypeCount];
    for (int i = 0; i < entityTypeCount; i++)
    {
        int id;
        foregroundReader->read((char*)&id, 4);
        entityTypes[i] = new EntityType(id, ModelInfo(foregroundReader));
    }
    loadMenu->allProgress(1);
    loadMenu->beginNextStage("rendering item icons", itemTypeCount);
    Cam3D* kam = new Cam3D();
    World3D* w = new World3D();
    w->addDiffuseLight(DiffuseLight{
        Vec3<float>(0.5f, 0.5f, -1.f), Vec3<float>(1.f, 1.f, 1.f)});
    kam->setWorld(w);
    kam->setScreenPosition(0, 0);
    kam->setScreenSize(50, 50);
    kam->setPosition(Vec3<float>(0, 0, 0));
    kam->setRotation(
        {(float)PI / 2.f, 0.f, std::atan2(0.f, -1.f) + (float)PI / 2});
    Image* b = new Image();
    b->newImage(50, 50, 0);
    for (int i = 0; i < itemTypeCount; i++)
    {
        Model3D* mdl = new Model3D();
        Model3DData* data = itemTypes[i]->getItemModel();
        if (data)
        {
            Vec3<float> min = data->getMinPos();
            Vec3<float> max = data->getMaxPos();
            float maxX = MAX(
                MAX(MAX(abs(min.x), abs(max.x)), MAX(abs(min.y), abs(max.y))),
                MAX(abs(min.z), abs(max.z)));
            kam->setPosition(Vec3<float>(maxX * 3.5f, 0.f, 0.f));
        }
        mdl->setModelData(data);
        mdl->setModelTextur(itemTypes[i]->getItemTextur());
        mdl->setPosition(Vec3<float>(0.f, 0.f, 0.f));
        mdl->setRotation(0.25f, 0.25f, 0.55f);
        mdl->setAmbientFactor(0.8f);
        mdl->setDiffusFactor(0.1f);
        mdl->setSpecularFactor(0.1f);
        mdl->setSize(itemTypes[i]->getSize());
        w->addDrawable(mdl);
        w->tick(0);
        DX11Texture* t = (DX11Texture*)window->zScreen()
                             ->zGraphicsApi()
                             ->createOrGetTexture(Text("rendered/items/")
                                                      + itemTypes[i]->getId(),
                                 dynamic_cast<Image*>(b->getThis()));
        renderThread->writeLock().lock();
        window->zScreen()->zGraphicsApi()->renderKamera(kam, t);
        renderThread->writeLock().unlock();
        Image* result = new Image();
        t->copyToImage(result);
        itemTypes[i]->setBild(result);
        t->release();
        w->removeDrawable(mdl);
        loadMenu->stageProgress(1);
    }
    b->release();
    kam->release();
    loadMenu->allProgress(1);
}

bool FactoryClient::connect(Text ip, unsigned short sslPort)
{
    if (client) disconnect();
    client = new SSLClient();
    if (!client->connect(sslPort, ip)) return false;
    char c;
    while (client->hasMessage(1))
        client->getMessage(&c, 1);
    this->ip = ip;
    return 1;
}

int FactoryClient::ping()
{
    Timer zm;
    zm.measureStart();
    if (!client->send("\3", 1)) return -1;
    char c;
    client->getMessage(&c, 1);
    zm.measureEnd();
    return (int)(zm.getSekunden() * 1000);
}

int FactoryClient::status(Framework::Text name, Framework::Text secret)
{
    if (!client->send("\4", 1)) return 404;
    char c;
    client->getMessage(&c, 1);
    if (c == 1)
    {
        char len = (char)name.getLength();
        client->send(&len, 1);
        client->send(name, len);
        short sLen = (short)secret.getLength();
        client->send((char*)&sLen, 2);
        client->send(secret, sLen);
        char res;
        client->getMessage(&res, 1);
        if (res == 1) return 200;
        if (res == 0) return 403;
    }
    return 404;
}

int FactoryClient::join(
    Framework::Text name, Framework::Text& secret, unsigned short port)
{
    client->send("\1", 1);
    char len = (char)name.getLength();
    client->send(&len, 1);
    client->send(name, len);
    short sLen = (short)secret.getLength();
    client->send((char*)&sLen, 2);
    client->send(secret, sLen);
    char res;
    client->getMessage(&res, 1);
    if (res == 1 || res == 2)
    {
        if (res == 2)
        {
            client->getMessage((char*)&sLen, 2);
            char* buffer = new char[sLen + 1];
            client->getMessage(buffer, sLen);
            buffer[sLen] = 0;
            secret = buffer;
            delete[] buffer;
        }
        short keyLen;
        client->getMessage((char*)&keyLen, 2);
        char* key = new char[keyLen];
        client->getMessage(key, keyLen);
        foreground = new Client();
        if (!foreground->connect(port, ip))
        {
            delete[] key;
            return false;
        }
        if (!foreground->send((char*)&keyLen, 2))
        {
            delete[] key;
            return false;
        }
        if (!foreground->send(key, keyLen))
        {
            delete[] key;
            return false;
        }
        background = new Client();
        if (!background->connect(port, ip))
        {
            delete[] key;
            foreground->release();
            foreground = 0;
            background->release();
            background = 0;
            return false;
        }
        if (!background->send((char*)&keyLen, 2))
        {
            delete[] key;
            foreground->release();
            foreground = 0;
            background->release();
            background = 0;
            return false;
        }
        if (!background->send(key, keyLen))
        {
            delete[] key;
            foreground->release();
            foreground = 0;
            background->release();
            background = 0;
            return false;
        }
        delete[] key;
        bool bg = 0;
        if (!foreground->send((char*)&bg, 1))
        {
            delete[] key;
            return 201;
        }
        foregroundReader = new NetworkReader(foreground);
        bg = 1;
        if (!background->send((char*)&bg, 1)) return 201;
        backgroundReader = new NetworkReader(background);
        char res;
        foregroundReader->read(&res, 1);
        if (res != 1) return 403;
        backgroundReader->read(&res, 1);
        if (res != 1) return 403;
        client->disconnect();
        loadServerInfo();
        return 200;
    }
    if (res == 0) return 403;
    return 500;
}

void FactoryClient::disconnect()
{
    if (client)
    {
        NetworkReader* fgReader = foregroundReader;
        NetworkReader* bgReader = backgroundReader;
        backgroundReader = 0;
        foregroundReader = 0;
        if (foreground) foreground->disconnect();
        if (background) background->disconnect();
        while (fgReaderUsage > 0 || bgReaderUsage > 0)
            Sleep(100);
        delete fgReader;
        delete bgReader;
        client->release();
        client = 0;
        if (foreground) foreground->release();
        foreground = 0;
        if (background) background->release();
        background = 0;
    }
}

NetworkReader* FactoryClient::getNextForegroundMessage()
{
    fgReaderUsage++;
    if (!foreground) return 0;
    if (!foreground->isConnected()) return 0;
    if (!foreground->hasMessage(0)) return 0;
    return foregroundReader;
}

NetworkReader* FactoryClient::getNextBackgroundMessage()
{
    bgReaderUsage++;
    if (!background) return 0;
    if (!background->isConnected()) return 0;
    if (!background->hasMessage(0)) return 0;
    return backgroundReader;
}

void FactoryClient::endMessageReading(bool bg)
{
    if (bg)
        bgReaderUsage--;
    else
        fgReaderUsage--;
}

void FactoryClient::sendPlayerAction(const char* data, unsigned short length)
{
    if (!foreground) return;
    cs.lock();
    length += 1;
    foreground->send((char*)&length, 2);
    char msgId = 2;
    foreground->send(&msgId, 1);
    foreground->send((char*)data, length - 1);
    cs.unlock();
}

void FactoryClient::sendPlayerFaceDirection(Vec3<float> dir)
{
    if (!foreground) return;
    cs.lock();
    short length = 14;
    foreground->send((char*)&length, 2);
    char msgId = 2; // player message
    foreground->send(&msgId, 1);
    foreground->send(&msgId, 1); // set face direction
    foreground->send((char*)&dir.x, 4);
    foreground->send((char*)&dir.y, 4);
    foreground->send((char*)&dir.z, 4);
    cs.unlock();
}

void FactoryClient::sendPlayerMovement(int flags)
{
    if (!foreground) return;
    cs.lock();
    short length = 6;
    foreground->send((char*)&length, 2);
    char msgId = 2; // player message
    foreground->send(&msgId, 1);
    msgId = 10; // set movement
    foreground->send(&msgId, 1);
    foreground->send((char*)&flags, 4);
    cs.unlock();
}

void FactoryClient::entityAPIRequest(
    int entityId, const char* message, unsigned short length)
{
    if (!foreground) return;
    cs.lock();
    length += 5;
    foreground->send((char*)&length, 2);
    char msgId = 3;
    foreground->send(&msgId, 1);
    foreground->send((char*)&entityId, 4);
    foreground->send(message, length - 5);
    cs.unlock();
}

void FactoryClient::blockAPIRequest(
    Vec3<int> pos, const char* message, unsigned short length)
{
    if (!foreground) return;
    cs.lock();
    length += 14;
    foreground->send((char*)&length, 2);
    char msgId = 1;
    foreground->send(&msgId, 1);
    foreground->send(&msgId, 1);
    foreground->send((char*)&pos.x, 4);
    foreground->send((char*)&pos.y, 4);
    foreground->send((char*)&pos.z, 4);
    foreground->send(message, length - 14);
    cs.unlock();
}

void FactoryClient::componentAPIRequest(
    int* address, int addrLen, const char* message, unsigned short length)
{
    if (!foreground) return;
    cs.lock();
    short sum = length + addrLen * 4 + (addrLen > 2 ? 3 : 2);
    foreground->send((char*)&sum, 2);
    if (addrLen > 2)
    {
        char msgId = 7; // dimension request
        foreground->send(&msgId, 1);
        foreground->send((char*)&address[0], 4); // dimension id
        msgId = 1;                               // block request
        foreground->send(&msgId, 1);
        foreground->send((char*)&address[1], 4); // position x
        foreground->send((char*)&address[2], 4); // position y
        foreground->send((char*)&address[3], 4); // position z
        msgId = 2;                               // component request
        foreground->send(&msgId, 1);
        foreground->send((char*)&address[4], 4); // component index
    }
    else
    {
        char msgId = 3; // entity request
        foreground->send(&msgId, 1);
        foreground->send((char*)&address[0], 4); // entity id
        msgId = 2;                               // component request
        foreground->send(&msgId, 1);
        foreground->send((char*)&address[1], 4); // component index
    }
    foreground->send(message, length);
    cs.unlock();
}

void FactoryClient::blockAPIRequest(
    int dimensionId, Vec3<int> pos, const char* message, unsigned short length)
{
    if (!foreground) return;
    cs.lock();
    length += 18;
    foreground->send((char*)&length, 2);
    char msgId = 7;
    foreground->send(&msgId, 1);
    foreground->send((char*)&dimensionId, 4);
    msgId = 1;
    foreground->send(&msgId, 1);
    foreground->send((char*)&pos.x, 4);
    foreground->send((char*)&pos.y, 4);
    foreground->send((char*)&pos.z, 4);
    foreground->send(message, length - 18);
    cs.unlock();
}

void FactoryClient::chunkAPIRequest(
    Point center, const char* message, unsigned short length)
{
    if (!foreground) return;
    length += 10;
    cs.lock();
    foreground->send((char*)&length, 2);
    char type = 1;
    foreground->send(&type, 1);
    type = 0;
    foreground->send(&type, 1);
    foreground->send((char*)&center.x, 4);
    foreground->send((char*)&center.y, 4);
    foreground->send(message, length - 10);
    cs.unlock();
}

void FactoryClient::dimensionAPIRequest(
    const char* message, unsigned short length)
{
    if (!foreground) return;
    length += 1;
    cs.lock();
    foreground->send((char*)&length, 2);
    char type = 1;
    foreground->send(&type, 1);
    foreground->send(message, length - 1);
    cs.unlock();
}

void FactoryClient::inventoryAPIRequest(
    Framework::Either<int, Framework::VecN<int, 4>> target,
    const char* message,
    unsigned short length)
{
    if (!foreground) return;
    cs.lock();
    length += target.isA() ? 6 : 18;
    foreground->send((char*)&length, 2);
    char msgId = 4;
    foreground->send(&msgId, 1);
    bool isEntity = target.isA();
    foreground->send((char*)&isEntity, 1);
    if (target.isA())
    {
        int id = target.getA();
        foreground->send((char*)&id, 4);
    }
    else
    {
        for (int i = 0; i < 4; i++)
        {
            int v = target.getB()[i];
            foreground->send((char*)&v, 4);
        }
    }
    foreground->send(message, length - (target.isA() ? 6 : 18));
    cs.unlock();
}

void FactoryClient::uiRequest(
    Framework::Text dialogId, const char* message, unsigned short length)
{
    if (!foreground) return;
    short nameLen = (short)dialogId.getLength();
    length += nameLen + 3;
    foreground->send((char*)&length, 2);
    cs.lock();
    char msgId = 8;
    foreground->send(&msgId, 1);
    foreground->send((char*)&nameLen, 2);
    foreground->send(dialogId, nameLen);
    foreground->send(message, length - nameLen - 3);
    cs.unlock();
}

void FactoryClient::craftingUIMLRequest(int itemTypeId)
{
    if (!foreground) return;
    cs.lock();
    short length = 5;
    foreground->send((char*)&length, 2);
    char msgId = 5;
    foreground->send(&msgId, 1);
    foreground->send((char*)&itemTypeId, 4);
    cs.unlock();
}

void FactoryClient::sendChatMessage(Framework::Text message)
{
    if (!background) return;
    cs.lock();
    short length = message.getLength() + 4;
    background->send((char*)&length, 2);
    char msgId = 6;
    background->send(&msgId, 1);
    msgId = 0;
    background->send(&msgId, 1);
    length = message.getLength();
    background->send((char*)&length, 2);
    background->send(message.getText(), message.getLength());
    cs.unlock();
}

void FactoryClient::chatAPIRequest(const char* data, unsigned short length)
{
    if (!background) return;
    cs.lock();
    short totalLength = length + 1;
    background->send((char*)&totalLength, 2);
    char msgId = 6;
    background->send(&msgId, 1);
    background->send(data, length);
    cs.unlock();
}

void FactoryClient::giveItemRequest(int itemTypeId, bool fullStack)
{
    if (!foreground) return;
    cs.lock();
    short length = 7;
    foreground->send((char*)&length, 2);
    char msgId = 2; // player message
    foreground->send(&msgId, 1);
    msgId = 11;
    foreground->send(&msgId, 1); // give items
    foreground->send((char*)&itemTypeId, 4);
    foreground->send((char*)&fullStack, 1);
    cs.unlock();
}

bool FactoryClient::isConnected()
{
    return foreground && background && foreground->isConnected()
        && background->isConnected();
}

void FactoryClient::leaveGame()
{
    cs.lock();
    disconnect();
    cs.unlock();
}