#pragma once

#include "extended_road_network.h"

#include <maps/libs/common/include/exception.h>

#include <queue>
#include <unordered_map>

namespace maps::mrc::gen_targets {

// Creates a map of unrecommended maneuvers
//
// https://wiki.yandex-team.ru/jandekskarty/projects/catalog/ek/ek-nk/projects/automnyak/main/Algoritm-generatora-marshrutov-obezda-gorodov/#nerekomendovannyemanevry
//
// Assume there is a node with two input target edges I1, I2 and two output
// target edges O1, O2. Maneuver I2-O1 is prohibited.
// Assume that on some generator stage maneuver I1-O2 is used. There
// are two remaining targets I2 and O1, but the manuver betweem them is
// prohibited. So it is not good to use maneuver I1-O2, because it
// breaks the subgraph of targets. We will call such maneuvers as unrecommended.
class UnrecommendedManeuversCalculator {
public:
    UnrecommendedManeuversCalculator(const ExtendedRoadNetwork& roadNetwork);

    // Returns a map of unrecommended maneuvers as
    // <edge1, <unrecommended output edges of edge1>>
    std::unordered_map<EdgeId, std::vector<EdgeId>> getUnrecommendedManeuvers();

private:
    void calculateUnrecommendedManeuvers();

    // marks all the other output maneuvers from edgeId as unrecommended and
    // deletes them from edges_
    void setSingleOutputEdge(EdgeId edgeId, EdgeId singleOutEdgeId);

    // marks all the other input maneuvers to edgeId as unrecommended and
    // deletes them from edges_
    void setSingleInputEdge(EdgeId edgeId, EdgeId singleInEdgeId);

    // adds unrecommended maneuver to unrecommendedManeuvers_
    void addUnrecommendedManeuver(EdgeId from, EdgeId to);

    // filter list of edge Ids and returns only Ids of the target edges
    std::vector<EdgeId> getTargets(const std::vector<EdgeId> edgeIds);

private:
    const ExtendedRoadNetwork& roadNetwork_;
    Edges edges_; // copy of roadNetwork_ that will be modified

    std::unordered_map<EdgeId, std::vector<EdgeId>> unrecommendedManeuvers_;
};

} // namespace maps::mrc::gen_targets
