#include <maps/wikimap/mapspro/services/mrc/eye/lib/feedback/include/hypothesis_attrs.h>

namespace maps::mrc::eye {

namespace {

json::Value serialize(const geolib3::Polyline2& polyline) {
    std::vector<json::Value> coordinates;
    for (const auto& point: polyline.points()) {
        coordinates.emplace_back(
            std::vector{json::Value(point.x()), json::Value(point.y())}
        );
    }
    return json::Value {
        {"type", json::Value{"LineString"}},
        {"coordinates", json::Value{coordinates}},
    };
}

void setAttrs(
    const db::eye::AbsentTrafficLightAttrs& /*attrs*/,
    json::ObjectBuilder /*obj*/)
{
    // has no additional attributes
}

void setAttrs(
    const db::eye::AbsentHouseNumberAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["addressHouseNumber"] = attrs.number;
}

void setAttrs(
    const db::eye::WrongSpeedLimitAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["correctSpeedLimit"] = attrs.correctSpeedLimit;
    if (attrs.currentSpeedLimit.has_value() &&
        attrs.currentSpeedLimit.value() != object::RoadElement::NO_SPEED_LIMIT)
    {
        obj["currentSpeedLimit"] = attrs.currentSpeedLimit.value();
    }
    if (attrs.direction.has_value()) {
        obj["direction"] = maps::ymapsdf::rd::toString((maps::ymapsdf::rd::Direction)attrs.direction.value());
    }
    if (attrs.truck.has_value()) {
        obj["truck"] = attrs.truck.value();
    }
}

void setAttrs(
    const db::eye::AbsentParkingAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["toll"] = attrs.isToll;
}

void setAttrs(
    const db::eye::WrongParkingFtTypeAttrs& attrs,
    json::ObjectBuilder obj)
{
    using IntType = std::underlying_type<ymapsdf::ft::Type>::type;

    obj["currentFtType"] = static_cast<IntType>(attrs.currentFtType);
    obj["correctFtType"] = static_cast<IntType>(attrs.correctFtType);
}

void setAttrs(
    const db::eye::TrafficSignAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["feedbackType"] = boost::lexical_cast<std::string>(attrs.feedbackType);
}

void setAttrs(
    const db::eye::LaneHypothesisAttrs& /*attrs*/,
    json::ObjectBuilder obj)
{
    obj["feedbackType"] = toString(wiki::social::feedback::Type::TrafficLaneSign);
}

void setAttrs(
    const db::eye::WrongDirectionAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["supposedDirection"] = boost::lexical_cast<std::string>(attrs.supposedDirection);
}

void setAttrs(
    const db::eye::ProhibitedPathAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["movement"] = boost::lexical_cast<std::string>(attrs.movement);
    obj["path"] = serialize(geolib3::convertMercatorToGeodetic(attrs.mercatorPath));
}

void setAttrs(
    const db::eye::SpeedBumpAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["ahead"] = attrs.isAhead;
}

} // namespace

void setHypothesisAttrs(
    const db::eye::Hypothesis& hypothesis,
    json::ObjectBuilder obj)
{
    switch (hypothesis.type()) {
        case db::eye::HypothesisType::AbsentTrafficLight:
            setAttrs(hypothesis.attrs<db::eye::AbsentTrafficLightAttrs>(), obj);
            return;
        case db::eye::HypothesisType::AbsentHouseNumber:
            setAttrs(hypothesis.attrs<db::eye::AbsentHouseNumberAttrs>(), obj);
            return;
        case db::eye::HypothesisType::WrongSpeedLimit:
            setAttrs(hypothesis.attrs<db::eye::WrongSpeedLimitAttrs>(), obj);
            return;
        case db::eye::HypothesisType::AbsentParking:
            setAttrs(hypothesis.attrs<db::eye::AbsentParkingAttrs>(), obj);
            return;
        case db::eye::HypothesisType::WrongParkingFtType:
            setAttrs(hypothesis.attrs<db::eye::WrongParkingFtTypeAttrs>(), obj);
            return;
        case db::eye::HypothesisType::TrafficSign:
            setAttrs(hypothesis.attrs<db::eye::TrafficSignAttrs>(), obj);
            return;
        case db::eye::HypothesisType::LaneHypothesis:
            setAttrs(hypothesis.attrs<db::eye::LaneHypothesisAttrs>(), obj);
            return;
        case db::eye::HypothesisType::WrongDirection:
            setAttrs(hypothesis.attrs<db::eye::WrongDirectionAttrs>(), obj);
            return;
        case db::eye::HypothesisType::ProhibitedPath:
            setAttrs(hypothesis.attrs<db::eye::ProhibitedPathAttrs>(), obj);
            return;
        case db::eye::HypothesisType::SpeedBump:
            setAttrs(hypothesis.attrs<db::eye::SpeedBumpAttrs>(), obj);
            return;
        default:
            throw maps::RuntimeError()
                << "Unknown hypothesis type " << hypothesis.type()
                << " (setHypothesisAttrs)";
    }
}

} // namespace maps::mrc::eye
