#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/fb/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/joined_ranges_iterator.h>
#include <maps/libs/road_graph/include/graph.h>
#include <maps/wikimap/mapspro/services/mrc/libs/fb_rtree/include/rtree.h>

#include <util/generic/iterator_range.h>
#include <boost/iterator/iterator_facade.hpp>

#include <string>
#include <vector>

namespace maps::mrc::fb {

class EdgeIdIterator :
    public boost::iterator_facade<
        EdgeIdIterator,
        road_graph::EdgeId,
        std::forward_iterator_tag,
        road_graph::EdgeId>
{
public:
    using IdIteratorRangeType =
        std::invoke_result<decltype(&fb_rtree::Rtree::allIdsInWindow), fb_rtree::Rtree*, const geolib3::BoundingBox&, fb_rtree::IntersectsWithId>::type;
    using IdIteratorType = IdIteratorRangeType::iterator;

    EdgeIdIterator() = default;
    EdgeIdIterator(common::JoinedRangesIterator<IdIteratorType> idIterator);

    bool equal(const EdgeIdIterator& other) const;
    road_graph::EdgeId dereference() const;
    void increment();

private:
    common::JoinedRangesIterator<IdIteratorType> idIterator_;
};

class CoverageRtreeReader {
public:

    CoverageRtreeReader(const road_graph::Graph& graph,
                        const std::string& dir,
                        EMappingMode = EMappingMode::Standard);

    std::string_view version() const;

    TIteratorRange<EdgeIdIterator> getCoveredEdgeIdsByBbox(
        const geolib3::BoundingBox& geoBbox,
        std::optional<db::TFc> maxFc = std::nullopt) const;

    TIteratorRange<EdgeIdIterator> getCoveredEdgeIds() const;

    std::vector<road_graph::EdgeId> getNearestCoveredEdgeIds(
        const geolib3::Point2& geoPos,
        size_t limit,
        db::TFc maxFc) const;

private:
    const road_graph::Graph& graph_;
    std::vector<fb_rtree::Rtree> rtrees_;  // in ascending order of FC

    const fb_rtree::Rtree& rtree(db::TFc) const;
};

}  // namespace maps::mrc::fb
