#pragma once

#include "generalization.h"
#include "params.h"
#include "revision_meta/snapshot.h"

#include <yandex/maps/wiki/common/pgpool3_helpers.h>
#include <yandex/maps/wiki/geom_tools/common.h>

namespace maps {
namespace wiki {
namespace contours {

/**
 * \brief covering of a contour object
 *        the contour object is an area object
 *        the area object consist of rings (internal and external)
 *        the rings are composed of linestrings
 *        the number of points in the linestring is limited - should not exceed 200
 * \see https://wiki.yandex-team.ru/jandekskarty/projects/catalog/ek/ek-nk/architecture/contourobjectsrendering
 */
class ContourObject {
public:
    void load
        ( revision_meta::Snapshot& snapshot
        , revision_meta::TObjectId id
        , const Params& params
        );
    std::vector<std::string> dumpToSql(pqxx::transaction_base& work) const;
    bool isDeleted() const { return !commitId_; }
    bool isValid() const;
    size_t polygonCount() const;

private:
    // a contour object geometry optimized for screen resolution
    struct Level {
        ZoomRange zoomRange;
        geom_tools::GeolibPolygonVector polygons;
        std::vector<double> originalAreas; // before simplify and partition
        size_t pointsNumber; // after simplify before partition
    };

    revision_meta::TObjectId objectId_;
    revision_meta::TCommitId commitId_; // zero for deleted object
    revision_meta::TCategoryId categoryId_;
    revision_meta::TAttributesMap domainAttrs_;
    revision_meta::TAttributesMap serviceAttrs_;
    geom_tools::GeolibPolygonVector polygons_;
    std::vector<double> areasCache_;
    ZoomRange zoomRange_;
    std::vector<Level> pyramid_;

    void addLevel(const Level& level, const Params& params);
    void correctPyramid();
    geom_tools::GeolibPolygonVector getPolygons( revision_meta::Snapshot& snapshot
        , double tolerance
        );
    geolib3::LinearRing2 getRing( revision_meta::Snapshot& snapshot
        , revision_meta::TObjectId ringId
        , double tolerance
        );
    Level makeLevel(Zoom zmax) const;
    void calculateServiceAttributes(revision_meta::Snapshot& snapshot);
    void reset();
    ContourObject::Level
    splitLevel(const ContourObject::Level& level, const Params& params) const;
    void sync(const revision_meta::Object& obj);

    /**
     * geom-tools library (and other libraries that are used by it) can throw
     *   map::Exception and std::exception.
     * geomToolsCall wrapper is used to translate
     *   every kind of exceptions to GeomToolsException class.
     */
    template <typename Functor>
    auto geomToolsCall(Functor&& f) const;
};

typedef std::vector<ContourObject> ContourObjects;

size_t getPolygonCount(const ContourObjects& objs);
size_t getInvalidCount(const ContourObjects& objs);
size_t getDeletedCount(const ContourObjects& objs);

std::vector<std::string> dumpToSql(const ContourObjects& objs, pqxx::transaction_base& work);
std::string sqlDelete(const revision_meta::TObjectIdSet& ids);
std::string sqlSelectCount();
std::string sqlSelectObjectIds();
std::string sqlSelectArea();

} // contours
} // wiki
} // maps
