#include "module.h"
#include "../transport-thread-common/common.h"
#include "../utils/misc.h"

#include <yandex/maps/wiki/validator/check.h>
#include <yandex/maps/wiki/validator/categories.h>

#include <maps/libs/geolib/include/convex_hull.h>

#include <boost/none.hpp>

namespace maps {
namespace wiki {
namespace validator {
namespace checks {

using categories::TRANSPORT_OPERATOR;
using categories::TRANSPORT_METRO_EXIT;
using categories::TRANSPORT_METRO_STATION;
using categories::TRANSPORT_METRO_LINE;
using categories::TRANSPORT_TRANSITION;
using categories::TRANSPORT_PASSAGEWAY;

VALIDATOR_CHECK_PART(transport_duplicates_check, transitions,
    TRANSPORT_TRANSITION,
    TRANSPORT_METRO_STATION,
    TRANSPORT_METRO_LINE,
    TRANSPORT_OPERATOR)
{
    std::map<std::pair<TId, TId>, std::vector<TId>> mapStationsToTransitions;
    context->objects<TRANSPORT_TRANSITION>().visit(
        [&](const TransportTransition* transition)
        {
            if (!isBoundToOperatorToValidate(context, transition)) {
                return;
            }
            mapStationsToTransitions[
                    std::make_pair(transition->stationA(), transition->stationB())
                ].push_back(transition->id());
            if (!transition->isOneway()) {
                mapStationsToTransitions[
                        std::make_pair(transition->stationB(), transition->stationA())
                    ].push_back(transition->id());
            }
        });
    auto geomById = [&](TId id) {
        return context->objects<TRANSPORT_METRO_STATION>().byId(id)->geom();
    };
    for (const auto& stationsAndTransitions : mapStationsToTransitions) {
        const auto& transitions = stationsAndTransitions.second;
        if (transitions.size() > 1) {
            context->critical(
                "duplicate-transitions",
                geolib3::bufferedConvexHull(
                    {
                        geomById(stationsAndTransitions.first.first),
                        geomById(stationsAndTransitions.first.second)
                    }, utils::BUFFER_DISTANCE),
                transitions);
        }
    }
}

VALIDATOR_CHECK_PART(transport_duplicates_check, passageway,
    TRANSPORT_PASSAGEWAY, TRANSPORT_METRO_EXIT,
    TRANSPORT_METRO_STATION,
    TRANSPORT_METRO_LINE,
    TRANSPORT_OPERATOR)
{
    std::map<std::pair<TId, TId>, std::vector<TId>> mapStationsToPassageways;
    std::set<TId> stations;
    context->objects<TRANSPORT_PASSAGEWAY>().visit(
        [&](const TransportPassageway* passageway)
        {
            if (!isBoundToOperatorToValidate(context, passageway)) {
                return;
            }
            stations.insert(passageway->station());
            if (passageway->oneway() == TransportPassageway::Oneway::To ||
                passageway->oneway() == TransportPassageway::Oneway::Both ) {
                mapStationsToPassageways[
                        std::make_pair(passageway->station(), passageway->exit())
                    ].push_back(passageway->id());
            }
            if (passageway->oneway() == TransportPassageway::Oneway::From ||
                passageway->oneway() == TransportPassageway::Oneway::Both ) {
                mapStationsToPassageways[
                        std::make_pair(passageway->exit(), passageway->station())
                    ].push_back(passageway->id());
            }
        });

    auto geomById = [&](TId id) {
        return stations.count(id)
            ? context->objects<TRANSPORT_METRO_STATION>().byId(id)->geom()
            : context->objects<TRANSPORT_METRO_EXIT>().byId(id)->geom();
    };

    for (const auto& stationsAndPassageways : mapStationsToPassageways) {
        const auto& passageways = stationsAndPassageways.second;
        if (passageways.size() > 1) {
            context->critical(
                "duplicate-passageways",
                geolib3::bufferedConvexHull(
                    {
                        geomById(stationsAndPassageways.first.first),
                        geomById(stationsAndPassageways.first.second)
                    }, utils::BUFFER_DISTANCE),
                passageways);
        }
    }
}

} // namespace checks
} // namespace validator
} // namespace wiki
} // namespace maps
