#pragma once

#include "visitors.h"

#include <yandex/maps/wiki/validator/common.h>
#include <yandex/maps/wiki/validator/check_context.h>
#include <maps/libs/common/include/exception.h>

#include <functional>
#include <unordered_map>

namespace maps {
namespace wiki {
namespace validator {

template<class Object>
class ObjectIdIndex
{
public:
    ObjectIdIndex() = default;

    template <typename TObjects>
    ObjectIdIndex(const TObjects& objects)
    {
        objectsById_.reserve(objects.size());

        for (const auto& object : objects) {
            bool insertionSucceeded = objectsById_.emplace(
                object.id(), &object).second;
            REQUIRE(insertionSucceeded,
                "Duplicate object id: " << object.id());
        }
    }

    void visit(
            std::function<void(const Object*)> visitor,
            CheckContext& context) const
    {
        for (const auto& idObjPair : objectsById_) {
            visitOne(visitor, idObjPair.second, context);
        }
    }

    void batchVisit(
            std::function<void(const Object*)> visitor,
            size_t batchSize,
            CheckContext& context,
            ThreadPool& pool) const
    {
        typedef decltype(objectsById_.cbegin()) CObjIt;
        auto visitFunc = [&](CObjIt begin, CObjIt end)
        {
            for (auto it = begin; it != end; ++it) {
                visitOne<Object>(visitor, it->second, context);
            }
        };
        batchVisitCollection(objectsById_, visitFunc, batchSize, pool);
    }

    bool contains(TId id) const
    { return objectsById_.find(id) != objectsById_.end(); }

    const Object* byId(TId id) const
    {
        auto object = objectsById_.find(id);
        REQUIRE(object != objectsById_.end(), "Object id: " << id << " not found");
        return object->second;
    }

private:
    std::unordered_map<TId, const Object*> objectsById_;
};

} // namespace validator
} // namespace wiki
} // namespace maps
