#include "guidance/suppress_mode_handler.hpp"
#include "extractor/travel_mode.hpp"

#include <algorithm>
#include <iterator>

namespace osrm::guidance
{

SuppressModeHandler::SuppressModeHandler(
    const util::NodeBasedDynamicGraph &node_based_graph,
    const extractor::EdgeBasedNodeDataContainer &node_data_container,
    const std::vector<util::Coordinate> &coordinates,
    const extractor::CompressedEdgeContainer &compressed_geometries,
    const extractor::RestrictionMap &node_restriction_map,
    const extractor::ObstacleMap &obstacle_nodes,
    const extractor::TurnLanesIndexedArray &turn_lanes_data,
    const extractor::StringTable &string_table,
    const extractor::SuffixTable &street_name_suffix_table)
    : IntersectionHandler(node_based_graph,
                          node_data_container,
                          coordinates,
                          compressed_geometries,
                          node_restriction_map,
                          obstacle_nodes,
                          turn_lanes_data,
                          string_table,
                          street_name_suffix_table)
{
}

bool SuppressModeHandler::canProcess(const NodeID,
                                     const EdgeID via_eid,
                                     const Intersection &intersection) const
{
    using std::begin;
    using std::end;

    // travel modes for which navigation should be suppressed
    static const constexpr char suppressed[] = {extractor::TRAVEL_MODE_TRAIN,
                                                extractor::TRAVEL_MODE_FERRY};

    // If the approach way is not on the suppression blacklist, and not all the exit ways share that
    // mode, there are no ways to suppress by this criteria.
    const auto in_mode =
        node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_eid).annotation_data)
            .travel_mode;
    const auto suppress_in_mode = std::find(begin(suppressed), end(suppressed), in_mode);

    const auto first = begin(intersection);
    const auto last = end(intersection);

    const auto all_share_mode = std::all_of(
        first,
        last,
        [this, &in_mode](const auto &road)
        {
            return node_data_container
                       .GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
                       .travel_mode == in_mode;
        });

    return (suppress_in_mode != end(suppressed)) && all_share_mode;
}

Intersection
SuppressModeHandler::operator()(const NodeID, const EdgeID, Intersection intersection) const
{
    const auto first = begin(intersection);
    const auto last = end(intersection);

    std::for_each(first,
                  last,
                  [&](auto &road)
                  {
                      const auto modifier = road.instruction.direction_modifier;
                      // use NoTurn, to not even have it as an IntermediateIntersection
                      const auto type = TurnType::NoTurn;

                      road.instruction = {type, modifier};
                  });

    return intersection;
}
} // namespace osrm::guidance
