#pragma once

#include "data_model.h"

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

#include <queue>
#include <unordered_map>

namespace maps::mrc::gen_targets {

// see unrecommended_maneuvers.h
const Seconds UNRECOMMENDED_MANEUVER_PENALTY = 3600;

using NodeId = Id;

// Node is a road junction(crossroad). It contains all the roads
// adjoined to the crossroad.
// Node doesn't consider prohibited maneuvers (it may contains a pair
// of in-out edges that are disconnected due to a prohibited maneuver)
struct Node {
    NodeId id;
    std::vector<EdgeId> inEdges;
    std::vector<EdgeId> outEdges;
};
using NodesMap = std::unordered_map<NodeId, Node>;

// RoadNetworkData with modification abilities.
// getRecommendedTurnsPenalty() is overloaded and uses some graph properties
class ExtendedRoadNetwork : public RoadNetworkData {
public:
    // Initializes all the nodes which are connected to the target edges
    ExtendedRoadNetwork(Edges edges);

    boost::iterator_range<NodesMap::const_iterator> nodes() const;
    const Node& node(NodeId id) const;

    // Adds edge to the graph
    void addEdge(const Edge& edge);

    // Connects two deadend edges (creates a "teleport" between them).
    // @param time - teleportation time.
    // Doesn't create a node.
    void connectEdges(EdgeId fromEdge, EdgeId toEdge, Seconds time);
    // Removes a connection made in connectEdges
    void disconnectEdges(EdgeId fromEdge, EdgeId toEdge);

    EdgeId generateNewEdgeId() { return nextUnusedEdgeId_++; };

    // returns UNRECOMMENDED_MANEUVER_PENALTY if maneuver
    // is unrecommended, otherwise returns 0
    // By default all the maneuvers are not unrecommended.
    // (See unrecommended_maneuvers.h)
    virtual Seconds getUnrecommendedManeuversPenalty(EdgeId from, EdgeId to) const;

    // set unrecommended output edges for some edges.
    void setUnrecommendedManeuvers(
        std::unordered_map<EdgeId, std::vector<EdgeId>> unrecommendedManeuvers);

private:
    // create new node on the initEdge end point. The node contains all the
    // edges that start or end on that point.
    void createNode(EdgeId initEdgeId, NodeId newNodeId);

private:
    NodesMap nodes_;
    // start node for each edge
    std::unordered_map<EdgeId, NodeId> edgeIdToStartNodeId_;
    // end node for each edge
    std::unordered_map<EdgeId, NodeId> edgeIdToEndNodeId_;

    // edgeId generator for creating new edges
    EdgeId nextUnusedEdgeId_;

    // map <edge1, <unrecommended output edges of edge1>>
    std::unordered_map<EdgeId, std::vector<EdgeId>> unrecommendedManeuvers_;
};

} // namespace maps::mrc::gen_targets
