#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,
    float size,
    bool passable,
    float speedModifier)
    : FactoryCraftModel(),
      zType(zType),
      location(pos),
      maxHP((float)maxHP),
      needRequestModelInfo(needRequestModelInfo),
      partOfModel(0),
      flowOptions(0),
      distanceToSource(0),
      passable(passable),
      speedModifier(speedModifier)
{
    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);
    memset(lightData, 0, 6 * 6);
    setSize(size);
}

Block::~Block() {}

void Block::beforeRender(
    GraphicsApi* api, Shader* zVertexShader, Shader* zPixelShader)
{
    if (needRequestModelInfo)
    {
        needRequestModelInfo = 0;
        World::INSTANCE->zClient()->blockAPIRequest(location, "\0", 1);
    }
    FactoryCraftModel::beforeRender(api, zVertexShader, zPixelShader);
}

void Block::setFlow(char flowOptions, char distanceToSource)
{
    this->flowOptions = flowOptions;
    this->distanceToSource = distanceToSource;
}

void Block::api(char* message)
{
    switch (message[0])
    {
    case 0: // hp change
        {
            hp = *(float*)(message + 1);
            setDestroyedState(1 - hp / maxHP);
            Model3D* mdl = World::INSTANCE->getCurrentTarget();
            if (mdl == this)
            {
                if (partOfModel)
                {
                    World::INSTANCE->zSelectedEffectModel()->setDestroyedState(
                        1 - hp / maxHP);
                }
            }
            if (mdl) mdl->release();
            break;
        }
    case 1: // model change
        {
            ByteArrayReader reader(message + 1, 10000, 0);
            ModelInfo info(&reader);
            setModelDaten(info.getModel());
            setSize(info.getSize());
            setModelTextur(info.getTexture());
            Chunk* zChunk = World::INSTANCE->zChunk(
                World::INSTANCE->getChunkCenter(location.x, location.y));
            if (zChunk)
            {
                zChunk->setModelChanged(partOfModel);
            }
            break;
        }
    case 2: // update fluid state
        {
            flowOptions = message[1];
            distanceToSource = message[2];
            Chunk* zChunk = World::INSTANCE->zChunk(
                World::INSTANCE->getChunkCenter(location.x, location.y));
            if (zChunk)
            {
                zChunk->setModelChanged(partOfModel);
            }
        }
    case 3: // update passable and speed modifier
        {
            passable = message[1];
            speedModifier = *(float*)(message + 2);
            break;
        }
    }
}

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

void Block::setLightData(Direction dir, unsigned char* data, Chunk* zC)
{
    memcpy(lightData + getDirectionIndex(dir) * 6, data, 6);
    if (data[0] | data[1] | data[2] | data[3] | data[4] | data[5])
        sideVisible[getDirectionIndex(dir)] = 1;
    else
        sideVisible[getDirectionIndex(dir)] = 0;
    if (zC) zC->setLightChanged(partOfModel);
}

void Block::setPartOfModel(int type, bool part)
{
    if (part)
        this->partOfModel |= type;
    else
        this->partOfModel &= ~type;
}

const unsigned char* Block::getLightData(Direction dir) const
{
    return lightData + getDirectionIndex(dir) * 6;
}

__int64 Block::getMaxLight() const
{
    unsigned char max[6];
    max[0] = max[1] = max[2] = max[3] = max[4] = max[5] = 0;
    for (int i = 0; i < 6; i++)
    {
        for (int j = 0; j < 6; j++)
        {
            if (lightData[i * 6 + j] > max[j]) max[j] = lightData[i * 6 + j];
        }
    }
    return ((__int64)max[0] << 24) | ((__int64)max[1] << 16)
         | ((__int64)max[2] << 8) | ((__int64)max[3] << 56)
         | ((__int64)max[4] << 48) | ((__int64)max[5] << 40);
}

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;
}

Skeleton* Block::zSkeleton() const
{
    return skelett;
}

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;
}

int Block::getPartOfModels() const
{
    return partOfModel;
}

char Block::getFlowOptions() const
{
    return flowOptions;
}

char Block::getDistanceToSource() const
{
    return distanceToSource;
}