#include <memory>

#include <ElectricLink/ElectricLink.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <Machine/Machine.hpp>
#include <WorldComponent/Wiring/ElectricWiring/ElectricWiring.hpp>
#include <MachineComponent/ElectricComponent/ElectricComponent.hpp>

std::shared_ptr<ElectricLink> ElectricLink::dummyLink = std::make_shared<ElectricLink>();

ElectricLink::ElectricLink(
    LocalWorld &world,
    std::shared_ptr<WorldGridNode> node,
    std::vector<Wiring::Port> &ports,
    size_t startingPort
) : pos(ports[startingPort].pos),
    side(ports[startingPort].side)
{
    Wiring::Port &ourPort = ports[startingPort];
    if (ourPort.connections.size() == 0)
    {
        std::shared_ptr<Machine> machine = world.getMachine(ourPort.pos);

        if (machine != nullptr)
        {
            endpoints.push_back({machine, ourPort.side});
        }
    }

    for (Wiring::Connection &connection : ourPort.connections)
    {
        Wiring::Port &targetPort = ports[connection.targetPort];
        std::shared_ptr<Machine> machine = world.getMachine(targetPort.machinePos);

        if (std::dynamic_pointer_cast<IElectricMachine>(machine) != nullptr)
        {
            endpoints.push_back({machine, targetPort.side.opposite()});
        }
    }
}

std::shared_ptr<ElectricLink> ElectricLink::get(Machine &machine, Side side)
{
    LocalWorld &world = machine.getWorld();
    return world
        .getRootNode()
        ->getComponent<ElectricWiring>()
        .getLink(world, machine.pos + side.out(), side.opposite());
}

int ElectricLink::drainEnergy(int amount)
{
    int drained = 0;
    for (Endpoint &endpoint : endpoints)
    {
        if (amount == drained)
        {
            break;
        }

        std::shared_ptr<Machine> machine = endpoint.machine.lock();
        if (machine == nullptr)
        {
            continue;
        }

        drained += machine
            ->getComponent<ElectricComponent>()
            .drainEnergyFromSide(endpoint.side, amount - drained);
    }

    return drained;
}