#include "Load.h"

#include <Array.h>
#include <AsynchronCall.h>
#include <Datei.h>
#include <DateiSystem.h>
#include <GraphicsApi.h>
#include <M3Datei.h>
#include <Text.h>
#include <Textur.h>

#include "Globals.h"
#include "Initialisierung.h"
#include "ServerSelection.h"

void createDefaultCube(Bildschirm* 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.
    left = (float)((-size / 2.0));
    // Calculate the screen coordinates of the right side of the bitmap.
    right = left + (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();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0;
    p->indexList[1] = 2;
    p->indexList[2] = 1;
    p->indexList[3] = 1;
    p->indexList[4] = 2;
    p->indexList[5] = 3;
    data->addPolygon(p);
    // left side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 8;
    p->indexList[1] = 2 + 8;
    p->indexList[2] = 1 + 8;
    p->indexList[3] = 1 + 8;
    p->indexList[4] = 2 + 8;
    p->indexList[5] = 3 + 8;
    data->addPolygon(p);
    // back side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 4;
    p->indexList[1] = 2 + 4;
    p->indexList[2] = 1 + 4;
    p->indexList[3] = 1 + 4;
    p->indexList[4] = 2 + 4;
    p->indexList[5] = 3 + 4;
    data->addPolygon(p);
    // right side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 12;
    p->indexList[1] = 2 + 12;
    p->indexList[2] = 1 + 12;
    p->indexList[3] = 1 + 12;
    p->indexList[4] = 2 + 12;
    p->indexList[5] = 3 + 12;
    data->addPolygon(p);
    // top side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 16;
    p->indexList[1] = 2 + 16;
    p->indexList[2] = 1 + 16;
    p->indexList[3] = 1 + 16;
    p->indexList[4] = 2 + 16;
    p->indexList[5] = 3 + 16;
    data->addPolygon(p);
    // botom side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 20;
    p->indexList[1] = 2 + 20;
    p->indexList[2] = 1 + 20;
    p->indexList[3] = 1 + 20;
    p->indexList[4] = 2 + 20;
    p->indexList[5] = 3 + 20;
    data->addPolygon(p);
    data->calculateNormals();
    data->release();
}

void createCubeItem(Bildschirm* zScreen)
{
    Framework::Model3DData* data
        = window->zBildschirm()->zGraphicsApi()->createModel("itemCube");
    data->setAmbientFactor(0.8f);
    data->setDiffusFactor(0.1f);
    data->setSpecularFactor(0.1f);
    float size = 0.2f;
    float left, right, top, bottom;
    // Calculate the screen coordinates of the left side of the bitmap.
    left = (float)((-size / 2.0));
    // Calculate the screen coordinates of the right side of the bitmap.
    right = left + (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();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0;
    p->indexList[1] = 2;
    p->indexList[2] = 1;
    p->indexList[3] = 1;
    p->indexList[4] = 2;
    p->indexList[5] = 3;
    data->addPolygon(p);
    // left side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 8;
    p->indexList[1] = 2 + 8;
    p->indexList[2] = 1 + 8;
    p->indexList[3] = 1 + 8;
    p->indexList[4] = 2 + 8;
    p->indexList[5] = 3 + 8;
    data->addPolygon(p);
    // back side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 4;
    p->indexList[1] = 2 + 4;
    p->indexList[2] = 1 + 4;
    p->indexList[3] = 1 + 4;
    p->indexList[4] = 2 + 4;
    p->indexList[5] = 3 + 4;
    data->addPolygon(p);
    // right side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 12;
    p->indexList[1] = 2 + 12;
    p->indexList[2] = 1 + 12;
    p->indexList[3] = 1 + 12;
    p->indexList[4] = 2 + 12;
    p->indexList[5] = 3 + 12;
    data->addPolygon(p);
    // top side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 16;
    p->indexList[1] = 2 + 16;
    p->indexList[2] = 1 + 16;
    p->indexList[3] = 1 + 16;
    p->indexList[4] = 2 + 16;
    p->indexList[5] = 3 + 16;
    data->addPolygon(p);
    // botom side
    p = new Polygon3D();
    p->indexAnz = 6;
    p->indexList = new int[p->indexAnz];
    p->indexList[0] = 0 + 20;
    p->indexList[1] = 2 + 20;
    p->indexList[2] = 1 + 20;
    p->indexList[3] = 1 + 20;
    p->indexList[4] = 2 + 20;
    p->indexList[5] = 3 + 20;
    data->addPolygon(p);
    data->calculateNormals();
    data->release();
}

void createPlayer(Bildschirm* zScreen)
{
    Framework::Model3DData* data
        = window->zBildschirm()->zGraphicsApi()->createModel("player");
    data->setAmbientFactor(0.8f);
    data->setDiffusFactor(0.1f);
    data->setSpecularFactor(0.1f);
    float size = 0.8f;
    float left, right, top, bottom;
    // Calculate the screen coordinates of the left side of the bitmap.
    left = (float)((size / 2.0) * -1);
    // Calculate the screen coordinates of the right side of the bitmap.
    right = left + (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 = -1.5f / 2;
    float back = front + 1.5f;

    Vertex3D* vertecies = new Vertex3D[24];
    for (int i = 0; i < 24; i++)
        vertecies[i].knochenId = 0;
    vertecies[0].pos = Vec3<float>(left, top, front);
    vertecies[0].tPos = Vec2<float>(0.f, 0.f);
    vertecies[1].pos = Vec3<float>(right, top, front);
    vertecies[1].tPos = Vec2<float>(1.f, 0.f);
    vertecies[2].pos = Vec3<float>(left, bottom, front);
    vertecies[2].tPos = Vec2<float>(0.f, 1.f);
    vertecies[3].pos = Vec3<float>(right, bottom, front);
    vertecies[3].tPos = Vec2<float>(1.f, 1.f);
    vertecies[4].pos = Vec3<float>(left, top, back);
    vertecies[4].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[5].pos = Vec3<float>(right, top, back);
    vertecies[5].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[6].pos = Vec3<float>(left, bottom, back);
    vertecies[6].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[7].pos = Vec3<float>(right, bottom, back);
    vertecies[7].tPos = Vec2<float>(1.0f, 1.0f);

    vertecies[8].pos = Vec3<float>(left, top, front);
    vertecies[8].tPos = Vec2<float>(1.f, 0.f);
    vertecies[9].pos = Vec3<float>(right, top, front);
    vertecies[9].tPos = Vec2<float>(0.f, 0.f);
    vertecies[10].pos = Vec3<float>(left, bottom, front);
    vertecies[10].tPos = Vec2<float>(1.f, 1.f);
    vertecies[11].pos = Vec3<float>(right, bottom, front);
    vertecies[11].tPos = Vec2<float>(0.f, 1.f);
    vertecies[12].pos = Vec3<float>(left, top, back);
    vertecies[12].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[13].pos = Vec3<float>(right, top, back);
    vertecies[13].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[14].pos = Vec3<float>(left, bottom, back);
    vertecies[14].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[15].pos = Vec3<float>(right, bottom, back);
    vertecies[15].tPos = Vec2<float>(1.0f, 1.0f);

    vertecies[16].pos = Vec3<float>(left, top, front);
    vertecies[16].tPos = Vec2<float>(0.f, 1.f);
    vertecies[17].pos = Vec3<float>(right, top, front);
    vertecies[17].tPos = Vec2<float>(1.f, 1.f);
    vertecies[18].pos = Vec3<float>(left, bottom, front);
    vertecies[18].tPos = Vec2<float>(0.f, 0.f);
    vertecies[19].pos = Vec3<float>(right, bottom, front);
    vertecies[19].tPos = Vec2<float>(1.f, 0.f);
    vertecies[20].pos = Vec3<float>(left, top, back);
    vertecies[20].tPos = Vec2<float>(0.0f, 0.0f);
    vertecies[21].pos = Vec3<float>(right, top, back);
    vertecies[21].tPos = Vec2<float>(1.0f, 0.0f);
    vertecies[22].pos = Vec3<float>(left, bottom, back);
    vertecies[22].tPos = Vec2<float>(0.0f, 1.0f);
    vertecies[23].pos = Vec3<float>(right, bottom, back);
    vertecies[23].tPos = Vec2<float>(1.0f, 1.0f);

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

void createModels(Bildschirm* zScreen)
{
    createDefaultCube(zScreen);
    createCubeItem(zScreen);
    createPlayer(zScreen);
}

LoadMenu::LoadMenu(Bildschirm* zScreen)
    : Menu(zScreen)
{
    Punkt center = zScreen->getBackBufferSize() / 2;
    step = initFBalken(
        center.x - 100, center.y + 25, 200, 30, FBalken::Style::normal);
    stage = initFBalken(
        center.x - 100, center.y - 15, 200, 30, FBalken::Style::normal);
    all = initFBalken(
        center.x - 100, center.y - 55, 200, 30, FBalken::Style::normal);
    elements.add(step);
    elements.add(stage);
    elements.add(all);

    new AsynchronCall("Load Menu", [this, zScreen]() {
        Sleep(1000);
        all->setAktionAnzahl(2);
        all->reset();
        // loading textures
        Datei texturF;
        texturF.setDatei("data/textures");
        RCArray<Text>* files = texturF.getDateiListe();
        if (files)
        {
            int count = 0;
            for (Text* fileName : *files)
            {
                LTDBDatei dat;
                dat.setDatei(new Text(Text("data/textures/") + *fileName));
                dat.leseDaten(0);
                count += dat.getBildAnzahl();
            }
            stage->setAktionAnzahl(count);
            stage->reset();
            for (Text* fileName : *files)
            {
                LTDBDatei dat;
                dat.setDatei(new Text(Text("data/textures/") + *fileName));
                dat.leseDaten(0);
                for (Text* name : *dat.zBildListe())
                {
                    step->reset();
                    Bild* b = dat.laden(step, new Text(*name));
                    zScreen->zGraphicsApi()
                        ->createOrGetTextur(*fileName + "/" + *name, b)
                        ->release();
                    stage->aktionPlus();
                }
            }
            files->release();
        }
        all->aktionPlus();
        // loading models
        stage->setAktionAnzahl(1);
        Datei modelF;
        modelF.setDatei("data/models");
        files = modelF.getDateiListe();
        if (files)
        {
            int count = 0;
            for (Text* fileName : *files)
            {
                M3Datei dat(Text("data/models/") + *fileName);
                dat.leseDaten();
                count += dat.getModelAnzahl();
            }
            stage->setAktionAnzahl(count + 1);
            stage->reset();
            for (Text* fileName : *files)
            {
                M3Datei dat(Text("data/models/") + *fileName);
                dat.leseDaten();
                for (int i = 0; i < dat.getModelAnzahl(); i++)
                {
                    step->reset();
                    Model3DData* d = dat.ladeModel(dat.zModelName(i)->getText(),
                        zScreen->zGraphicsApi(),
                        *fileName + "/" + *dat.zModelName(i));
                    d->release();
                    stage->aktionPlus();
                }
            }
            files->release();
        }
        createModels(zScreen);
        stage->aktionPlus();
        all->aktionPlus();
        stage->reset();
        zScreen->lock();
        hide();
        menuRegister->get("serverSelection")->show();
        zScreen->unlock();
    });
}