#include <yandex/maps/wiki/graph/traversal.h>

#include <yandex/maps/wiki/graph/common.h>
#include <yandex/maps/wiki/graph/graph.h>

#include <queue>
#include <set>

namespace maps {
namespace wiki {
namespace graph {

void breadthFirstSearch(
        GetOutEdges outEdges,
        BreadthFirstSearchStep step,
        NodeID fromNodeId,
        NodePredicate isLockNode)
{
    struct NodeIdWithDistance {
        NodeIdWithDistance(NodeID nodeId, size_t distance)
            : nodeId(nodeId)
            , distance(distance)
        {}

        NodeID nodeId;
        size_t distance;
    };

    std::queue<NodeIdWithDistance> nodeIdQueue;
    std::set<NodeID> addedInQueue;

    nodeIdQueue.emplace(fromNodeId, 0);
    addedInQueue.insert(fromNodeId);

    while(!nodeIdQueue.empty()) {
        const NodeID nodeId = nodeIdQueue.front().nodeId;
        const size_t distance = nodeIdQueue.front().distance + 1;
        nodeIdQueue.pop();

        if (isLockNode(nodeId)) {
            continue;
        }

        for (const auto& edge: outEdges(nodeId)) {
            const auto nextNodeId = edge.endNodeId();
            if (addedInQueue.insert(nextNodeId).second) {
                step(nodeId, nextNodeId, distance);
                nodeIdQueue.emplace(nextNodeId, distance);
            }
        }
    }
}

} // namespace graph
} // namespace wiki
} // namespace maps
