#pragma once

#include <maps/wikimap/mapspro/services/editor/src/common.h>
#include <maps/wikimap/mapspro/services/editor/src/relation_infos.h>
#include <maps/wikimap/mapspro/services/editor/src/srv_attrs/object_predicates.h>
#include <yandex/maps/wiki/revision/commit.h>

#include <unordered_set>
#include <unordered_map>
#include <string>
#include <list>

namespace maps {
namespace wiki {

class FormatterContext
{
public:
    class VisitedObjectsStack
    {
    public:
        void push(const GeoObject* obj) { objects_.push_back(obj); }
        void pop() { objects_.pop_back(); }
        bool isEqual(const std::list<std::string>& categoriesPath) const;
        bool isPartOfPath(const std::list<std::string>& categoriesPath) const;
        std::string dump() const;
        size_t depth() const { return objects_.size(); }
        const std::string& currentCategoryId() const;
        const GeoObject* currentObject() const;
        std::list<std::string> categoryIds() const;

    private:
        std::list<const GeoObject*> objects_;
    };

    virtual ~FormatterContext() {}
    //Mark object as expanded for future checks
    void markExpanded(TOid);
    //Decide if object needs compact or expanded output
    virtual bool needsExpansion(const GeoObject* obj);

    bool shouldPutMastersWhenSlave(const GeoObject* obj) const;

    VisitedObjectsStack& visitedObjectsStack() { return visitedObjectsStack_; }
    const VisitedObjectsStack& visitedObjectsStack() const { return visitedObjectsStack_; }
    size_t visitedDepth() const { return visitedObjectsStack_.depth(); }
    bool needExtendedRelativesOutput() const;
    bool continueToOutputMasters(const std::string& roleId) const;
    bool mastersOnExtendedOutputPath() const;
    bool slavesOnExtendedOutputPath() const;

    boost::optional<const StringSet&> putAllSlavesRoles(const GeoObject* obj) const;

    TRevisionId actualObjectRevisionId(const TRevisionId& revisionId) const;
    void setActualObjectRevisionId(const TRevisionId& revisionId);

    static RelativesLimit slavesPerRoleLimit(const std::string& slaveRoleId,
        const std::string& categoryId);
    static RelativesLimit slavesPerRoleLimitMax(const std::string& categoryId);
    RelativesLimit slavesPerRoleLimit(const std::string& slaveRoleId) const;
    RelativesLimit slavesPerRoleLimitMax() const;

    void setUserId(TUid userId) { userId_ = userId; }
    TUid userId() const { return userId_; }

private:
    //Check if object has been already expanded
    bool isExpanded(TOid) const;

    IsPartOfSimpleContourObject isContourPart_;
    std::unordered_set<TOid> expandedObjects_;
    VisitedObjectsStack visitedObjectsStack_;

    std::unordered_map<TOid, TRevisionId> actualObjectRevisionIds_;
    TUid userId_ = 0;
};

} // namespace wiki
} // namespace maps
