#include "topo_edit_context.h"

#include <maps/wikimap/mapspro/services/editor/src/configs/editor_cfg.h>
#include <maps/wikimap/mapspro/services/editor/src/configs/config.h>

#include <yandex/maps/wiki/configs/editor/restrictions.h>
#include <yandex/maps/wiki/configs/editor/categories.h>

namespace maps {
namespace wiki {


TopologyEditContext::TopologyEditContext(
        const TopologyGroup& topoGroup,
        ObjectsCache& cache,
        NewLinearElementsDataMap& newLinearElementsData,
        MergeJunctionsPolicy mergeJunctionsPolicy)
    : topoGroup_(topoGroup)
    , cache_(cache)
    , storage_(new TopoStorage(topoGroup, cache_))
    , newLinearElementsData_(newLinearElementsData)
    , mergeJunctionsPolicy_(mergeJunctionsPolicy)
{
    reset();
}

void
TopologyEditContext::reset()
{
    topoCache_.reset(new topo::Cache(*storage_, CALCULATION_TOLERANCE));
    editor_ = topoCache_->editor();
    editor_->registerTopoCallback(
        make_unique<TopoAddLinearElementCallback>(newLinearElementsData_, cache_)
    );
    editor_->registerTopoCallback(
        make_unique<TopoMoveLinearElementCallback>(cache_)
    );
    editor_->registerTopoCallback(
        make_unique<TopoDeleteLinearElementCallback>(cache_)
    );
    editor_->registerCallback(
        make_unique<SplitLinearElementCallback>(newLinearElementsData_, cache_)
    );

    std::unique_ptr<topo::MergeNodesCallback> mergeJcCallback;
    if (mergeJunctionsPolicy_ == MergeJunctionsPolicy::Restrict) {
        mergeJcCallback = make_unique<RestrictedMergeJunctionsCallback>();
    } else {
        ASSERT(mergeJunctionsPolicy_ == MergeJunctionsPolicy::Allow);
        mergeJcCallback = make_unique<AllowedMergeJunctionsCallback>(cache_);
    }
    editor_->registerCallback(std::move(mergeJcCallback));
}



topo::TopologyRestrictions
TopologyEditContext::getRestrictions(TZoom zoom)
{
    const double tolerance = topoGroup_.tolerance();
    const double groupJunctionGravity = topoGroup_.maxGroupJunctionGravity();
    const double groupJunctionSnapVertexGravity =
        topoGroup_.maxGroupJunctionSnapVertexGravity();

    const double junctionGravity = std::max(
        topoGroup_.junctionGravity(zoom),
        tolerance
    );
    const double vertexGravity = std::max(
        topoGroup_.vertexGravity(zoom),
        tolerance
    );

    const Restrictions* restrictions = nullptr;
    for (const auto& linearCategoryId : topoGroup_.linearElementsCategories()) {
        const auto& category = cfg()->editor()->categories()[linearCategoryId];
        if (!restrictions) {
            restrictions = &category.restrictions();
        }
    }

    topo::Limits<double> segmentLengthLimits(tolerance);

    topo::Limits<double> edgeLengthLimits(junctionGravity);

    return topo::TopologyRestrictions(
        tolerance,
        junctionGravity,
        vertexGravity,
        groupJunctionGravity,
        groupJunctionSnapVertexGravity,
        segmentLengthLimits,
        edgeLengthLimits,
        restrictions->maxIntersectionWithElement(),
        restrictions->maxCrossedElements(),
        topoGroup_.allowMergeOfOverlappedEdges()
            ? topo::MergeOfOverlappedEdgesPolicy::Allowed
            : topo::MergeOfOverlappedEdgesPolicy::Forbidden
    );
}

} // namespace wiki
} // namespace maps
