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

#include <maps/libs/common/include/exception.h>
#include <maps/libs/geolib/include/conversion.h>
#include <maps/libs/geolib/include/distance.h>

namespace maps::mrc::object {

constexpr int RoadElement::NO_SPEED_LIMIT;

TId RoadElement::oppositeJunction(TId junctionId) const
{
    if (junctionId == startJunctionId()) {
        return endJunctionId();
    }

    if (junctionId == endJunctionId()) {
        return startJunctionId();
    }

    throw RuntimeError() << "Road element " << id() << " is not connected with " << junctionId;
}

bool RoadElement::hasJunction(TId junctionId) const
{
    return junctionId == startJunctionId() || junctionId == endJunctionId();
}

int RoadElement::zLevel(TId junctionId) const
{
    if (junctionId == startJunctionId()) {
        return startZLevel();
    }

    if (junctionId == endJunctionId()) {
        return endZLevel();
    }

    throw RuntimeError() << "Road element " << id() << " is not connected with " << junctionId;
}

RoadElement::Direction RoadElement::directionTo(TId junctionId) const
{
    if (junctionId == startJunctionId()) {
        return Direction::Backward;
    }

    if (junctionId == endJunctionId()) {
        return Direction::Forward;
    }

    throw RuntimeError() << "Road element " << id() << " is not connected with " << junctionId;
}

double RoadElement::length() const
{
    const auto& points = geom().points();
    if (points.size() <= 1) {
        return 0.0;
    }

    double value = 0.0;
    geolib3::Point2 prev = geolib3::mercator2GeoPoint(points.front());

    for (auto it = points.begin() + 1; it != points.end(); ++it) {
        const geolib3::Point2 current = geolib3::mercator2GeoPoint(*it);
        value += geolib3::geoDistance(prev, current);
        prev = current;
    }

    return value;
}

double RoadElement::fastLength() const
{
    double value = 0.0;

    for (size_t i = 0; i < geom().segmentsNumber(); ++i) {
        const auto& segment = geom().segmentAt(i);

        value += geolib3::toMeters(geolib3::length(segment), segment.midpoint());
    }

    return value;
}

const RoadElement::Lanes& RoadElement::lanes(Direction direction) const
{
    switch (direction) {
        case Direction::Forward:
            return tLanes();

        case Direction::Backward:
            return fLanes();

        default:
            throw RuntimeError() << "Bad direction " << direction;
    }
}

} // namespace maps::mrc::object
