#include <yandex/maps/wiki/configs/editor/config_holder.h>
#include <yandex/maps/wiki/configs/editor/exception.h>
#include <yandex/maps/wiki/configs/editor/category_template.h>
#include <yandex/maps/wiki/configs/editor/section.h>
#include <yandex/maps/wiki/configs/editor/attrdef.h>
#include <yandex/maps/wiki/configs/editor/restrictions.h>
#include <yandex/maps/wiki/configs/editor/interactivity.h>
#include <yandex/maps/wiki/configs/editor/category_groups.h>
#include <yandex/maps/wiki/configs/editor/overlay_groups.h>
#include <yandex/maps/wiki/configs/editor/categories.h>
#include <yandex/maps/wiki/configs/editor/countries.h>
#include <yandex/maps/wiki/configs/editor/topology_groups.h>
#include <yandex/maps/wiki/configs/editor/externals.h>

#include <library/cpp/resource/resource.h>

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

const auto RESOURCE_WIKI_CONFIGS_EDITOR_XML = "WIKI_CONFIGS_EDITOR_XML";

ConfigHolder::ConfigHolder()
    : doc_(maps::xml3::Doc::fromString(NResource::Find(RESOURCE_WIKI_CONFIGS_EDITOR_XML)))
{
    init();
}

ConfigHolder::ConfigHolder(const std::string& path)
    : doc_(path)
{
    init();
}

ConfigHolder::~ConfigHolder() = default;


void ConfigHolder::init()
{
    maps::xml3::Node node = doc_.node("/editor");

    const auto categoryTemplates = node.nodes("category-templates/category-template");
    for (size_t i = 0; i < categoryTemplates.size(); ++i) {
        categoryTemplates_.insert(std::make_pair(
            categoryTemplates[i].attr<std::string>("id"),
            CategoryTemplate(categoryTemplates[i])));
    }

    const auto sections = node.nodes("sections/section");
    for (size_t i = 0; i < sections.size(); i++) {
        sections_.insert(std::make_pair(
            sections[i].attr<std::string>("id"),
            Section(sections[i])));
    }

    const auto attrs = node.nodes("attributes/attribute");
    for (size_t i = 0; i < attrs.size(); ++i) {
        attributes_.insert(std::make_pair(
            attrs[i].attr<std::string>("id"),
            std::make_shared<AttributeDef>(attrs[i])));
    }

    const auto restrictionNodes = node.nodes("restrictions/restriction");
    for (size_t i = 0; i < restrictionNodes.size(); ++i) {
        restrictionsById_.insert(std::make_pair(
            restrictionNodes[i].attr<std::string>("id"),
            Restrictions(restrictionNodes[i])));
    }

    const auto interactivityNodes = node.nodes("interactivities/interactivity");
    for (size_t i = 0; i < interactivityNodes.size(); ++i) {
        interactivitySettings_.insert(std::make_pair(
            interactivityNodes[i].attr<std::string>("id"),
            Interactivity(interactivityNodes[i])));
    }

    countries_.reset(new Countries(node.node("countries")));

    categoryGroups_.reset(new CategoryGroups(node.node("category-groups")));

    overlayGroups_.reset(new OverlayGroups(node.node("overlay-groups")));

    topologyGroups_.reset(new TopologyGroups(node.node("topology-groups")));

    externals_.reset(new Externals(node.node("externals")));

    categories_.reset(new Categories(node.node("categories")));
    categories_->onLoad(*this);
}

const CategoryTemplate&
ConfigHolder::categoryTemplate(const std::string& id) const
{
    auto catTIt = categoryTemplates_.find(id);
    if (catTIt == categoryTemplates_.end()) {
        throw InitializationError() << "CategoryTemplate " << id << " not found.";
    }
    return catTIt->second;
}

const Section&
ConfigHolder::section(const std::string& id) const
{
    auto sectionIt = sections_.find(id);
    if (sectionIt == sections_.end()) {
        throw InitializationError() << "Section " << id << " not found.";
    }
    return sectionIt->second;
}

const Restrictions&
ConfigHolder::restrictions(const std::string& id) const
{
    auto it = restrictionsById_.find(id);
    if (it == restrictionsById_.end()) {
        throw InitializationError() << "Restrictions " << id << " not found.";
    }
    return it->second;
}

const Interactivity&
ConfigHolder::interactivity(const std::string& id) const
{
    auto interactivityIt = interactivitySettings_.find(id);
    if (interactivityIt == interactivitySettings_.end()) {
        throw InitializationError() << "Interactivity settings " << id << " not found.";
    }
    return interactivityIt->second;
}

const AttrDefPtr&
ConfigHolder::attribute(const std::string& id) const
{
    auto attrIt = attributes_.find(id);
    if (attrIt == attributes_.end()) {
        throw InitializationError() << "Attribute " << id << " not found.";
    }
    return attrIt->second;
}

const AttrDefPtr&
ConfigHolder::registerSystemAttribute(
    const std::string& id,
    const std::string& defaultValue,
    const StringSet& allowedValues)
{
    return attributes_.insert(
        std::make_pair(id,
            std::make_shared<AttributeDef>(
                id,
                defaultValue,
                allowedValues))).first->second;
}

bool
ConfigHolder::isAttributeDefined(const std::string& id) const
{
    auto attrIt = attributes_.find(id);
    return attrIt != attributes_.end();
}

} // maps::wiki::configs::editor
