#pragma once

#include <yandex/maps/wiki/revision/common.h>

#include <maps/libs/geolib/include/bounding_box.h>
#include <maps/libs/geolib/include/point.h>
#include <maps/libs/common/include/exception.h>

#include <boost/optional.hpp>
#include <pqxx/pqxx>

#include <cstdint>
#include <string>
#include <vector>


namespace maps {
namespace wiki {
namespace geolocks {

typedef uint64_t TLockId;
typedef uint64_t TUId;

class NotFoundException : public maps::Exception { };

enum class GeolockType
{
    All,
    Auto,
    Manual
};

// Serializable: result is consistent for the calls in the same transaction.
bool isLocked(
        pqxx::transaction_base& txn,
        revision::DBID branchId,
        const geolib3::BoundingBox& bbox,
        GeolockType geolockType = GeolockType::All);
bool isLocked(
        pqxx::transaction_base& txn,
        revision::DBID branchId,
        const geolib3::Point2& point,
        GeolockType geolockType = GeolockType::All);


class GeoLock
{
public:
    ~GeoLock();
    GeoLock(const GeoLock& other);
    GeoLock(GeoLock&& other) noexcept;
    GeoLock& operator=(const GeoLock& other);
    GeoLock& operator=(GeoLock&& other) noexcept;

    TLockId id() const;
    TUId createdBy() const;
    const std::string& createdAt() const;

    revision::DBID branchId() const;
    revision::DBID commitId() const;
    const std::string& extentWkb() const;


    static boost::optional<GeoLock> tryLock(
            pqxx::connection_base& conn, TUId uid,
            revision::DBID branchId, const std::string& extentWkb);

    static boost::optional<std::vector<GeoLock>> tryLockAll(
            pqxx::connection_base& conn, TUId uid,
            revision::DBID branchId,
            const std::vector<std::string>& extentWkbs);

    static GeoLock load(pqxx::transaction_base& txn, TLockId id);

    // NOT serializable: result may change during subsequent calls in
    // the same transaction.
    static std::list<GeoLock> loadByGeom(
            pqxx::transaction_base& txn,
            revision::DBID branchId,
            const geolib3::BoundingBox& bbox,
            GeolockType geolockType = GeolockType::All);
    static std::list<GeoLock> loadByGeom(
            pqxx::transaction_base& txn,
            revision::DBID branchId,
            const geolib3::Point2& point,
            GeolockType geolockType = GeolockType::All);

    void unlock(pqxx::transaction_base& txn);
    void unlock(pqxx::connection_base& conn);

private:
    class Impl;
    explicit GeoLock(std::unique_ptr<Impl>&& impl);

    std::unique_ptr<Impl> impl_;
};

} // namespace geolocks
} // namespace wiki
} // namespace maps
