#pragma once

#include <maps/libs/chrono/include/time_point.h>
#include <maps/libs/enum_io/include/enum_io_fwd.h>
#include <maps/libs/sql_chemistry/include/column.h>
#include <maps/libs/geolib/include/units.h>

#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include <unordered_set>

namespace maps::mrc::db {

using TId = int64_t;
using TIds = std::vector<TId>;
using TIdSet = std::set<TId>;
using OptionalId = std::optional<TId>;

template<class T>
using IdTo = std::unordered_map<db::TId, T>;

using TFc = int32_t;
using UserId = std::string;
using OptionalTimePoint = std::optional<chrono::TimePoint>;
using Strings = std::vector<std::string>;

// Describes clock-wise turn angle of the camera direction
// relative to the movement direction
enum class CameraDeviation : int16_t {
    Front = 0,
    Right = 90,
    Back = 180,
    Left = 270,
};

enum class FeaturePrivacy : int8_t {
    Public,
    Restricted, ///< external balancer, ACL
    Secret,     ///< internal balancer, IDM
    Min = Public,
    Max = Secret,
};

DECLARE_ENUM_IO(FeaturePrivacy);

FeaturePrivacy selectStricterPrivacy(FeaturePrivacy lhs, FeaturePrivacy rhs);

enum class GraphType : int8_t {
    Road,
    Pedestrian,
};

DECLARE_ENUM_IO(GraphType);

enum class DisqType {
    DisableCapturing,
    DisablePublishing,
};

DECLARE_ENUM_IO(DisqType);

enum class RideStatus {
    Pending,
    Processed,
};

DECLARE_ENUM_IO(RideStatus);

template <typename Enum>
constexpr auto toIntegral(Enum value)
{
    return static_cast<std::underlying_type_t<Enum>>(value);
}

constexpr geolib3::Degrees toDegreesCW(CameraDeviation value)
{
    return geolib3::Degrees(static_cast<double>(toIntegral(value)));
}

constexpr geolib3::Degrees toDegreesCCW(CameraDeviation value)
{
    return -toDegreesCW(value);
}

constexpr TId NO_OBJECT_ID = 0;
constexpr TId NO_COMMIT_ID = 0;

class GeneralGeometryColumn : public sql_chemistry::MercatorColumn<std::string> {
public:
    using sql_chemistry::MercatorColumn<std::string>::MercatorColumn;

    std::string selectStatement() const;

    std::string unescapeField(const sql_chemistry::ResultField& field) const;

    std::string escapeValue(const std::string& val, sql_chemistry::Transaction& txn) const;
};


} // namespace maps::mrc::db
