#include <Block/SpriteBlock/PipelineBlock/DataCable/DataCable.hpp>
#include <Machine/Machine.hpp>
#include <WorldComponent/Wiring/DataWiring/DataWiring.hpp>
#include <MachineComponent/MultiBlockComponent/MultiBlockComponent.hpp>
#include <Machine/IDataMachine.hpp>

DataCable::DataCable(std::string name, std::string displayName) :
    PipelineBlock(name, displayName, "dataCable/$+", 3)
{}

bool DataCable::shouldConnectOnSide(LocalWorld &world, glm::ivec3 pos, Side side)
{
    pos += side.out();

    Block *block = world.getBlockAt(pos);
    if (block == nullptr)
    {
        return false;
    }

    if (block == this)
    {
        return true;
    }

    if (block->isSuffixed("Casing"))
    {
        uint8_t meta = *world.getBlockMeta(pos);
        MultiBlockComponent::CasingConnectorType connectorType = static_cast<MultiBlockComponent::CasingConnectorType>(meta & 0xF);
        
        if (connectorType == MultiBlockComponent::CasingConnectorType::Data)
        {
            Side casingConnectorSide(meta >> 4);
            if (casingConnectorSide == side.opposite())
            {
                return true;
            }
        }
    }

    std::shared_ptr<IDataMachine> dataMachine = std::dynamic_pointer_cast<IDataMachine>(world.getMachine(pos));
    if (dataMachine == nullptr)
    {
        return false;
    }

    return dataMachine->getDataInputSide() == side.opposite() || dataMachine->getDataOutputSide() == side.opposite();
}

Wiring& DataCable::getWiringFor(Chunk &chunk)
{
    return chunk.getNode()->getComponent<DataWiring>();
}