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

#include <maps/libs/tile/include/utils.h>

namespace maps {
namespace wiki {
namespace configs {
namespace editor {

TopologyGroup::Interactivity::Interactivity(const xml3::Node& node)
    : highlightingZoom_(node.node("highlighting"))
{
    const xml3::Node& editing = node.node("editing");
    junctionAccuracy_ = editing.attr<double>("junction-accuracy-pixels");
    vertexAccuracy_ = editing.attr<double>("vertex-accuracy-pixels");
}

TopologyGroup::TopologyGroup(const xml3::Node& node)
    : interactivity_(node.node("interactivity"))
{
    junctionsCategory_ = node.firstElementChild("junction").attr<std::string>("category-id");
    xml3::Node leNode = node.firstElementChild("linear-element");
    while (!leNode.isNull()) {
        linearElementsCategories_.insert(leNode.attr<std::string>("category-id"));
        leNode = leNode.nextElementSibling("linear-element");
    }
    const xml3::Node& geomNode = node.node("geometry");
    tolerance_ = geomNode.attr<double>("tolerance-meters");
    maxJunctionGravity_ = geomNode.attr<double>("max-junction-gravity-meters");
    maxVertexGravity_ = geomNode.attr<double>("max-vertex-gravity-meters");
    maxGroupJunctionGravity_ = geomNode.attr<double>("max-group-junction-gravity-meters");
    maxGroupJunctionSnapVertexGravity_ =
        geomNode.attr<double>("max-group-junction-snap-vertex-gravity-meters");
    allowClosedEdge_ = geomNode.attr<bool>("allow-closed-edge");
    allowMergeOfOverlappedEdges_ = geomNode.attr<bool>("allow-merge-of-overlapped-edges");
}

bool
TopologyGroup::isInGroup(const std::string categoryId) const
{
    return junctionsCategory_ == categoryId
        || linearElementsCategories_.find(categoryId) != linearElementsCategories_.end();
}

double
TopologyGroup::junctionGravity(TZoom zoom) const
{
    double radius = interactivity().junctionAccuracy() *
        tile::zoomToResolution(zoom);
    return std::min(radius, maxJunctionGravity_);
}

double
TopologyGroup::vertexGravity(TZoom zoom) const
{
    double radius = interactivity().vertexAccuracy() *
        tile::zoomToResolution(zoom);
    return std::min(radius, maxVertexGravity_);
}

TopologyGroups::TopologyGroups(const xml3::Node& node)
{
    xml3::Node grpNode = node.firstElementChild("topology-group");
    while(!grpNode.isNull()){
        size_t newPos = groups_.size();
        groups_.emplace_back(grpNode);
        const auto& newGrp = groups_.back();
        for (const auto& linearElementCat : newGrp.linearElementsCategories()) {
            indexGroupsByCat_.emplace(linearElementCat, newPos);
        }
        indexGroupsByCat_.emplace(newGrp.junctionsCategory(), newPos);
        grpNode = grpNode.nextElementSibling("topology-group");
    }
}

const TopologyGroup*
TopologyGroups::findGroup(const std::string& categoryId) const
{
    auto it = indexGroupsByCat_.find(categoryId);
    return it == indexGroupsByCat_.end()
        ? nullptr
        : &groups_[it->second];
}

} // namespace editor
} // namespace configs
} // namespace wiki
} // namespace maps
