#include "Block.h"

#include <Shader.h>
#include <Textur.h>

#include "CustomDX11API.h"
#include "Globals.h"

Block::Block(const BlockType* zType,
    Framework::Vec3<int> pos,
    Model3DData* model,
    Model3DTextur* texture,
    int maxHP,
    bool transparent,
    bool needRequestModelInfo)
    : Model3D(),
      zType(zType),
      location(pos),
      maxHP((float)maxHP),
      needRequestModelInfo(needRequestModelInfo)
{
    hp = (float)maxHP;
    memset(sideVisible, 0, 6);
    Model3D::setAlpha(transparent);
    Model3D::setPosition(
        (Framework::Vec3<float>)pos + Framework::Vec3<float>{0.5f, 0.5f, 0.5f});
    setModelDaten(model);
    setModelTextur(texture);
    breakTextur
        = uiFactory.initParam.bildschirm->zGraphicsApi()->createOrGetTextur(
            "blocks.ltdb/crack.png", 0);
    memset(lightData, 0, 6 * 6);
}

Block::~Block()
{
    breakTextur->release();
}

void Block::beforeRender(
    GraphicsApi* api, Shader* zVertexShader, Shader* zPixelShader)
{
    if (needRequestModelInfo)
    {
        needRequestModelInfo = 0;
        World::INSTANCE->zClient()->blockAPIRequest(location, "\0", 1);
    }
    CustomDX11API* cApi = dynamic_cast<CustomDX11API*>(api);
    if (cApi && model->getVertexAnzahl() == 24)
    {
        int count = 24;
        zVertexShader->füllConstBuffer(
            (char*)&count, cApi->getVertexShaderLightBufferIndex(), 4);
        zVertexShader->füllConstBuffer(
            lightBuffer, cApi->getVertexShaderLightBufferIndex() + 1, 24 * 4);
    }
    else if (cApi)
    {
        int count = 1;
        zVertexShader->füllConstBuffer(
            (char*)&count, cApi->getVertexShaderLightBufferIndex(), 4);
        zVertexShader->füllConstBuffer(
            lightBuffer, cApi->getVertexShaderLightBufferIndex() + 1, 4);
    }
}

void Block::afterRender(
    GraphicsApi* api, Shader* zVertexShader, Shader* zPixelShader)
{
    CustomDX11API* cApi = dynamic_cast<CustomDX11API*>(api);
    if (cApi)
    {
        int count = 0;
        zVertexShader->füllConstBuffer(
            (char*)&count, cApi->getVertexShaderLightBufferIndex(), 4);
    }
}

void Block::api(char* message)
{
    // TODO: implement api
    switch (message[0])
    {
    case 0: // hp change
        hp = *(float*)(message + 1);
        break;
    case 1: // model change
        {
            ByteArrayReader reader(message + 1, 10000, 0);
            ModelInfo info(&reader);
            setModelDaten(info.getModel());
            setModelTextur(info.getTexture());
            break;
        }
    case 2: // update fluid fill state
        {
            short fluidAmount = *(short*)(message + 1);
            if (this->skelett)
            {
                float z = (float)fluidAmount / 1000.f;
                Knochen* k = this->skelett->zKnochen()->zKind();
                k->setPosition(Vec3<float>(0.f, 0.f, z - 1));
                k = k->zGeschwister();
                k->setPosition(Vec3<float>(0.f, 0.f, z - 1));
                k = k->zGeschwister();
                k->setPosition(Vec3<float>(0.f, 0.f, z - 1));
                k = k->zGeschwister();
                k->setPosition(Vec3<float>(0.f, 0.f, z - 1));
            }
        }
    }
}

void Block::copyLightTo(Block* zB)
{
    memcpy(zB->lightBuffer, lightBuffer, 24 * 4);
    memcpy(zB->lightData, lightData, 6 * 6);
    memcpy(zB->sideVisible, sideVisible, 6);
}

void Block::setLightData(Direction dir, unsigned char* data)
{
    memcpy(lightData + getDirectionIndex(dir) * 6, data, 6);
    float dayLightFactor = 1.0; // TODO: day (1.f) - night (0.1f) adjustment
    if (model->getVertexAnzahl() == 24)
    {
        const Vertex3D* verticies = model->zVertexBuffer();
        for (int i = 0; i < 24; i++)
        { // TODO: calculate rotations
            Directions dirs = getDirectionsFromVector(verticies[i].pos);
            Vec3<unsigned char> light(0, 0, 0);
            for (int d = 0; d < 6; d++)
            {
                if ((dirs | getDirectionFromIndex(d)) == dirs)
                {
                    light = Vec3<unsigned char>(
                        max((unsigned char)((float)lightData[d * 6]
                                            * dayLightFactor),
                            light.x),
                        max((unsigned char)((float)lightData[d * 6 + 1]
                                            * dayLightFactor),
                            light.y),
                        max((unsigned char)((float)lightData[d * 6 + 2]
                                            * dayLightFactor),
                            light.z));
                    light = Vec3<float>(max(lightData[d * 6 + 3], light.x),
                        max(lightData[d * 6 + 4], light.y),
                        max(lightData[d * 6 + 5], light.z));
                }
            }
            int l = ((((light.x << 8) | light.y) << 8) | light.z) << 8;
            *(int*)(lightBuffer + i * 4) = l;
        }
    }
    else if (dir == Direction::TOP)
    {
        Vec3<unsigned char> light = Vec3<unsigned char>(
            max((unsigned char)((float)data[0] * dayLightFactor), data[3]),
            max((unsigned char)((float)data[1] * dayLightFactor), data[4]),
            max((unsigned char)((float)data[2] * dayLightFactor), data[5]));
        int l = ((((light.x << 8) | light.y) << 8) | light.z) << 8;
        *(int*)lightBuffer = l;
    }
    if (data[0] | data[1] | data[2] | data[3] | data[4] | data[5])
        sideVisible[getDirectionIndex(dir)] = 1;
    else
        sideVisible[getDirectionIndex(dir)] = 0;
}

bool Block::isVisible() const
{
    return true;
    //return sideVisible[0] || sideVisible[1] || sideVisible[2] || sideVisible[3]
    //    || sideVisible[4] || sideVisible[5];
}

Vec3<int> Block::getLocation() const
{
    return location;
}

const BlockType* Block::zBlockType() const
{
    return zType;
}

Textur* Block::zEffectTextur()
{
    if (hp < maxHP) return breakTextur;
    return 0;
}

float Block::getEffectPercentage()
{
    return 1 - hp / maxHP;
}

Text Block::printLightInfo()
{
    Text result = "NORTH[0;-1;0](";
    result += (int)lightData[0];
    result += ",";
    result += (int)lightData[1];
    result += ",";
    result += (int)lightData[2];
    result += ";";
    result += (int)lightData[3];
    result += ",";
    result += (int)lightData[4];
    result += ",";
    result += (int)lightData[5];
    result += ")\n";
    result += "EAST[1;0;0](";
    result += (int)lightData[6];
    result += ",";
    result += (int)lightData[7];
    result += ",";
    result += (int)lightData[8];
    result += ";";
    result += (int)lightData[9];
    result += ",";
    result += (int)lightData[10];
    result += ",";
    result += (int)lightData[11];
    result += ")\n";
    result += "SOUTH[0;1;0](";
    result += (int)lightData[12];
    result += ",";
    result += (int)lightData[13];
    result += ",";
    result += (int)lightData[14];
    result += ";";
    result += (int)lightData[15];
    result += ",";
    result += (int)lightData[16];
    result += ",";
    result += (int)lightData[17];
    result += ")\n";
    result += "WEST[-1;0;0](";
    result += (int)lightData[18];
    result += ",";
    result += (int)lightData[19];
    result += ",";
    result += (int)lightData[20];
    result += ";";
    result += (int)lightData[21];
    result += ",";
    result += (int)lightData[22];
    result += ",";
    result += (int)lightData[23];
    result += ")\n";
    result += "TOP[0;0;1](";
    result += (int)lightData[24];
    result += ",";
    result += (int)lightData[25];
    result += ",";
    result += (int)lightData[26];
    result += ";";
    result += (int)lightData[27];
    result += ",";
    result += (int)lightData[28];
    result += ",";
    result += (int)lightData[29];
    result += ")\n";
    result += "BOTTOM[0;0;-1](";
    result += (int)lightData[30];
    result += ",";
    result += (int)lightData[31];
    result += ",";
    result += (int)lightData[32];
    result += ";";
    result += (int)lightData[33];
    result += ",";
    result += (int)lightData[34];
    result += ",";
    result += (int)lightData[35];
    result += ")\n";
    return result;
}