#include "check_context_impl.h"
#include "common/utils.h"

#include <maps/libs/geolib/include/point.h>
#include <maps/libs/geolib/include/polygon.h>

#include <boost/none_t.hpp>

namespace maps::wiki::validator {

template<typename TGeom>
void CheckContext::Impl::baseReport(
    Severity severity,
    std::string description,
    const TGeom& geometry,
    const std::vector<TId>& oids,
    MessageKeyGenerator keyGenerator,
    MessageComparator messageComparator)
{
    std::vector<RevisionID> revIds;
    for (TId oid : oids) {
        revIds.push_back(
            dataSource_.objectRevisionId(oid, checkPart_.dependencies));
    }

    Message message(
        severity,
        checkId_,
        std::move(description),
        dataSource_.importantRegions().determineRegionType(geometry),
        geomWkb(geometry),
        std::move(revIds));

    taskMessages_.addMessage(
        std::move(message),
        checkId_,
        keyGenerator,
        messageComparator);
}

template<typename TGeom>
void CheckContext::Impl::report(
    Severity severity,
    std::string description,
    const TGeom& geometry,
    const std::vector<TId>& oids,
    MessageKeyGenerator keyGenerator,
    MessageComparator messageComparator)
{
    const auto& aoi = dataSource_.aoi();
    if (!aoi.empty() && !aoi.intersects<TGeom>(geometry)) {
        return;
    }
    baseReport(
        severity,
        std::move(description),
        geometry,
        oids,
        keyGenerator,
        messageComparator);
}

template<>
void CheckContext::Impl::report<boost::none_t>(
    Severity severity,
    std::string description,
    const boost::none_t& geometry,
    const std::vector<TId>& oids,
    MessageKeyGenerator keyGenerator,
    MessageComparator messageComparator)
{
    baseReport(
        severity,
        std::move(description),
        geometry,
        oids,
        keyGenerator,
        messageComparator);
}

CheckContext::CheckContext(Impl* impl)
    : impl_(impl)
{ }

CheckContext::~CheckContext() = default;

#define REPORT_HELPER_SPEC(name, severity, TGeom) \
void CheckContext::name( \
    std::string description, \
    const TGeom& geometry, \
    const std::vector<TId>& oids, \
    MessageKeyGenerator keyGenerator, \
    MessageComparator messageComparator) \
{ impl_->report(severity, std::move(description), geometry, oids, keyGenerator, messageComparator); }

#define REPORT_HELPER(name, severity) \
REPORT_HELPER_SPEC(name, severity, geolib3::Point2) \
REPORT_HELPER_SPEC(name, severity, geolib3::Polygon2) \
REPORT_HELPER_SPEC(name, severity, boost::none_t)

REPORT_HELPER(warning, Severity::Warning)
REPORT_HELPER(error, Severity::Error)
REPORT_HELPER(critical, Severity::Critical)
REPORT_HELPER(fatal, Severity::Fatal)

#undef REPORT_HELPER
#undef REPORT_HELPER_SPEC

#define SEVERITY_REPORT_HELPER(TGeom) \
void CheckContext::report( \
    Severity severity, \
    std::string description, \
    const TGeom& geometry, \
    const std::vector<TId>& oids, \
    MessageKeyGenerator keyGenerator, \
    MessageComparator messageComparator) \
{ impl_->report(severity, std::move(description), geometry, oids, keyGenerator, messageComparator); }

SEVERITY_REPORT_HELPER(geolib3::Point2)
SEVERITY_REPORT_HELPER(geolib3::Polygon2)
SEVERITY_REPORT_HELPER(boost::none_t)

#undef SEVERITY_REPORT_HELPER

const AreaOfInterest& CheckContext::aoi() const
{ return impl_->aoi(); }

template<class Category>
bool CheckContext::canSkipValidation(TId objectId) const
{
    return impl_->canSkipValidation(Category::id(), objectId);
}

template<class Category>
typename Category::TObjectsView CheckContext::objects()
{
    return castToCollection<Category>(impl_->collectionsTuple()).view(
        *this, impl_->threadPool());
}

template<class Category>
void CheckContext::checkLoaded() const
{ castToCollection<Category>(impl_->collectionsTuple()).checkLoaded(); }

#define INSTANTIATE_ACCESS_FUNCTION(category) \
template categories::category::TObjectsView \
CheckContext::objects<categories::category>(); \
template void CheckContext::checkLoaded<categories::category>() const; \
template bool CheckContext::canSkipValidation<categories::category>(TId) const; \

INSTANTIATE_ACCESS_FUNCTION(RD_EL)
INSTANTIATE_ACCESS_FUNCTION(RD_JC)
INSTANTIATE_ACCESS_FUNCTION(RD)
INSTANTIATE_ACCESS_FUNCTION(RD_NM)
INSTANTIATE_ACCESS_FUNCTION(COND)
INSTANTIATE_ACCESS_FUNCTION(COND_ANNOTATION)
INSTANTIATE_ACCESS_FUNCTION(COND_CAM)
INSTANTIATE_ACCESS_FUNCTION(COND_LANE)
INSTANTIATE_ACCESS_FUNCTION(COND_TRAFFIC_LIGHT);
INSTANTIATE_ACCESS_FUNCTION(COND_TOLL)
INSTANTIATE_ACCESS_FUNCTION(COND_DS);
INSTANTIATE_ACCESS_FUNCTION(COND_DS_EL);
INSTANTIATE_ACCESS_FUNCTION(COND_DT);
INSTANTIATE_ACCESS_FUNCTION(FREQ_DT)

INSTANTIATE_ACCESS_FUNCTION(AD_EL)
INSTANTIATE_ACCESS_FUNCTION(AD_JC)
INSTANTIATE_ACCESS_FUNCTION(AD_FC)
INSTANTIATE_ACCESS_FUNCTION(AD_CNT)
INSTANTIATE_ACCESS_FUNCTION(AD)
INSTANTIATE_ACCESS_FUNCTION(AD_SUBST_FC)
INSTANTIATE_ACCESS_FUNCTION(AD_SUBST)
INSTANTIATE_ACCESS_FUNCTION(AD_NM)

INSTANTIATE_ACCESS_FUNCTION(ADDR)
INSTANTIATE_ACCESS_FUNCTION(ADDR_NM)
INSTANTIATE_ACCESS_FUNCTION(ZIPCODE)
INSTANTIATE_ACCESS_FUNCTION(ZIPCODE_NM)
INSTANTIATE_ACCESS_FUNCTION(ARRIVAL_POINT)
INSTANTIATE_ACCESS_FUNCTION(ARRIVAL_POINT_NM)
INSTANTIATE_ACCESS_FUNCTION(FLAT_RANGE)

INSTANTIATE_ACCESS_FUNCTION(BLD)
INSTANTIATE_ACCESS_FUNCTION(BLD_COMPLEX)

INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_OPERATOR)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_METRO_LINE)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_METRO_STATION)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_METRO_EXIT)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_METRO_EL)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_METRO_JC)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_METRO_NM)

INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_METRO_THREAD)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_THREAD_STOP)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_TRANSITION)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_PASSAGEWAY)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_TRANSITION_BOARDING)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_PASSAGEWAY_BOARDING)

INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_RAILWAY)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_RAILWAY_STATION)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_RAILWAY_PLATFORM)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_RAILWAY_EL)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_RAILWAY_JC)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_RAILWAY_NM)

INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_TRAM_ROUTE)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_TRAM_THREAD)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_STOP)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_TERMINAL)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_TRAM_EL)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_TRAM_JC)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_NM)

INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_AIRPORT)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_AIRPORT_TERMINAL)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_HELICOPTER)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_AIRPORT_NM)

INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_WATERWAY_ROUTE)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_WATERWAY_THREAD)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_WATERWAY_STOP)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_WATERWAY_EL)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_WATERWAY_JC)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_WATERWAY_NM)

INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_BUS_THREAD)
INSTANTIATE_ACCESS_FUNCTION(TRANSPORT_BUS_ROUTE)

INSTANTIATE_ACCESS_FUNCTION(VEGETATION)
INSTANTIATE_ACCESS_FUNCTION(VEGETATION_CNT)
INSTANTIATE_ACCESS_FUNCTION(VEGETATION_FC)
INSTANTIATE_ACCESS_FUNCTION(VEGETATION_EL)
INSTANTIATE_ACCESS_FUNCTION(VEGETATION_JC)
INSTANTIATE_ACCESS_FUNCTION(VEGETATION_NM)

INSTANTIATE_ACCESS_FUNCTION(HYDRO)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_FC)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_FC_EL)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_FC_JC)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_LN)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_LN_EL)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_LN_JC)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_POINT)
INSTANTIATE_ACCESS_FUNCTION(HYDRO_NM)

INSTANTIATE_ACCESS_FUNCTION(URBAN)
INSTANTIATE_ACCESS_FUNCTION(URBAN_FC)
INSTANTIATE_ACCESS_FUNCTION(URBAN_EL)
INSTANTIATE_ACCESS_FUNCTION(URBAN_JC)
INSTANTIATE_ACCESS_FUNCTION(URBAN_NM)
INSTANTIATE_ACCESS_FUNCTION(URBAN_AREAL)

INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_FC)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_EL)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_JC)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_AREAL)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_PARKING_LOT)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_PARKING_LOT_LINEAR)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_PARKING_CONTROLLED_ZONE)
INSTANTIATE_ACCESS_FUNCTION(URBAN_ROADNET_NM)

INSTANTIATE_ACCESS_FUNCTION(RELIEF)
INSTANTIATE_ACCESS_FUNCTION(RELIEF_POINT)
INSTANTIATE_ACCESS_FUNCTION(RELIEF_FC)
INSTANTIATE_ACCESS_FUNCTION(RELIEF_EL)
INSTANTIATE_ACCESS_FUNCTION(RELIEF_JC)
INSTANTIATE_ACCESS_FUNCTION(RELIEF_NM)

INSTANTIATE_ACCESS_FUNCTION(POI_MEDICINE)
INSTANTIATE_ACCESS_FUNCTION(POI_EDU)
INSTANTIATE_ACCESS_FUNCTION(POI_FINANCE)
INSTANTIATE_ACCESS_FUNCTION(POI_SHOPPING)
INSTANTIATE_ACCESS_FUNCTION(POI_GOVERMENT)
INSTANTIATE_ACCESS_FUNCTION(POI_RELIGION)
INSTANTIATE_ACCESS_FUNCTION(POI_FOOD)
INSTANTIATE_ACCESS_FUNCTION(POI_AUTO)
INSTANTIATE_ACCESS_FUNCTION(POI_SPORT)
INSTANTIATE_ACCESS_FUNCTION(POI_LEISURE)
INSTANTIATE_ACCESS_FUNCTION(POI_URBAN)
INSTANTIATE_ACCESS_FUNCTION(POI_SERVICE)
INSTANTIATE_ACCESS_FUNCTION(POI_OTHER)
INSTANTIATE_ACCESS_FUNCTION(POI_NM)

INSTANTIATE_ACCESS_FUNCTION(INDOOR_PLAN)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_LEVEL)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_NM)

INSTANTIATE_ACCESS_FUNCTION(INDOOR_AREA)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_AUTO)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_FOOD)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_RELIGION)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_SHOPPING)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_SERVICE)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_FINANCE)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_MEDICINE)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_EDU)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_LEISURE)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_SPORT)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_GOVERMENT)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_POI_NM)

INSTANTIATE_ACCESS_FUNCTION(POI_ENTRANCE)

INSTANTIATE_ACCESS_FUNCTION(AD_NEUTRAL)
INSTANTIATE_ACCESS_FUNCTION(AD_NEUTRAL_FC)
INSTANTIATE_ACCESS_FUNCTION(AD_NEUTRAL_EL)
INSTANTIATE_ACCESS_FUNCTION(AD_NEUTRAL_JC)

INSTANTIATE_ACCESS_FUNCTION(REGION)

INSTANTIATE_ACCESS_FUNCTION(VEHICLE_RESTRICTION)

INSTANTIATE_ACCESS_FUNCTION(MRC_PEDESTRIAN_REGION)
INSTANTIATE_ACCESS_FUNCTION(INDOOR_RADIOMAP_CAPTURER_PATH)

INSTANTIATE_ACCESS_FUNCTION(ROAD_SURFACE)
INSTANTIATE_ACCESS_FUNCTION(ROAD_MARKING_POLYGONAL)
INSTANTIATE_ACCESS_FUNCTION(ROAD_MARKING_LINEAR)
INSTANTIATE_ACCESS_FUNCTION(ROAD_MARKING_POINT_LANE_DIRECTION)
INSTANTIATE_ACCESS_FUNCTION(ROAD_MARKING_POINT_SYMBOL)
INSTANTIATE_ACCESS_FUNCTION(ROAD_MARKING_POINT_ROAD_SIGN)
INSTANTIATE_ACCESS_FUNCTION(ROAD_MARKING_POINT_ROAD_SIGN_SPEED_LIMIT)
INSTANTIATE_ACCESS_FUNCTION(ROAD_MARKING_POINT_TEXT)

#undef INSTANTIATE_ACCESS_FUNCTION

} // namespace maps::wiki::validator
