#include "editor_impl.h"

#include "editing/save_edge.h"
#include "editing/save_objects.h"
#include "editing/create_intersections.h"
#include "editing/merge_edges.h"
#include "editing/delete_edge.h"
#include "editing/move_node.h"
#include "editing/snap_nodes.h"

namespace maps {
namespace wiki {
namespace topo {

namespace {

template <class CallbackHolder>
void checkCallback(const CallbackHolder& holder,
    const std::string& callbackName, const std::string& operation)
{
    REQUIRE(holder.isSet(),
        callbackName << " callback is not set, needed for operation " << operation);
}

void checkCallbacksForMergeEdges(const Callbacks& callbacks)
{
    checkCallback<CallbackHolder<TopoDeleteEdgeCallback>>(
        callbacks.deleteEdgeTopo, "AddEdge topological", "MergeEdges");
    checkCallback<CallbackHolder<TopoMoveEdgeCallback>>(
        callbacks.moveEdgeTopo, "MoveEdge topological", "MergeEdges");
    checkCallback<CallbackHolder<MergeEdgesCallback>>(
        callbacks.mergeEdges, "MergeEdges", "MergeEdges");
}

void checkCallbacksForEdgesEditing(const Callbacks& callbacks,
                                    const std::string& operation)
{
    checkCallback<CallbackHolder<TopoAddEdgeCallback>>(
        callbacks.addEdgeTopo, "AddEdge topological", operation);
    checkCallback<CallbackHolder<TopoMoveEdgeCallback>>(
        callbacks.moveEdgeTopo, "MoveEdge topological", operation);
    checkCallback<CallbackHolder<SplitEdgeCallback>>(
        callbacks.splitEdge, "SplitEdge", operation);
}

} // namespace

EditorImpl::EditorImpl(CacheImpl& cacheImpl)
    : cacheImpl_(cacheImpl)
    , callbacks_(new Callbacks())
{}

void EditorImpl::saveEdge(
    const Editor::EdgeData& data,
    const TopologyRestrictions& restrictions)
{
    const std::string op(!data.id.exists() ? "AddEdge" : "EditEdge");
    checkCallbacksForEdgesEditing(*callbacks_, op);
    SaveEdgeOperation(
        callbacks(), cacheImpl_, data, restrictions
    )();
}

void EditorImpl::saveNode(
    const Editor::NodeData& data,
    const TopologyRestrictions& restrictions)
{
    checkCallbacksForEdgesEditing(*callbacks_, "MoveNode");
    checkCallback<CallbackHolder<MergeNodesCallback>>(
        callbacks_->mergeNodes, "MergeNodes", "MoveNode");
    MoveNodeOperation(
        callbacks(), cacheImpl_, data, restrictions)();
}

void
EditorImpl::saveObjects(
    const Editor::TopologyData& data,
    const TopologyRestrictions& restrictions)
{
    checkCallbacksForEdgesEditing(*callbacks_, "UpdateObjects");
    SaveObjectsOperation(callbacks(), cacheImpl_, data, restrictions)();
}

Editor::CreateIntersectionsResult
EditorImpl::createIntersections(
    const geolib3::PolylinesVector& geoms,
    const TopologyRestrictions& restrictions)
{
    const std::string op = "CreateIntersections";
    checkCallbacksForEdgesEditing(*callbacks_, op);
    return CreateIntersectionsOperation(
        callbacks(), cacheImpl_, geoms, restrictions
    )();
}

void EditorImpl::deleteEdge(EdgeID edgeId)
{
    checkCallback<CallbackHolder<TopoDeleteEdgeCallback>>(
        callbacks_->deleteEdgeTopo, "DeleteEdge topological", "DeleteEdge");
    checkCallback<CallbackHolder<DeleteEdgeCallback>>(
        callbacks_->deleteEdge, "DeleteEdge", "DeleteEdge");
    DeleteEdgeOperation(
        callbacks(), cacheImpl_, edgeId)();
}

void EditorImpl::mergeEdges(NodeID commonNodeId)
{
    checkCallbacksForMergeEdges(*callbacks_);
    MergeEdgesOperation(
        callbacks(), cacheImpl_, commonNodeId)();
}

void EditorImpl::snapNodes(
    const NodeIDSet& nodeIds,
    const TopologyRestrictions& restrictions)
{
    const std::string op = "SnapNodes";
    checkCallbacksForEdgesEditing(*callbacks_, op);
    SnapNodesOperation(
        callbacks(), cacheImpl_, nodeIds, restrictions
    )();
}

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