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

#include <maps/libs/pgpool/include/pgpool3.h>

#include <boost/python.hpp>

namespace bp = boost::python;
namespace mw = maps::wiki;

namespace maps {
namespace wiki {
namespace {

bp::object tryLock(
        pgpool3::Pool& pgPool, geolocks::TUId uid,
        revision::DBID branchId, const std::string& wkb)
{
    auto conn = pgPool.getMasterConnection();
    return common::toPyObject(
        geolocks::GeoLock::tryLock(conn.get(), uid, branchId, wkb));
}

bp::object tryLockAll(
        pgpool3::Pool& pgPool, geolocks::TUId uid,
        revision::DBID branchId, const bp::object& wkbs)
{
    auto conn = pgPool.getMasterConnection();
    auto locks = geolocks::GeoLock::tryLockAll(
        conn.get(), uid, branchId, common::toVector<std::string>(wkbs));
    return locks ? common::toPyList(*locks) : bp::object();
}

geolocks::GeoLock loadLock(pgpool3::Pool& pgPool, geolocks::TLockId id)
{
    auto txn = pgPool.masterWriteableTransaction();
    return geolocks::GeoLock::load(*txn, id);
}

void geoLockUnlock(geolocks::GeoLock& lock, pgpool3::Pool& pgPool)
{
    auto txn = pgPool.masterWriteableTransaction();
    lock.unlock(*txn);
    txn->commit();
}

void unlockAll(pgpool3::Pool& pgPool, bp::object& pyLocks)
{
    auto locks = common::toVector<geolocks::GeoLock>(pyLocks);

    if (locks.empty()) {
        return;
    }

    auto txn = pgPool.masterWriteableTransaction();
    for (auto& lock : locks) {
        lock.unlock(*txn);
    }
    txn->commit();
}

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

BOOST_PYTHON_MODULE(geolocks)
{
    PyEval_InitThreads();

    bp::class_<mw::geolocks::GeoLock>(
            "GeoLock", bp::no_init)
        .def_readonly("id", &mw::geolocks::GeoLock::id)
        .def_readonly("commit_id", &mw::geolocks::GeoLock::commitId)
        .def("unlock", &::mw::geoLockUnlock)
        .def("load", &::mw::loadLock).staticmethod("load");

    bp::def("try_lock", ::mw::tryLock,
            (bp::arg("pgpool"), "uid", "branch_id", "wkb"));

    bp::def("try_lock_all", ::mw::tryLockAll,
            (bp::arg("pgpool"), "uid", "branch_id", "wkbs"));
    bp::def("unlock_all", ::mw::unlockAll,
            (bp::arg("pgpool"), "locks"));
}
