#pragma once

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

#include <algorithm>
#include <cstdint>
#include <functional>
#include <vector>

namespace maps::mrc::fb_rtree {

using Id = uint64_t;
using Ids = std::vector<Id>;
using IntersectsWithId = std::function<bool(const geolib3::BoundingBox&, Id)>;
using DistanceToId = std::function<double(const geolib3::Point2&, Id)>;

using DistanceToBox =
    std::function<double(const geolib3::Point2&, const geolib3::BoundingBox&)>;

/**
 * Compute squared distance between two points or between a point and a bounding
 * box. In the latter case if point is inside the bounding box then the distance
 * is zero. Otherwise it snaps point to a bounding box edge or vertex and then
 * compute distance to that snapped point.
 */
struct SquaredDistance {
    double operator()(const geolib3::Point2& lhs,
                      const geolib3::Point2& rhs) const
    {
        return geolib3::squaredDistance(lhs, rhs);
    }

    double operator()(const geolib3::Point2& lhs,
                      const geolib3::BoundingBox& rhs) const
    {
        return operator()(
            lhs,
            geolib3::Point2{std::clamp(lhs.x(), rhs.minX(), rhs.maxX()),
                            std::clamp(lhs.y(), rhs.minY(), rhs.maxY())});
    }
};

struct FastGeoDistance {
    template <class LhsGeom, class RhsGeom>
    double operator()(const LhsGeom& lhs, const RhsGeom& rhs) const
    {
        return geolib3::fastGeoDistance(lhs, rhs);
    }
};

}  // namespace maps::mrc::fb_rtree
