#include "check_generator_result.h"

#include "data_model.h"

#include <maps/libs/log8/include/log8.h>

namespace maps::mrc::gen_targets {

void checkResult(RoadNetworkData& roadNetwork,
                 const std::unordered_set<EdgeId>& targetEdges,
                 const std::vector<const Path*>& paths)
{
    double targetEdgesLength = 0;
    for (EdgeId edgeId : targetEdges) {
        targetEdgesLength += roadNetwork.edge(edgeId).length;
    }

    int leftTurns = 0;
    int rightTurns = 0;
    int uTurns = 0;
    int straightPass = 0;

    for (const auto pathPtr : paths) {
        const auto& taskPath = *pathPtr;
        for(size_t i = 0; i < taskPath.size(); i++) {
            Edge curEdge = roadNetwork.edge(taskPath[i].edgeId);
            EdgeId nextEdgeId = taskPath[(i+1) % taskPath.size()].edgeId;
            REQUIRE(roadNetwork.edgesAreConnected(curEdge.id, nextEdgeId),
                    "task path is not connected "
                    << curEdge.id << " " << nextEdgeId);


            geolib3::Direction2 fromDirection = curEdge.outgoingDirection();
            geolib3::Direction2 toDirection = roadNetwork.edge(nextEdgeId).incomingDirection();

            if (turnIsUTurn(fromDirection, toDirection)) {
                uTurns++;
            } else if (turnIsLeft(fromDirection, toDirection)) {
                leftTurns++;
            } else if (turnIsRight(fromDirection, toDirection)) {
                rightTurns++;
            } else {
                straightPass++;
            }
        }
    }


    double coveredLength = 0;
    double tasksLength = 0;
    double edgesTime = 0;
    double maneuverTime = 0;
    int tollRoadEntrances = 0;
    int unnecessaryTollRoadEntrances = 0;
    std::unordered_set<EdgeId> usefulAsTargetEdges;
    std::unordered_set<EdgeId> visitedTargetEdges;
    for (const auto pathPtr : paths) {
        const auto& taskPath = *pathPtr;
        for (size_t i = 0; i < taskPath.size(); i++) {
            auto& pathEdge = taskPath[i];
            auto& edge = roadNetwork.edge(pathEdge.edgeId);
            auto& prevEdge = roadNetwork.edge(
                taskPath[(i - 1 + taskPath.size()) % taskPath.size()].edgeId);
            tasksLength += edge.length;
            edgesTime += edge.time;
            if (i != 0) {
                maneuverTime += roadNetwork.getManeuverPenalty(taskPath[i-1].edgeId,
                                                               pathEdge.edgeId);
            }

            if (edge.isToll && !prevEdge.isToll) {
                tollRoadEntrances++;
                if (!pathEdge.isUsefulAsTarget) {
                    unnecessaryTollRoadEntrances++;
                }
            }

            if (pathEdge.isUsefulAsTarget) {
                REQUIRE(usefulAsTargetEdges.insert(pathEdge.edgeId).second,
                        "Result tasks contain duplicate 'isUsefulAsTarget' edge visits");
                REQUIRE(targetEdges.count(pathEdge.edgeId),
                        "Result tasks contain non target 'isUsefulAsTarget' edge");
            }

           if (targetEdges.count(pathEdge.edgeId)
                && visitedTargetEdges.insert(pathEdge.edgeId).second) {
                coveredLength += edge.length;
            }
        }
    }

    double drivingTime = edgesTime + maneuverTime;

    REQUIRE(visitedTargetEdges.size() == usefulAsTargetEdges.size(),
            "Result task edges have wrong 'isUsefulAsTarget' flag");

    INFO() << "Targets length sum: " << targetEdgesLength
           << ", covered: " << coveredLength
           << " (" << 100 * coveredLength / targetEdgesLength << "%)"
           << ", full route length: " << tasksLength
           << " (" << 100 * tasksLength / coveredLength << "%)\n"
           << "driving time: " << drivingTime / 3600 << " hours\n"
           << "edges driving time: " << edgesTime / 3600 << " hours\n"
           << "maneuver driving time: " << maneuverTime / 3600 << " hours";

    INFO() << uTurns << " U turns, "
           << leftTurns << " left turns, "
           << rightTurns << " right turns, "
           << straightPass << " streighs passes";

    INFO() << unnecessaryTollRoadEntrances << " unnecessary toll road entrances, "
           << tollRoadEntrances << " toll road entrances";

    double maxTaskLength = 0;
    for (const auto pathPtr : paths) {
        const auto& taskPath = *pathPtr;
        double taskLength = 0;
        for (auto pathEdge : taskPath) {
            taskLength += roadNetwork.edge(pathEdge.edgeId).length;
        }
        maxTaskLength = std::max(maxTaskLength, taskLength);
        INFO() << "task length: " << int(taskLength / 1000.0) << " km";
    }
    INFO() << "max task length: " << int(maxTaskLength / 1000.0) << " km";
}


} // namespace maps::mrc::gen_targets
