#include <maps/wikimap/mapspro/services/mrc/libs/privacy/include/geo_id_provider.h>

#include <yandex/maps/coverage5/coverage.h>

namespace maps::mrc::privacy {

namespace {

bool byArea(const coverage5::Region& lhs, const coverage5::Region& rhs)
{
    return lhs.area() < rhs.area();
}

class GeoIdProviderImpl: public GeoIdProvider {

public:
    GeoIdProviderImpl(
            const std::string& geoIdPath,
            const std::string& layerName,
            LockMemory lockMemory)
        : coverage_(geoIdPath)
        , layer_(coverage_[layerName])
    {
        if (lockMemory == LockMemory::Yes) {
            coverage_.lockMemory();
        }
    }

    template<typename Geom>
    db::TIds load(const Geom& geoGeom) const
    {
        auto regions = layer_.regions(geoGeom, boost::none);
        regions.sort(byArea); // no guarantee of order

        db::TIds geoIds;
        for(const auto& region: regions) {
            if (region.id()) {
                geoIds.push_back(*region.id());
            }
        }

        return geoIds;
    }

    db::TIds load(const geolib3::Point2& geoPoint) const override
    {
        return load<geolib3::Point2>(geoPoint);
    }

    db::TIds load(const geolib3::BoundingBox& geoBox) const override
    {
        return load<geolib3::BoundingBox>(geoBox);
    }

    geolib3::MultiPolygon2 geomById(db::TId geoId) const override
    {
        return layer_.getRegion(geoId).geoms();
    }

private:
    coverage5::Coverage coverage_;
    const coverage5::Layer& layer_;
};

} // namespace

GeoIdProviderPtr makeGeoIdProvider(const std::string& geoIdPath,
                                   LockMemory lockMemory)
{
    return std::make_shared<GeoIdProviderImpl>(geoIdPath, "geoid", lockMemory);
}

} // namespace maps::mrc::privacy
