#include "GeneratedStructure.h"

#include "GenerationTemplate.h"
#include "MultiblockStructure.h"
#include "NoBlock.h"

using namespace Framework;

GeneratedStructure::GeneratedStructure(GenerationTemplate* t,
    Framework::Vec3<int> originPos,
    Framework::Vec3<int> size,
    Framework::Vec3<int> minAffectedPos)
    : ReferenceCounter(),
      size(size),
      minAffectedPos(minAffectedPos),
      originPos(originPos),
      t(t),
      multiblock(0)
{
    blockIds = new int[size.x * size.y * size.z];
    blocks = new Block*[size.x * size.y * size.z];
    memset(blockIds, 0, sizeof(int) * size.x * size.y * size.z);
    memset(blocks, 0, sizeof(Block*) * size.x * size.y * size.z);
}

GeneratedStructure::~GeneratedStructure()
{
    for (int i = 0; i < size.x * size.y * size.z; i++)
    {
        if (blocks[i]) blocks[i]->release();
    }
    delete[] blockIds;
    delete[] blocks;
    t->release();
    if (multiblock) multiblock->release();
}

void GeneratedStructure::setBlockAt(
    Framework::Either<Block*, int> block, Framework::Vec3<int> localPos)
{
    assert(localPos.x >= 0 && localPos.y >= 0 && localPos.z >= 0
           && localPos.x < size.x && localPos.y < size.y
           && localPos.z < size.z);
    int index = ((localPos.x * size.y) + localPos.y) * size.z + localPos.z;
    if (block.isA())
        blocks[index] = block;
    else
        blockIds[index] = block;
}

bool GeneratedStructure::isBlockAffected(Framework::Vec3<int> location) const
{
    Framework::Vec3<int> localPos = location - minAffectedPos;
    if (localPos.x >= 0 && localPos.y >= 0 && localPos.z >= 0
        && localPos.x < size.x && localPos.y < size.y && localPos.z < size.z)
    {
        int index = ((localPos.x * size.y) + localPos.y) * size.z + localPos.z;
        return blocks[index] || blockIds[index];
    }
    return 0;
}

Framework::Either<Block*, int> GeneratedStructure::generateBlockAt(
    Framework::Vec3<int> location) const
{
    Framework::Vec3<int> localPos = location - minAffectedPos;
    if (localPos.x >= 0 && localPos.y >= 0 && localPos.z >= 0
        && localPos.x < size.x && localPos.y < size.y && localPos.z < size.z)
    {
        int index = ((localPos.x * size.y) + localPos.y) * size.z + localPos.z;
        if (multiblock)
        {
            multiblock->addMemberPosition(minAffectedPos + localPos);
            if (blocks[index])
            {
                blocks[index]->addToStructure(
                    dynamic_cast<MultiblockStructure*>(multiblock->getThis()));
                return blocks[index];
            }
            if (blockIds[index] && blockIds[index] != BlockTypeEnum::AIR)
            {
                Block* result
                    = StaticRegistry<BlockType>::INSTANCE
                          .zElement(blockIds[index])
                          ->createBlockAt(minAffectedPos + localPos, 0);
                if (result)
                {
                    result->addToStructure(dynamic_cast<MultiblockStructure*>(
                        multiblock->getThis()));
                    return result;
                }
            }
            return blockIds[index];
        }
        else
        {
            if (blocks[index]) return blocks[index];
            return blockIds[index];
        }
    }
    return 0;
}

void GeneratedStructure::addAllBlocksToStructure(MultiblockStructure* structure)
{
    if (multiblock) multiblock->release();
    multiblock = structure;
}

Framework::Vec3<int> GeneratedStructure::getOriginPos() const
{
    return originPos;
}

GenerationTemplate* GeneratedStructure::zTemplate() const
{
    return t;
}