#pragma once

#include "types.h"

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

#include <utility>
#include <vector>


namespace maps {
namespace wiki {
namespace topo {
namespace geom {


// Object is near other one, if distance between them is less than tolerance.
template<typename GeomTypeOne, typename GeomTypeTwo>
bool near(const GeomTypeOne& one, const GeomTypeTwo& two, double tolerance = geolib3::EPS)
{
    return geolib3::distance(one, two) < tolerance;
}

// Arrange the objects relative to the specified metrics.
// Metric is evaluated once for each object.
// Metric's values are also returned.
template<typename Type, typename Metric>
std::vector<double> order(std::vector<Type>& objects, Metric metric)
{
    typedef std::pair<double, size_t> Index;

    std::vector<Index> indices;
    indices.reserve(objects.size());
    for (size_t i = 0; i < objects.size(); ++i) {
        indices.emplace_back(metric(objects[i]), i);
    }

    std::sort(indices.begin(), indices.end());

    std::vector<Type> orderedObjects;
    std::vector<double> orderValues;
    orderedObjects.reserve(objects.size());
    orderValues.reserve(objects.size());
    for (const auto& index: indices) {
        orderValues.push_back(index.first);
        orderedObjects.push_back(std::move(objects[index.second]));
    }

    std::swap(objects, orderedObjects);
    return orderValues;
}


// Polyline is closed if distance between end points less than tolerance.
bool isClosed(const geolib3::Polyline2& polyline, double tolerance = geolib3::EPS);


// Points are ordered as their projections on polyline,
// then one by one is added to polyline respectively their order.
// If distance between projection of point and nearest vertex is less
// than tolerance, then point is skipped.
SnapPolyline supplementPolylineWithPoints(const SnapPolyline& polyline,
    SnapPointVector points, double tolerance = geolib3::EPS);


geolib3::Point2 center(const geolib3::Segment2& segment);
geolib3::Point2 center(const geolib3::TwoPoints& pair);
// Return center of middle segment
geolib3::Point2 center(const geolib3::Polyline2& polyline);

geolib3::Polyline2 reverse(const geolib3::Polyline2& polyline);


} // namespace maps::wiki::topo::geom
} // namespace maps::wiki::topo
} // namespace maps::wiki
} // namespace maps

