#include "context.h"
#include "strings.h"

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/yt/include/serialization.h>

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


namespace maps::mrc::graph_coverage_export {

void ContextBase::processEdge(
    const maps::road_graph::EdgeData& edgeData,
    const geolib3::Polyline2& edgeGeometry,
    const db::Features& features,
    std::unordered_set<int32_t>& referencedGeoIds,
    NYT::TTableWriter<NYT::TNode>& writer) const
{
    auto coverageEdgeData = makeEdgeData(edgeData, edgeGeometry, features);
    referencedGeoIds.insert(std::begin(coverageEdgeData.geoIds), std::end(coverageEdgeData.geoIds));
    writer.AddRow(yt::serialize(coverageEdgeData));
}

EdgeData ContextBase::makeEdgeData(
    const maps::road_graph::EdgeData& edgeData,
    const geolib3::Polyline2& edgeGeometry,
    const db::Features& features) const
{
    auto pos = edgeGeometry.pointAt(0);

    auto privateArea = privateAreaIndex().isInPrivateArea(
        edgeGeometry, edgeData.category());

    return {static_cast<uint8_t>(edgeData.category()),
            edgeData.isToll(),
            edgeGeometry,
            evalGeoIds(pos),
            privateArea,
            std::move(features),
            static_cast<uint8_t>(graphType())};
}

std::vector<int32_t>
ContextBase::evalGeoIds(const geolib3::Point2& geodeticPos) const
{
    auto& geobase = this->geobase();
    auto geoId = geobase.getRegionIdByLocation(geodeticPos);

    auto regionsFilter = [&geobase](int32_t geoId)
        {
            auto region = geobase.getRegionById(geoId);
            // see https://doc.yandex-team.ru/lib/libgeobase5/concepts/region-types.html
            constexpr int32_t REGION_TYPE_COUNTRY = 3;
            constexpr int32_t REGION_TYPE_CITY = 6;
            return REGION_TYPE_COUNTRY <= region.type &&
                region.type <= REGION_TYPE_CITY;
        };

    std::vector<int32_t> geoIds;
    if (regionsFilter(geoId)) {
        geoIds.push_back(geoId);
    }

    for (auto parentGeoId : geobase.getRegionParentIds(geoId)) {
        if (regionsFilter(parentGeoId)) {
            geoIds.push_back(parentGeoId);
        }
    }
    return geoIds;
}

Context::Context(const std::string& mrcGraphPath,
                 db::GraphType graphType,
                 geobase::GeobasePtr geobasePtr,
                 object::Loader& objectLoader)
    : graph_(mrcGraphPath + "road_graph.fb")
    , graphRTree_(mrcGraphPath + "rtree.fb", graph_)
    , graphPersistentIndex_(mrcGraphPath + "edges_persistent_index.fb")
    , graphType_(graphType)
    , geobasePtr_(std::move(geobasePtr))
    , privateAreaIndex_(objectLoader)
{
    REQUIRE((graph_.version() == graphRTree_.version()) &&
            (graph_.version() == graphPersistentIndex_.version()),
        "road_graph version " << graph_.version()
        << " edges_persistent_index version " << graphPersistentIndex_.version()
        << " rtree version " << graphRTree_.version()
        << " are not equal");

    INFO() << "Graph version: " << graph_.version();
    INFO() << "Edges number: " << graph_.edgesNumber();
}

const road_graph::Graph& Context::graph() const
{
    return graph_;
}

const succinct_rtree::Rtree& Context::graphRTree() const
{
    return graphRTree_;
}

const road_graph::PersistentIndex& Context::graphPersistentIndex() const
{
    return graphPersistentIndex_;
}

const geobase::GeobaseInterface& Context::geobase() const
{
    return *geobasePtr_;
}

db::GraphType Context::graphType() const
{
    return graphType_;
}

const PrivateAreaIndex& Context::privateAreaIndex() const
{
    return privateAreaIndex_;
}

bool areCompatible(road_graph::AccessIdMask accessIdMask, db::GraphType graph)
{
    switch (graph) {
        case db::GraphType::Road:
            return accessIdMask & road_graph::AccessId::Automobile;
        case db::GraphType::Pedestrian:
            return accessIdMask & road_graph::AccessId::Pedestrian;
    }
}

} // namespace maps::mrc::graph_coverage_export
