#include <maps/wikimap/mapspro/services/mrc/libs/object/include/mock_loader.h>

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

#include <algorithm>
#include <functional>
#include <iterator>
#include <tuple>
#include <vector>

namespace maps::mrc::object {

namespace {

template<typename T>
std::vector<T> filter(const std::vector<T>& objects, std::function<bool(const T&)> predicate)
{
    std::vector<T> result;

    std::copy_if(
        objects.begin(), objects.end(),
        std::back_inserter(result),
        predicate
    );

    return result;
}

template<typename T>
std::vector<T> filterByBox(const std::vector<T>& objects, const geolib3::BoundingBox& mercatorBox)
{
    return filter<T>(
        objects,
        [&](const T& object) {
            return geolib3::spatialRelation(
                mercatorBox,
                object.geom(),
                geolib3::SpatialRelation::Intersects
            );
        }
    );
}

template<typename T>
std::vector<T> filterById(const std::vector<T>& objects, const TIds& ids)
{
    return filter<T>(
        objects,
        [&](const T& object) {
            return ids.end() != std::find(ids.begin(), ids.end(), object.id());
        }
    );
}

} // namespace

RoadElements MockLoader::loadRoadElements(const TIds& ids)
{
    return filterById(std::get<RoadElements>(data_), ids);
}

RoadElements MockLoader::loadRoadElements(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<RoadElements>(data_), mercatorBox);
}

ParkingLots  MockLoader::loadParkingLots(const TIds& ids)
{
    return filterById(std::get<ParkingLots>(data_), ids);
}

ParkingLots MockLoader::loadParkingLots(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<ParkingLots>(data_), mercatorBox);
}

LinearParkingLots MockLoader::loadLinearParkingLots(const TIds& ids)
{
    return filterById(std::get<LinearParkingLots>(data_), ids);
}

LinearParkingLots MockLoader::loadLinearParkingLots(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<LinearParkingLots>(data_), mercatorBox);
}

MrcRegions MockLoader::loadMrcRegions(const TIds& ids)
{
    return filterById(std::get<MrcRegions>(data_), ids);
}

MrcRegions MockLoader::loadMrcRegions(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<MrcRegions>(data_), mercatorBox);
}

MrcRegions MockLoader::loadAllMrcRegions()
{
    return std::get<MrcRegions>(data_);
}

RoadJunctions MockLoader::loadRoadJunctions(const TIds& ids)
{
    return filterById(std::get<RoadJunctions>(data_), ids);
}

RoadJunctions MockLoader::loadRoadJunctions(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<RoadJunctions>(data_), mercatorBox);
}

Conditions MockLoader::loadConditions(const TIds& ids)
{
    return filterById(std::get<Conditions>(data_), ids);
}

TrafficLights MockLoader::loadTrafficLights(const TIds& ids)
{
    return filterById(std::get<TrafficLights>(data_), ids);
}

SpeedBumps MockLoader::loadSpeedBumps(const TIds& ids)
{
    return filterById(std::get<SpeedBumps>(data_), ids);
}

AddressPointWithNames MockLoader::loadAddressPointWithNames(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<AddressPointWithNames>(data_), mercatorBox);
}

bool MockLoader::hasBuildings(const geolib3::BoundingBox& mercatorBox)
{
    return !mercatorBox.isDegenerate();
}

Buildings MockLoader::loadBuildings(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<Buildings>(data_), mercatorBox);
}

Entrances MockLoader::loadEntrances(const geolib3::BoundingBox& mercatorBox)
{
    return filterByBox(std::get<Entrances>(data_), mercatorBox);
}

} //namespace maps::mrc::object
