#include "merge_edges.h"

#include "../editor_impl.h"
#include "../cache_impl.h"
#include "../graph.h"
#include "process_events.h"

#include <yandex/maps/wiki/topo/storage.h>
#include <yandex/maps/wiki/topo/exception.h>

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

#include <algorithm>

namespace maps {
namespace wiki {
namespace topo {

MergeEdgesOperation::MergeEdgesOperation(
        const Callbacks& callbacks,
        CacheImpl& cache,
        NodeID commonNodeId)
    : Operation(callbacks, cache)
    , edgeId1(0)
    , edgeId2(0)
    , commonNodeId(commonNodeId)
{}

void MergeEdgesOperation::prepare()
{
    REQUIRE(commonNodeId, "Common node id not set for merge");
    cache_.loadByNodes( NodeIDSet{commonNodeId} );
    const Node& node = cache_.graph().node(commonNodeId);
    EdgeConstPtrVector incPtrs = node.incidentEdges();
    if (incPtrs.size() != 2) {
        throw GeomProcessingError(ErrorCode::NodeDeletionForbidden) <<
            "Node " << commonNodeId << " has " << incPtrs.size() <<
            " incident edges, must be 2";
    }
    edgeId1 = incPtrs.front()->id();
    edgeId2 = incPtrs.back()->id();
}

void MergeEdgesOperation::operator () ()
{
    prepare();
    Graph& graph = cache_.graph();
    const Edge& edge1 = graph.edge(edgeId1);
    const Edge& edge2 = graph.edge(edgeId2);
    geolib3::PointsVector points1 = edge1.geom().points();
    const geolib3::PointsVector& p2 = edge2.geom().points();
    bool isGeomDirectedEqually =
        (edge1.isStart(commonNodeId) && edge2.isEnd(commonNodeId)) ||
        (edge1.isEnd(commonNodeId) && edge2.isStart(commonNodeId));
    geolib3::PointsVector points2 = isGeomDirectedEqually
        ? geolib3::PointsVector(p2.begin(), p2.end())
        : geolib3::PointsVector(p2.rbegin(), p2.rend());
    if (edge1.isEnd(commonNodeId)) {
        auto start = points2.begin();
        points1.insert(points1.end(), ++start, points2.end());
    } else {
        auto end = points2.end();
        points1.insert(points1.begin(), points2.begin(), --end);
    }
    MergeEdgesEventData data(edgeId1, edgeId2, commonNodeId,
        std::move(points1), isGeomDirectedEqually);

    ProcessEvents process(cache_, callbacks_);

    MergeEdgesRequest request(data);
    callbacks_.mergeEdges.callback().processRequest(request);
    process(data);
    callbacks_.mergeEdges.callback().processEvent(MergeEdgesEvent(data));
}

} // namespace topo
} // namespace wiki
} // namespace maps
