#include "BlockType.h"

#include "BasicBlocks.h"
#include "Block.h"
#include "Game.h"
#include "ItemType.h"
#include "MultiblockStructure.h"

using namespace Framework;

BlockType::BlockType(int id,
    Block* defaultBlock,
    ModelInfo model,
    bool needsClientInstance,
    int initialMaxHP,
    bool lightSource,
    const char* name,
    bool needModelSubscription,
    int initialMapColor)
    : ReferenceCounter(),
      id(id),
      model(model),
      initialMaxHP(initialMaxHP),
      needsClientInstance(needsClientInstance),
      lightSource(lightSource),
      name(name),
      needModelSubscription(needModelSubscription),
      initialMapColor(initialMapColor),
      defaultBlock(defaultBlock)
{
    StaticRegistry<BlockType>::INSTANCE.registerT(this, id);
}

BlockType::~BlockType()
{
    if (defaultBlock) defaultBlock->release();
}

void BlockType::loadSuperBlock(
    Block* zBlock, Framework::StreamReader* zReader, int dimensionId) const
{
    zBlock->loadInventory(zReader);
    zReader->lese((char*)&zBlock->transparent, 1);
    zReader->lese((char*)&zBlock->passable, 1);
    zReader->lese((char*)&zBlock->hp, 4);
    zReader->lese((char*)&zBlock->maxHP, 4);
    zReader->lese((char*)&zBlock->hardness, 4);
    zReader->lese((char*)&zBlock->speedModifier, 4);
    zReader->lese((char*)&zBlock->mapColor, 4);
    int effectiveToolId;
    zReader->lese((char*)&effectiveToolId, 4);
    if (effectiveToolId >= 0)
        zBlock->zTool
            = StaticRegistry<ItemType>::INSTANCE.zElement(effectiveToolId);
    else
        zBlock->zTool = 0;
    zReader->lese((char*)&zBlock->interactable, 1);
    int strCount;
    zReader->lese((char*)&strCount, 4);
    for (int i = 0; i < strCount; i++)
    {
        __int64 id;
        zReader->lese((char*)&id, 8);
        MultiblockStructure* str
            = Game::INSTANCE->zDimension(dimensionId)->zStructureById(id);
        if (str)
        {
            zBlock->structures.add(
                dynamic_cast<MultiblockStructure*>(str->getThis()));
        }
    }
}

void BlockType::saveSuperBlock(
    Block* zBlock, Framework::StreamWriter* zWriter) const
{
    zBlock->saveInventory(zWriter);
    zWriter->schreibe((char*)&zBlock->transparent, 1);
    zWriter->schreibe((char*)&zBlock->passable, 1);
    zWriter->schreibe((char*)&zBlock->hp, 4);
    zWriter->schreibe((char*)&zBlock->maxHP, 4);
    zWriter->schreibe((char*)&zBlock->hardness, 4);
    zWriter->schreibe((char*)&zBlock->speedModifier, 4);
    zWriter->schreibe((char*)&zBlock->mapColor, 4);
    int effectiveToolId = zBlock->zTool ? zBlock->zTool->getId() : -1;
    zWriter->schreibe((char*)&effectiveToolId, 4);
    zWriter->schreibe((char*)&zBlock->interactable, 1);
    int strCount = zBlock->structures.getEintragAnzahl();
    zWriter->schreibe((char*)&strCount, 4);
    for (MultiblockStructure* structure : zBlock->structures)
    {
        __int64 id = structure->getStructureId();
        zWriter->schreibe((char*)&id, 8);
    }
}

void BlockType::createSuperBlock(Block* zBlock, Item* zItem) const
{
    if (zItem)
    {
        BasicBlockItem* item = dynamic_cast<BasicBlockItem*>(zItem);
        if (!item)
        {
            throw "BlockType::createSuperBlock was called with an item witch "
                  "was not an instance of BasicBlockItem";
        }
        zBlock->transparent = item->transparent;
        zBlock->passable = item->passable;
        zBlock->hardness = item->hardness;
        zBlock->speedModifier = item->speedModifier;
        zBlock->zTool
            = item->toolId >= 0
                ? StaticRegistry<ItemType>::INSTANCE.zElement(item->toolId)
                : 0;
        zBlock->interactable = item->interactable;
    }
    zBlock->mapColor = initialMapColor;
}

void BlockType::createSuperItem(Block* zBlock, Item* zItem) const
{
    BasicBlockItem* item = dynamic_cast<BasicBlockItem*>(zItem);
    if (item)
    {
        item->transparent = zBlock->transparent;
        item->passable = zBlock->passable;
        item->hardness = zBlock->hardness;
        item->speedModifier = zBlock->speedModifier;
        item->toolId = zBlock->zTool ? zBlock->zTool->getId() : -1;
        item->interactable = zBlock->interactable;
    }
}

Framework::Text BlockType::getTargetUIML() const
{
    return Text("<targetInfo><text width=\"auto\" height=\"auto\">") + name
         + "</text></targetInfo>";
}

Block* BlockType::loadBlock(Framework::Vec3<int> position,
    Framework::StreamReader* zReader,
    int dimensionId) const
{
    Block* result = createBlock(position, dimensionId);
    loadSuperBlock(result, zReader, dimensionId);
    return result;
}

void BlockType::saveBlock(Block* zBlock, Framework::StreamWriter* zWriter) const
{
    saveSuperBlock(zBlock, zWriter);
}

Item* BlockType::getItemFromBlock(Block* zBlock) const
{
    Item* result = createItem();
    if (result)
    {
        createSuperItem(zBlock, result);
    }
    return result;
}

Block* BlockType::createBlockAt(
    Framework::Vec3<int> position, int dimensionId, Item* zUsedItem) const
{
    Block* result = createBlock(position, dimensionId);
    createSuperBlock(result, zUsedItem);
    return result;
}

bool BlockType::doesNeedClientInstance() const
{
    return needsClientInstance;
}

const ModelInfo& BlockType::getModel() const
{
    return model;
}

int BlockType::getId() const
{
    return id;
}

const Block* BlockType::zDefault() const
{
    return defaultBlock;
}

int BlockType::getInitialMaxHP() const
{
    return initialMaxHP;
}

bool BlockType::isLightSource() const
{
    return lightSource;
}

const Block* getDefaultBlock(Either<Block*, int> b)
{
    if (b.isA())
        return b;
    else
        return StaticRegistry<BlockType>::INSTANCE.zElement(b)->zDefault();
}

BlockType* BlockType::initializeDefault()
{
    defaultBlock = createBlockAt({0, 0, 0}, 0, 0);
    return this;
}

const char* BlockType::getName() const
{
    return name;
}

const bool BlockType::doesNeedModelSubscription() const
{
    return needModelSubscription;
}

void BlockType::writeTypeInfo(StreamWriter* zWriter) const
{
    int id = getId();
    zWriter->schreibe((char*)&id, 4);
    bool inst = doesNeedClientInstance();
    zWriter->schreibe((char*)&inst, 1);
    bool sub = doesNeedModelSubscription();
    zWriter->schreibe((char*)&sub, 1);
    bool fluid = isFluid();
    zWriter->schreibe((char*)&fluid, 1);
    int maxHp = getInitialMaxHP();
    zWriter->schreibe((char*)&maxHp, 4);
    getModel().writeTo(zWriter);
}

bool BlockType::isFluid() const
{
    return false;
}

int BlockType::getTypeId(const char* name)
{
    Text n = name;
    for (int i = 0; i < StaticRegistry<BlockType>::INSTANCE.getCount(); i++)
    {
        if (StaticRegistry<BlockType>::INSTANCE.zElement(i)
            && n.istGleich(
                StaticRegistry<BlockType>::INSTANCE.zElement(i)->getName()))
            return StaticRegistry<BlockType>::INSTANCE.zElement(i)->getId();
    }
    return 0;
}

Framework::Text BlockType::getTypeName(int id)
{
    for (int i = 0; i < StaticRegistry<BlockType>::INSTANCE.getCount(); i++)
    {
        if (StaticRegistry<BlockType>::INSTANCE.zElement(i)
            && StaticRegistry<BlockType>::INSTANCE.zElement(i)->getId() == id)
            return StaticRegistry<BlockType>::INSTANCE.zElement(i)->getName();
    }
    return 0;
}