#include "render/loader/WorldLoader.h"

#include <iostream>
#include <string.h>

#include "game/BaseGame.h"

namespace td {
namespace render {

namespace WorldLoader {

GL::VertexArray loadMobModel() {
    std::vector<float> positions = {
        -0.5, -0.5,
        0.5, -0.5,
        -0.5, 0.5,

        0.5, -0.5,
        -0.5, 0.5,
        0.5, 0.5
    };

    float yellowFloat;
    int yellow = 255 << 24 | 255 << 16 | 255;
    memcpy(&yellowFloat, &yellow, sizeof(int));

    std::vector<float> colors = {
        yellowFloat,
        yellowFloat,
        yellowFloat,

        yellowFloat,
        yellowFloat,
        yellowFloat
    };

    GL::VertexBuffer positionVBO(positions, 2);
    positionVBO.addVertexAttribPointer(0, 2, 0);
    GL::VertexBuffer colorVBO(colors, 1);
    colorVBO.addVertexAttribPointer(1, 1, 0);

    GL::VertexArray mobVao(colors.size()); // each pos = 1 color
    mobVao.bind();
    mobVao.bindVertexBuffer(positionVBO);
    mobVao.bindVertexBuffer(colorVBO);
    mobVao.unbind();
    return mobVao;
}

GL::VertexArray loadWorldModel(const td::game::World* world) {
    std::vector<float> positions;
    std::vector<float> colors;

    for (const auto& chunkInfo : world->getChunks()) {
        const td::game::ChunkCoord& coords = chunkInfo.first;
        td::game::ChunkPtr chunk = chunkInfo.second;

        std::int32_t chunkX = coords.first * td::game::Chunk::ChunkWidth;
        std::int32_t chunkY = coords.second * td::game::Chunk::ChunkHeight;

        for (int tileY = 0; tileY < td::game::Chunk::ChunkHeight; tileY++) {
            for (int tileX = 0; tileX < td::game::Chunk::ChunkWidth; tileX++) {
                int tileNumber = tileY * td::game::Chunk::ChunkWidth + tileX;
                td::game::TileIndex tileIndex = chunk->getTileIndex(tileNumber);
                td::game::TilePtr tile = world->getTilePtr(tileIndex);

                if (tile == nullptr)
                    continue;

                positions.insert(positions.end(), {
                    (float)chunkX + tileX, (float)chunkY + tileY,
                    (float)chunkX + tileX + 1, (float)chunkY + tileY,
                    (float)chunkX + tileX, (float)chunkY + tileY + 1,

                    (float)chunkX + tileX + 1, (float)chunkY + tileY,
                    (float)chunkX + tileX, (float)chunkY + tileY + 1,
                    (float)chunkX + tileX + 1, (float)chunkY + tileY + 1,
                    });

                const td::game::Color* tileColor = world->getTileColor(tile);

                for (int i = 0; i < 6; i++) {
                    int color = 255;
                    color |= tileColor->r << 24;
                    color |= tileColor->g << 16;
                    color |= tileColor->b << 8;

                    int newColorIndex = colors.size();
                    colors.push_back(0);

                    memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
                }
            }
        }
    }

    for (int spawnColor = 0; spawnColor < 2; spawnColor++) {
        const game::Spawn& spawn = world->getTeam(game::TeamColor(spawnColor)).getSpawn();
        float fromX = spawn.getTopLeft().getX() , toX = spawn.getBottomRight().getX();
        float fromY = spawn.getTopLeft().getY() , toY = spawn.getBottomRight().getY();

        positions.insert(positions.end(), {
            fromX, fromY,
            fromX, toY,
            toX, fromY,

            toX, toY,
            fromX, toY,
            toX, fromY,
            });

        for (int i = 0; i < 6; i++) {
            int color = 255;
            color |= world->getSpawnColor(game::TeamColor(spawnColor)).r << 24;
            color |= world->getSpawnColor(game::TeamColor(spawnColor)).g << 16;
            color |= world->getSpawnColor(game::TeamColor(spawnColor)).b << 8;

            int newColorIndex = colors.size();
            colors.push_back(0);

            memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
        }
    }

    for (int castleColor = 0; castleColor < 2; castleColor++) {
        const game::TeamCastle& castle = world->getTeam(game::TeamColor(castleColor)).getCastle();
        float fromX = castle.getTopLeft().getX(), toX = castle.getBottomRight().getX();
        float fromY = castle.getTopLeft().getY(), toY = castle.getBottomRight().getY();

        positions.insert(positions.end(), {
            fromX, fromY,
            fromX, toY,
            toX, fromY,

            toX, toY,
            fromX, toY,
            toX, fromY,
            });

        for (int i = 0; i < 6; i++) {
            int color = 255;
            color |= world->getSpawnColor(game::TeamColor(castleColor)).r << 24;
            color |= world->getSpawnColor(game::TeamColor(castleColor)).g << 16;
            color |= world->getSpawnColor(game::TeamColor(castleColor)).b << 8;

            int newColorIndex = colors.size();
            colors.push_back(0);

            memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
        }
    }

    GL::VertexBuffer positionVBO(positions, 2);
    positionVBO.addVertexAttribPointer(0, 2, 0);
    GL::VertexBuffer colorVBO(colors, 1);
    colorVBO.addVertexAttribPointer(1, 1, 0);

    GL::VertexArray worldVao(positions.size() / 2); // each pos = 2 vertecies
    worldVao.bind();
    worldVao.bindVertexBuffer(positionVBO);
    worldVao.bindVertexBuffer(colorVBO);
    worldVao.unbind();
    return worldVao;
}

GL::VertexArray loadTileSelectModel() {
    std::vector<float> positions = {
        0, 0,
        1, 0,
        0, 1,

        1, 0,
        0, 1,
        1, 1
    };

    int color = 255 << 24 | 255 << 16 | 255 << 8 | 150;
    float colorFloat;

    memcpy((std::uint8_t*)&colorFloat, &color, sizeof(float));

    std::vector<float> colors(6, colorFloat);

    GL::VertexBuffer positionVBO(positions, 2);
    positionVBO.addVertexAttribPointer(0, 2, 0);
    GL::VertexBuffer colorVBO(colors, 1);
    colorVBO.addVertexAttribPointer(1, 1, 0);

    GL::VertexArray tileSelectVao(positions.size() / 2); // each pos = 2 vertecies
    tileSelectVao.bind();
    tileSelectVao.bindVertexBuffer(positionVBO);
    tileSelectVao.bindVertexBuffer(colorVBO);
    tileSelectVao.unbind();

    return tileSelectVao;
}

RenderData loadTowerModel(game::TowerPtr tower) {
    RenderData renderData;

    float towerX, towerDX;
    float towerY, towerDY;

    if (tower->getSize() == game::TowerSize::Little) {
        towerX = tower->getCenterX() - 1.5f;
        towerDX = tower->getCenterX() + 1.5f;

        towerY = tower->getCenterY() - 1.5f;
        towerDY = tower->getCenterY() + 1.5f;
    } else {
        towerX = tower->getCenterX() - 2.5f;
        towerDX = tower->getCenterX() + 2.5f;

        towerY = tower->getCenterY() - 2.5f;
        towerDY = tower->getCenterY() + 2.5f;
    }
    std::vector<float> positions = {
        towerX, towerY,
        towerDX, towerY,
        towerX, towerDY,

        towerDX, towerY,
        towerX, towerDY,
        towerDX, towerDY
    };

    renderData.positions = positions;

    std::uint8_t towerType = static_cast<std::uint8_t>(tower->getType());
    std::uint8_t r = 10 * towerType + 40, g = 5 * towerType + 30, b = 10 * towerType + 20;

    float colorFloat;
    int color = r << 24 | g << 16 | b << 8 | 255;
    memcpy(&colorFloat, &color, sizeof(int));

    std::vector<float> colors(6, colorFloat);
    renderData.colors = colors;

    return renderData;
}


} // namespace WorldLoader


} // namespace render
} // namespace td
