#include "tool.h"

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/libs/geolib/include/distance.h>

namespace maps {
namespace mrc {
namespace tool {
namespace {

auto absTimeInterval(const db::Feature& lhs, const db::Feature& rhs)
{
    auto result = lhs.timestamp() - rhs.timestamp();
    if (result < result.zero())
        result = -result;
    return result;
}

/// area in which a long time between shots (traffic light, jam)
static constexpr double GAP_STOP_LIMIT_METERS{30};
static constexpr std::chrono::seconds GAP_STOP_LIMIT_TIME{120};

/// regular driving
static constexpr std::chrono::seconds GAP_DRIVING_LIMIT_TIME{15};

struct Gap {
    /// check if photos taken in different passages
    bool operator()(const db::Feature& lhs, const db::Feature& rhs) const
    {
        auto timeInterval = absTimeInterval(lhs, rhs);
        if (timeInterval > GAP_STOP_LIMIT_TIME)
            return true;
        auto diffOfMeters
            = geolib3::fastGeoDistance(lhs.geodeticPos(), rhs.geodeticPos());
        return diffOfMeters > GAP_STOP_LIMIT_METERS
               && timeInterval > GAP_DRIVING_LIMIT_TIME;
    };
};

db::Features selectPassageByPhotoId(db::Features photos, db::TId photoId)
{
    std::sort(photos.begin(), photos.end(),
              [](const db::Feature& lhs, const db::Feature& rhs) {
                  return lhs.timestamp() < rhs.timestamp();
              });
    for (auto first = photos.begin(); first != photos.end();) {
        auto last = std::adjacent_find(first, photos.end(), Gap{});
        if (last != photos.end()) {
            last = std::next(last);
        }
        if (std::any_of(first, last, [photoId](const db::Feature& photo) {
                return photoId == photo.id();
            })) {
            return {first, last};
        }
        first = last;
    }
    return {};
}

} // anonymous namespace

db::Features load(pqxx::transaction_base& txn,
                  Mode mode,
                  db::TId photoId,
                  db::TId maxPhotoId)
{
    using Gtw = db::FeatureGateway;
    auto photo = Gtw{txn}.loadById(photoId);
    auto sourceFilter
        = Gtw::Tb::isPublished && Gtw::Tb::sourceId.equals(photo.sourceId());
    if (mode == Mode::Photo) {
        return {photo};
    }
    else if (mode == Mode::User) {
        return Gtw{txn}.load(sourceFilter);
    }
    else if (maxPhotoId) {
        return Gtw{txn}.load(sourceFilter
                             && Gtw::Tb::id.between(photoId, maxPhotoId));
    }
    else {
        return selectPassageByPhotoId(Gtw{txn}.load(sourceFilter), photoId);
    }
}

} // tool
} // mrc
} // maps
