#include "bbox_iterator.h"
#include "rtree.h"

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

namespace maps::mrc::fb_rtree::impl {

BboxIterator::BboxIterator(const Rtree* rtree,
                           const geolib3::BoundingBox& bbox,
                           IntersectsWithId intersectsWithId)
    : rtree_(rtree), bbox_(bbox), intersectsWithId_(intersectsWithId)
{
    onNewNode(rtree->root());
    findNextResult();
}

bool BboxIterator::equal(const BboxIterator& other) const
{
    return innerNodes_.empty() && results_.empty() &&
           other.innerNodes_.empty() && other.results_.empty();
}

Id BboxIterator::dereference() const
{
    ASSERT(!results_.empty());
    return results_.back();
}

void BboxIterator::increment()
{
    ASSERT(!results_.empty());
    results_.pop_back();
    findNextResult();
}

void BboxIterator::findNextResult()
{
    while (results_.empty() && !innerNodes_.empty()) {
        const auto node = innerNodes_.top();
        innerNodes_.pop();
        rtree_->forEachDirectChild(
            node, [this](const Node& child) { onNewNode(child); });
    }
}

void BboxIterator::onNewNode(const Node& node)
{
    if (!spatialRelation(node.bbox, bbox_, geolib3::Intersects)) {
        return;
    }
    if (const auto id = node.id) {
        if (intersectsWithId_(bbox_, *id)) {
            results_.push_back(*id);
        }
    }
    else {
        innerNodes_.push(node);
    }
}

}  // namespace maps::mrc::fb_rtree::impl
